Fix nurse shark and remove mid-battle choice.

This commit is contained in:
Greyson Parrelli
2026-07-24 10:12:17 -04:00
parent 094f38593e
commit 0fe0757e18
14 changed files with 84 additions and 326 deletions
+26 -83
View File
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useState } from 'react'
import type { Dispatch, SetStateAction } from 'react'
import { createPortal } from 'react-dom'
import type { BattleEvent, Card, ClientMessage, GameView, PendingBattleDecision } from '../types'
import type { BattleEvent, Card, ClientMessage, GameView } from '../types'
import { CardView } from './CardView'
import { DiceRoll, ROLL_MS } from './DiceRoll'
@@ -553,94 +553,37 @@ export function BattlePhase({ view, send, step, setStep }: Props) {
)
})()}
{done && view.pendingBattle ? (
<BattleDecision
pd={view.pendingBattle}
youSeat={youSeat}
oppName={opp?.name ?? 'Opponent'}
send={send}
/>
) : (
done && (
<div className={`battle-result ${draw ? 'is-draw' : won ? 'is-win' : 'is-loss'}`}>
<div className="battle-result-title">
{draw ? 'Draw!' : won ? 'Victory!' : 'Defeat…'}
</div>
{!draw && (
<div className="battle-result-sub">
{view.players[battle.winnerSeat]?.name} wins{' '}
{'🏆'.repeat(battle.trophies)}
</div>
)}
{draw && <div className="battle-result-sub">No trophies awarded</div>}
{acked ? (
<p className="muted">Waiting for opponent</p>
) : (
<button
className="btn btn-primary btn-big"
onClick={() => {
setAcked(true)
send({ type: 'ready' })
}}
>
{battle.round >= view.maxRounds ? 'See final results' : 'Next round →'}
</button>
)}
{done && (
<div className={`battle-result ${draw ? 'is-draw' : won ? 'is-win' : 'is-loss'}`}>
<div className="battle-result-title">
{draw ? 'Draw!' : won ? 'Victory!' : 'Defeat…'}
</div>
)
{!draw && (
<div className="battle-result-sub">
{view.players[battle.winnerSeat]?.name} wins{' '}
{'🏆'.repeat(battle.trophies)}
</div>
)}
{draw && <div className="battle-result-sub">No trophies awarded</div>}
{acked ? (
<p className="muted">Waiting for opponent</p>
) : (
<button
className="btn btn-primary btn-big"
onClick={() => {
setAcked(true)
send({ type: 'ready' })
}}
>
{battle.round >= view.maxRounds ? 'See final results' : 'Next round →'}
</button>
)}
</div>
)}
</div>
)
}
// BattleDecision is the mid-battle prompt (Golden pack: Nurse Shark). The
// deciding player picks how many Trumpets to spend; the other player waits.
function BattleDecision({
pd,
youSeat,
oppName,
send,
}: {
pd: PendingBattleDecision
youSeat: number
oppName: string
send: (msg: ClientMessage) => void
}) {
const [sent, setSent] = useState(false)
// Reset when a fresh decision arrives (e.g. a second Nurse Shark).
useEffect(() => setSent(false), [pd.seat, pd.trumpets, pd.max])
if (pd.seat !== youSeat) {
return (
<div className="battle-result">
<p className="muted">{oppName} is deciding {pd.petName}</p>
</div>
)
}
return (
<div className="battle-result battle-decision">
<div className="battle-result-title">{pd.petName}</div>
<div className="battle-result-sub">
Spend Trumpets to throw 2 🪨 each you hold {pd.trumpets} 🎺
</div>
<div className="battle-decision-options">
{Array.from({ length: pd.max + 1 }, (_, n) => (
<button
key={n}
className="btn btn-primary"
disabled={sent}
onClick={() => {
setSent(true)
send({ type: 'battleChoose', value: n })
}}
>
{n === 0 ? 'Spend none' : `${n} 🎺 → ${2 * n} 🪨`}
</button>
))}
</div>
</div>
)
}
// clashDamageTaken computes how much damage a seat's pet took in the clash
// at event index `idx` (its damage total there minus its total beforehand).
function clashDamageTaken(events: BattleEvent[], idx: number, seat: number): number {