// Mirrors of the Go view types (internal/game/view.go). export type Suit = 'red' | 'blue' | 'yellow' export type CardKind = 'pet' | 'food' 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 } export interface PlayerView { id: string name: string seat: number coins: number trophies: number 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) 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) } // 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' | '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) 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 } export interface BattleResult { round: number stackSizes: number[] lineups?: Card[][] // each seat's arranged deck (top first); public for peeking events: BattleEvent[] | null winnerSeat: number trophies: number } 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 pack: string packs: PackInfo[] hostSeat: number minPlayers: number maxPlayers: number youSeat: number turn: number shopRow: Card[] deckCounts: number[] players: PlayerView[] pending?: PendingTrade pendingReveal?: PendingReveal pendingBattle?: PendingBattleDecision battle?: BattleResult winnerSeat: number log?: LogEntry[] debug?: boolean // server DEBUG mode: unlocks the buy-any-card panel } export type ClientMessage = | { type: 'setPack'; pack: 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: 'battleChoose'; value: number } | { type: 'pass' } | { type: 'arrange'; order: string[] } | { type: 'ready' } | { type: 'debugAdd'; name: string } export interface Session { gameId: string code: string playerId: string token: string }