Initial pass at unicorn pack.
This commit is contained in:
@@ -23,6 +23,8 @@ interface UnitVis {
|
||||
bonus: number
|
||||
damage: number
|
||||
dying: boolean
|
||||
spooked: number // Unicorn pack ailment: lowers the pet's clash attack
|
||||
exposed: number // Unicorn pack ailment: raises damage it takes per hit
|
||||
}
|
||||
|
||||
interface SideVis {
|
||||
@@ -54,6 +56,9 @@ const EVENT_MS: Record<BattleEvent['type'], number> = {
|
||||
release: 500,
|
||||
trumpet: 800,
|
||||
prevent: 900,
|
||||
mana: 800,
|
||||
ailment: 900,
|
||||
bounce: 1000,
|
||||
}
|
||||
|
||||
const appleCount = (foods: Card[]) => foods.filter((f) => f.food === 'apple').length
|
||||
@@ -104,12 +109,16 @@ function replay(events: BattleEvent[], stackSizes: number[], upto: number): Side
|
||||
bonus: 0,
|
||||
damage: 0,
|
||||
dying: false,
|
||||
spooked: 0,
|
||||
exposed: 0,
|
||||
}
|
||||
break
|
||||
}
|
||||
s.stack--
|
||||
const card = ev.card!
|
||||
if (card.kind === 'food') {
|
||||
if (card.kind === 'food' || card.kind === 'ailment') {
|
||||
// Ailments waiting on top of the deck sit in the pending fan until a
|
||||
// pet arrives; the backend then emits an 'ailment' event to attach it.
|
||||
s.pending.push(card)
|
||||
} else {
|
||||
s.unit = {
|
||||
@@ -118,6 +127,8 @@ function replay(events: BattleEvent[], stackSizes: number[], upto: number): Side
|
||||
bonus: ev.bonus ?? appleCount(s.pending),
|
||||
damage: 0,
|
||||
dying: false,
|
||||
spooked: 0,
|
||||
exposed: 0,
|
||||
}
|
||||
s.pending = []
|
||||
}
|
||||
@@ -187,6 +198,24 @@ function replay(events: BattleEvent[], stackSizes: number[], upto: number): Side
|
||||
break // pure animation; no state change
|
||||
case 'trumpet':
|
||||
break // pure animation; the pool isn't drawn on the board
|
||||
case 'mana':
|
||||
break // pure animation; the Mana pool isn't drawn on the board
|
||||
case 'ailment': {
|
||||
// A pet gained (count > 0) an ailment; count 0 means Baku shrugged it.
|
||||
const u = sides[ev.seat!].unit
|
||||
const kind = ev.card?.ailment
|
||||
if (u && ev.count && kind === 'spooked') u.spooked += ev.count
|
||||
else if (u && ev.count && kind === 'exposed') u.exposed += ev.count
|
||||
break
|
||||
}
|
||||
case 'bounce': {
|
||||
// The target pet is sent to the bottom of its deck: it leaves play and
|
||||
// the deck grows by one.
|
||||
const t = sides[ev.target!]
|
||||
t.unit = null
|
||||
t.stack++
|
||||
break
|
||||
}
|
||||
case 'prevent': {
|
||||
// Cone Snail shaved damage off the hit; the reduced total rides along.
|
||||
const u = sides[ev.seat!].unit
|
||||
@@ -228,6 +257,14 @@ function unitPop(ev: BattleEvent | null, seat: number, events: BattleEvent[], st
|
||||
case 'trumpet':
|
||||
if (ev.seat !== seat) return null
|
||||
return (ev.count ?? 0) >= 0 ? `🎺 +${ev.count}` : `🎺 ${ev.count}`
|
||||
case 'mana':
|
||||
if (ev.seat !== seat) return null
|
||||
return (ev.count ?? 0) >= 0 ? `🔮 +${ev.count}` : `🔮 ${ev.count}`
|
||||
case 'ailment':
|
||||
if (ev.seat !== seat) return null
|
||||
return ev.card?.ailment === 'spooked' ? '👻' : '🎯'
|
||||
case 'bounce':
|
||||
return ev.target === seat ? '🌀' : null
|
||||
case 'eat':
|
||||
return ev.seat === seat ? '🍎' : null
|
||||
case 'heal':
|
||||
@@ -337,7 +374,8 @@ export function BattlePhase({ view, send, step, setStep }: Props) {
|
||||
!done &&
|
||||
lastEvent?.seat === seat &&
|
||||
(lastEvent.type === 'prep' ||
|
||||
(lastEvent.type === 'reveal' && lastEvent.card?.kind === 'food'))
|
||||
(lastEvent.type === 'reveal' &&
|
||||
(lastEvent.card?.kind === 'food' || lastEvent.card?.kind === 'ailment')))
|
||||
? lastEvent.card?.id
|
||||
: null
|
||||
// A pet just set aside slides into the set-aside row beside the arena.
|
||||
@@ -450,6 +488,26 @@ export function BattlePhase({ view, send, step, setStep }: Props) {
|
||||
damage={s.unit.damage}
|
||||
dead={s.unit.dying}
|
||||
/>
|
||||
{(s.unit.spooked > 0 || s.unit.exposed > 0) && (
|
||||
<div className="ailment-badges">
|
||||
{s.unit.spooked > 0 && (
|
||||
<span
|
||||
className="ailment-badge ailment-spooked"
|
||||
title={`Spooked ×${s.unit.spooked}: deals ${s.unit.spooked} less damage`}
|
||||
>
|
||||
👻{s.unit.spooked > 1 ? s.unit.spooked : ''}
|
||||
</span>
|
||||
)}
|
||||
{s.unit.exposed > 0 && (
|
||||
<span
|
||||
className="ailment-badge ailment-exposed"
|
||||
title={`Exposed ×${s.unit.exposed}: takes ${s.unit.exposed} extra damage per hit`}
|
||||
>
|
||||
🎯{s.unit.exposed > 1 ? s.unit.exposed : ''}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{pop && (
|
||||
<div key={`pop-${step}`} className={pop.startsWith('−') ? 'damage-pop' : 'fx-pop'}>
|
||||
{pop}
|
||||
|
||||
Reference in New Issue
Block a user