Files
super-auto-pets-board-game/web/src/types.ts
T

199 lines
5.7 KiB
TypeScript

// Mirrors of the Go view types (internal/game/view.go).
export type Suit = 'red' | 'blue' | 'yellow'
export type CardKind = 'pet' | 'food' | 'ailment'
export type Phase = 'lobby' | 'shop' | 'arrange' | 'battle' | 'gameover'
export interface Card {
id: string
kind: CardKind
name: string
tier?: number
power?: number
suit?: Suit
effects?: unknown[]
effectText?: string
food?: string
perk?: boolean
temporary?: boolean
ailment?: string // 'spooked' | 'exposed' (Unicorn pack; kind === 'ailment')
}
export interface PlayerView {
id: string
name: string
seat: number
coins: number
trophies: number
roundWins?: number[] // rounds this player won a battle in (public)
ready: boolean
connected: boolean
isBot?: boolean
deckSize: number
petCount: number
avocados?: number // set-aside Avocado tokens (Golden pack)
firstBuyFree?: boolean // Manta Ray: next buy is free (self only)
buysThisRound?: number // Blue-Ringed Octopus counter (self only)
trumpets?: number // banked Trumpets for next battle (Bird of Paradise, self only)
mana?: number // persistent Mana pool (Unicorn pack; public)
shopPeek?: Card // Bigfoot's look at the top of the shop deck (self only)
deck?: Card[]
}
export interface PendingTrade {
playerId: string
tier: number
options: [Card, Card]
}
// Cockatoo (Golden pack): the buyer must reveal one of their pets.
export interface PendingReveal {
playerId: string
source: string
options?: string[] // eligible pet card ids (buyer only)
apples?: number // fixed apple reward (Quetzalcoatl); 0 = revealed pet's power (Cockatoo)
}
// Water of Youth (Unicorn pack): the buyer must sacrifice one of their pets to
// upgrade into a next-tier card.
export interface PendingSacrifice {
playerId: string
source: string
tier: number
options?: string[] // eligible pet card ids to sacrifice (buyer only)
}
export interface BattleEvent {
type:
| 'prep'
| 'reveal'
| 'summon'
| 'mill'
| 'rock'
| 'clash'
| 'shield'
| 'strip'
| 'steal'
| 'eat'
| 'heal'
| 'setaside'
| 'release'
| 'trumpet' // a side gains (+) or spends/loses (-) Trumpets (Golden pack)
| 'prevent' // a Cone Snail shaves damage off a hit (Golden pack)
| 'mana' // a side gains (+) or spends (-) Mana (Unicorn pack)
| 'ailment' // a pet gains Ailment cards (Unicorn pack)
| 'bounce' // a pet is sent to the bottom of its deck (Unicorn: Loveland Frogman)
seat?: number
target?: number
card?: Card
count?: number
// clash
damage?: number[]
died?: boolean[]
// rock / strip / steal
roll?: number
dice?: number[] // rock: individual die faces (each 0, 1, or 2)
damageAfter?: number
targetDied?: boolean
// eat (new bonus total) / reveal (starting bonus)
bonus?: number
// human-readable description of this step for the event log
text?: string
}
// A round runs one battle per pairing, so a six-player round has three of
// these. Everything inside is indexed by *side* — 0 or 1 within this battle —
// rather than by seat at the table, including BattleEvent.seat/target. `seats`
// maps the two apart. winnerSeat is the exception: it's a real seat.
export interface BattleResult {
round: number
seats: number[] // the two seats fighting, first player first
stackSizes: number[]
lineups?: Card[][] // each side's arranged deck (top first); public for peeking
events: BattleEvent[] | null
winnerSeat: number
trophies: number
}
// sideOf maps a seat to its side index in a battle, or -1 if it wasn't in it.
export function sideOf(battle: BattleResult, seat: number): number {
return battle.seats?.indexOf(seat) ?? -1
}
export interface LogEntry {
seq: number
round: number
phase: Phase
seat: number // acting seat, or -1
icon?: string
kind?: string // e.g. 'result' (battle outcome)
text: string
source?: string // card id that caused a spawn
spawn?: string // 'apple' | 'bee'
}
export interface PackInfo {
id: string
name: string
emoji: string
playable: boolean
}
export interface GameView {
gameId: string
code: string
phase: Phase
round: number
maxRounds: number
maxPets: number
packs: string[] // selected packs, shuffled together
packCatalog: PackInfo[] // the choices offered in the lobby
packsNeeded: number // packs the current table size requires
hostSeat: number
minPlayers: number
maxPlayers: number
playerCounts: number[] // table sizes a game can start at (2, 4, 6)
youSeat: number
turn: number
prioritySeat: number
shopRow: Card[]
deckCounts: number[]
players: PlayerView[]
matchups?: [number, number][] // this round's battle pairings (public)
yourOpponent: number // the seat you face this round, or -1
pending?: PendingTrade
pendingReveal?: PendingReveal
pendingSacrifice?: PendingSacrifice
battle?: BattleResult // your own battle this round
battles?: BattleResult[] // every table's, all replayable
winnerSeat: number // outright winner, or -1 when shared
winnerSeats?: number[] // everyone holding the title
log?: LogEntry[]
debug?: boolean // server DEBUG mode: unlocks the buy-any-card panel
}
export type ClientMessage =
| { type: 'setPacks'; packs: string[] }
| { type: 'addBot'; difficulty: string }
| { type: 'removePlayer'; target: string }
| { type: 'start' }
| { type: 'buy'; row: number }
| { type: 'buyAvocado'; row: number }
| { type: 'sell'; cards: string[] }
| { type: 'trade'; cards: string[] }
| { type: 'tradeChoose'; pick: number }
| { type: 'revealChoose'; card: string }
| { type: 'sacrificeChoose'; card: string }
| { type: 'peek' }
| { type: 'pass' }
| { type: 'arrange'; order: string[] }
| { type: 'ready' }
| { type: 'debugAdd'; name: string }
export interface Session {
gameId: string
code: string
playerId: string
token: string
}