Fix nurse shark and remove mid-battle choice.
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -1410,19 +1410,6 @@ h3 {
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
/* Mid-battle decision panel (Golden pack: Nurse Shark). */
|
||||
.battle-decision-options {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.battle-decision-options .btn {
|
||||
font-family: var(--font-display);
|
||||
}
|
||||
|
||||
/* Set-aside Avocado tokens (Golden pack): a slim tray with a toggle pill. */
|
||||
.avocado-zone {
|
||||
background: rgba(0, 0, 0, 0.24);
|
||||
|
||||
@@ -48,16 +48,6 @@ export interface PendingReveal {
|
||||
options?: string[] // eligible pet card ids (buyer only)
|
||||
}
|
||||
|
||||
// Nurse Shark (Golden pack): a mid-battle Trumpet-spend choice.
|
||||
export interface PendingBattleDecision {
|
||||
seat: number
|
||||
kind: string
|
||||
petName: string
|
||||
min: number
|
||||
max: number
|
||||
trumpets: number
|
||||
}
|
||||
|
||||
export interface BattleEvent {
|
||||
type:
|
||||
| 'prep'
|
||||
@@ -140,7 +130,6 @@ export interface GameView {
|
||||
players: PlayerView[]
|
||||
pending?: PendingTrade
|
||||
pendingReveal?: PendingReveal
|
||||
pendingBattle?: PendingBattleDecision
|
||||
battle?: BattleResult
|
||||
winnerSeat: number
|
||||
log?: LogEntry[]
|
||||
@@ -158,7 +147,6 @@ export type ClientMessage =
|
||||
| { type: 'trade'; cards: string[] }
|
||||
| { type: 'tradeChoose'; pick: number }
|
||||
| { type: 'revealChoose'; card: string }
|
||||
| { type: 'battleChoose'; value: number }
|
||||
| { type: 'pass' }
|
||||
| { type: 'arrange'; order: string[] }
|
||||
| { type: 'ready' }
|
||||
|
||||
Reference in New Issue
Block a user