Shop-Leiste im TFT-Stil: Cards mit Splash, Trait-Pills, Shine, Upgrade-Sterne, Lock, Gold/Streak-Pills
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,12 +20,10 @@ const kindLabel = { pvp: 'PvP', pve: 'PvE', carousel: 'Carousel' }
|
||||
<header>
|
||||
<span class="round">Runde {{ s.round.label }} · {{ kindLabel[s.round.kind] }}</span>
|
||||
<span class="stat">❤️ {{ s.player.hp }}</span>
|
||||
<span class="stat">🪙 {{ s.player.gold }}</span>
|
||||
<span class="stat">
|
||||
Lvl {{ s.player.level }}
|
||||
<template v-if="s.player.xp_needed !== null">({{ s.player.xp }}/{{ s.player.xp_needed }})</template>
|
||||
</span>
|
||||
<span class="stat">Streak {{ s.player.streak }}</span>
|
||||
<span class="score">Boardstärke {{ s.player.score }}</span>
|
||||
<button class="fight" :disabled="s.over" @click="store.next()">
|
||||
{{ s.round.kind === 'pvp' ? 'Kampf' : 'Weiter' }} ▶
|
||||
|
||||
@@ -4,57 +4,180 @@ const costColors = ['#8a8f9d', '#4caf6e', '#4a90d9', '#b153d8', '#e5a531']
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="shopbar">
|
||||
<div class="controls">
|
||||
<button
|
||||
:disabled="store.state.player.xp_needed === null || store.state.player.gold < store.state.xp_cost"
|
||||
@click="store.buyXp()"
|
||||
<div class="shopwrap">
|
||||
<div class="topbar">
|
||||
<span class="pill gold">🪙 {{ store.state.player.gold }}</span>
|
||||
<span
|
||||
v-if="store.state.player.streak !== 0"
|
||||
class="pill streak"
|
||||
:class="store.state.player.streak > 0 ? 'hot' : 'cold'"
|
||||
>
|
||||
XP kaufen ({{ store.state.xp_cost }}g)
|
||||
</button>
|
||||
<button :disabled="store.state.player.gold < store.state.reroll_cost" @click="store.reroll()">
|
||||
Reroll ({{ store.state.reroll_cost }}g)
|
||||
</button>
|
||||
<span class="flame">🔥</span> {{ Math.abs(store.state.player.streak) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="slots">
|
||||
<div
|
||||
v-for="(s, i) in store.state.shop"
|
||||
:key="i"
|
||||
class="slot"
|
||||
:class="{ empty: !s, unaffordable: s && s.cost > store.state.player.gold }"
|
||||
:style="s ? { borderColor: costColors[s.cost - 1] } : {}"
|
||||
@click="s && store.buy(i)"
|
||||
>
|
||||
<template v-if="s">
|
||||
<img :src="s.icon" :alt="s.name" />
|
||||
<div class="sname">{{ s.name }}</div>
|
||||
<div class="straits">{{ s.traits.join(' · ') }}</div>
|
||||
<div class="scost">{{ s.cost }}g</div>
|
||||
</template>
|
||||
|
||||
<div class="shoprow">
|
||||
<div class="controls">
|
||||
<button
|
||||
class="ctrl"
|
||||
:disabled="store.state.player.gold < store.state.reroll_cost"
|
||||
@click="store.reroll()"
|
||||
>
|
||||
<span class="ctrl-label">Refresh</span>
|
||||
<span class="ctrl-cost">🪙 {{ store.state.reroll_cost }} ⟳</span>
|
||||
</button>
|
||||
<button
|
||||
class="ctrl"
|
||||
:disabled="store.state.player.xp_needed === null || store.state.player.gold < store.state.xp_cost"
|
||||
@click="store.buyXp()"
|
||||
>
|
||||
<span class="ctrl-label">XP kaufen</span>
|
||||
<span class="ctrl-cost">🪙 {{ store.state.xp_cost }} ⌃</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="cards">
|
||||
<div
|
||||
v-for="(s, i) in store.state.shop"
|
||||
:key="i"
|
||||
class="card"
|
||||
:class="{ empty: !s, unaffordable: s && s.cost > store.state.player.gold, upgrade: s && s.upgrade }"
|
||||
:style="s ? { borderColor: s.upgrade ? '#ffd75e' : costColors[s.cost - 1] } : {}"
|
||||
@click="s && store.buy(i)"
|
||||
>
|
||||
<template v-if="s">
|
||||
<div class="art" :style="{ backgroundImage: `url(${s.icon})` }" />
|
||||
<div class="shine" />
|
||||
<div v-if="s.upgrade" class="starhint">
|
||||
<span v-for="n in s.upgrade" :key="n">★</span>
|
||||
</div>
|
||||
<div class="traitlist">
|
||||
<span v-for="t in s.traits" :key="t.name" class="traitpill">
|
||||
<img v-if="t.icon" :src="t.icon" :alt="t.name" />{{ t.name }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="cardbottom">
|
||||
<span class="cname">{{ s.name }}</span>
|
||||
<span class="ccost">🪙 {{ s.cost }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="lock"
|
||||
:class="{ locked: store.state.shop_locked }"
|
||||
:title="store.state.shop_locked ? 'Shop entsperren' : 'Shop sperren'"
|
||||
@click="store.act({ action: 'lock' })"
|
||||
>
|
||||
{{ store.state.shop_locked ? '🔒' : '🔓' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.shopbar { display: flex; gap: 8px; }
|
||||
.controls { display: flex; flex-direction: column; gap: 6px; justify-content: center; }
|
||||
.slots { display: flex; gap: 6px; flex: 1; }
|
||||
.slot {
|
||||
.shopwrap { display: flex; flex-direction: column; gap: 6px; }
|
||||
|
||||
.topbar { display: flex; justify-content: center; gap: 10px; }
|
||||
.pill {
|
||||
background: #161b29;
|
||||
border: 1px solid #3a4460;
|
||||
border-radius: 14px;
|
||||
padding: 3px 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.pill.gold { color: #ffd75e; }
|
||||
.streak.hot { border-color: #b3543a; }
|
||||
.streak.cold { border-color: #4a7fd9; }
|
||||
.streak.cold .flame { filter: hue-rotate(190deg) saturate(2); }
|
||||
|
||||
.shoprow { display: flex; gap: 8px; align-items: stretch; }
|
||||
|
||||
.controls { display: flex; flex-direction: column; gap: 6px; width: 120px; }
|
||||
.ctrl {
|
||||
flex: 1;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: flex-start; justify-content: center; gap: 2px;
|
||||
background: linear-gradient(180deg, #1d2438, #141927);
|
||||
border: 1px solid #4a4f30;
|
||||
}
|
||||
.ctrl:hover:not(:disabled) { border-color: #8a8340; }
|
||||
.ctrl-label { font-weight: 700; font-size: 13px; }
|
||||
.ctrl-cost { font-size: 11px; color: #c9b96a; }
|
||||
|
||||
.cards { display: flex; gap: 6px; flex: 1; }
|
||||
.card {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border: 2px solid #2a3145;
|
||||
border-radius: 8px;
|
||||
background: #1a2030;
|
||||
padding: 4px;
|
||||
background: #12141d;
|
||||
height: 130px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
min-height: 96px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.slot:hover:not(.empty) { filter: brightness(1.2); }
|
||||
.slot.empty { cursor: default; background: #12141d; }
|
||||
.slot.unaffordable { opacity: 0.5; }
|
||||
.slot img { width: 100%; height: 52px; object-fit: cover; border-radius: 5px; }
|
||||
.sname { font-size: 12px; font-weight: 600; }
|
||||
.straits { font-size: 10px; color: #7d86a0; }
|
||||
.scost { font-size: 11px; color: #ffd75e; }
|
||||
.card:hover:not(.empty) { filter: brightness(1.15); }
|
||||
.card.empty { cursor: default; }
|
||||
.card.unaffordable { opacity: 0.5; }
|
||||
.card.upgrade { box-shadow: 0 0 10px #ffd75e88; }
|
||||
|
||||
.art { position: absolute; inset: 0; background-size: cover; background-position: center 20%; }
|
||||
|
||||
.shine { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
|
||||
.shine::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; bottom: 0; width: 45%;
|
||||
background: linear-gradient(105deg, transparent, rgba(255, 255, 255, 0.16), transparent);
|
||||
animation: sweep 3s ease-in-out infinite;
|
||||
}
|
||||
@keyframes sweep {
|
||||
0% { left: -50%; }
|
||||
55% { left: 110%; }
|
||||
100% { left: 110%; }
|
||||
}
|
||||
|
||||
.starhint {
|
||||
position: absolute;
|
||||
top: 3px; left: 0; right: 0;
|
||||
text-align: center;
|
||||
color: #ffd75e;
|
||||
text-shadow: 0 0 4px #000;
|
||||
font-size: 15px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.traitlist {
|
||||
position: absolute;
|
||||
top: 4px; left: 4px;
|
||||
display: flex; flex-direction: column; gap: 3px;
|
||||
}
|
||||
.traitpill {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
background: rgba(8, 10, 18, 0.72);
|
||||
border-radius: 9px;
|
||||
padding: 1px 7px 1px 3px;
|
||||
font-size: 10px;
|
||||
width: fit-content;
|
||||
}
|
||||
.traitpill img { width: 13px; height: 13px; filter: brightness(0) invert(1); }
|
||||
|
||||
.cardbottom {
|
||||
position: absolute;
|
||||
left: 0; right: 0; bottom: 0;
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 14px 7px 4px;
|
||||
background: linear-gradient(transparent, rgba(5, 7, 12, 0.92) 55%);
|
||||
font-size: 12px;
|
||||
}
|
||||
.cname { font-weight: 700; }
|
||||
.ccost { color: #ffd75e; font-weight: 700; }
|
||||
|
||||
.lock {
|
||||
width: 44px;
|
||||
font-size: 17px;
|
||||
background: #141927;
|
||||
}
|
||||
.lock.locked { border-color: #ffd75e; background: #2a2618; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user