Bug fix: During resolution phase (dice rolling), isMyTurn was false for both players, preventing anyone from seeing the dice roller. Now the batting team has control during resolution since they read their card. Demo mode: myTeamId now returns whichever team needs to act, allowing single-player testing of both sides. Changes: - Add creator_discord_id to GameState (backend + frontend types) - Add get_current_user_optional dependency for optional auth - Update quick-create to capture creator's discord_id - Fix isMyTurn to give batting team control during resolution - Demo mode: myTeamId returns active team based on phase Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
338 lines
7.2 KiB
TypeScript
338 lines
7.2 KiB
TypeScript
/**
|
|
* Game State and Play Result Types
|
|
*
|
|
* TypeScript definitions matching backend Pydantic models exactly.
|
|
* These types ensure type safety between frontend and backend.
|
|
*
|
|
* Backend Reference: app/models/game_models.py
|
|
*/
|
|
|
|
/**
|
|
* Game status enumeration
|
|
*/
|
|
export type GameStatus = 'pending' | 'active' | 'paused' | 'completed' | 'abandoned'
|
|
|
|
/**
|
|
* Inning half
|
|
*/
|
|
export type InningHalf = 'top' | 'bottom'
|
|
|
|
/**
|
|
* Game mode
|
|
*/
|
|
export type GameMode = 'live' | 'async' | 'vs_ai'
|
|
|
|
/**
|
|
* Game visibility
|
|
*/
|
|
export type GameVisibility = 'public' | 'private'
|
|
|
|
/**
|
|
* League identifier
|
|
*/
|
|
export type LeagueId = 'sba' | 'pd'
|
|
|
|
/**
|
|
* Decision phase in the play workflow
|
|
*
|
|
* Standardized naming (2025-01-21): Uses backend convention 'awaiting_*'
|
|
* for clarity about what action is pending.
|
|
*/
|
|
export type DecisionPhase = 'awaiting_defensive' | 'awaiting_stolen_base' | 'awaiting_offensive' | 'resolution' | 'complete'
|
|
|
|
/**
|
|
* Lineup player state - represents a player in the game
|
|
* Backend: LineupPlayerState
|
|
*/
|
|
export interface LineupPlayerState {
|
|
lineup_id: number
|
|
card_id: number
|
|
position: string
|
|
batting_order: number | null
|
|
is_active: boolean
|
|
position_rating?: {
|
|
range: number
|
|
error: number
|
|
innings: number
|
|
} | null
|
|
}
|
|
|
|
/**
|
|
* Core game state - complete representation of active game
|
|
* Backend: GameState
|
|
*/
|
|
export interface GameState {
|
|
// Identity
|
|
game_id: string
|
|
league_id: LeagueId
|
|
|
|
// Teams
|
|
home_team_id: number
|
|
away_team_id: number
|
|
home_team_is_ai: boolean
|
|
away_team_is_ai: boolean
|
|
|
|
// Creator (for demo/testing - creator can control home team)
|
|
creator_discord_id: string | null
|
|
|
|
// Resolution mode
|
|
auto_mode: boolean
|
|
|
|
// Game state
|
|
status: GameStatus
|
|
inning: number
|
|
half: InningHalf
|
|
outs: number
|
|
balls: number
|
|
strikes: number
|
|
home_score: number
|
|
away_score: number
|
|
|
|
// Runners (direct references)
|
|
on_first: LineupPlayerState | null
|
|
on_second: LineupPlayerState | null
|
|
on_third: LineupPlayerState | null
|
|
|
|
// Current players
|
|
current_batter: LineupPlayerState
|
|
current_pitcher: LineupPlayerState | null
|
|
current_catcher: LineupPlayerState | null
|
|
current_on_base_code: number
|
|
|
|
// Batting order tracking
|
|
away_team_batter_idx: number // 0-8
|
|
home_team_batter_idx: number // 0-8
|
|
|
|
// Decision tracking
|
|
pending_decision: DecisionPhase | null
|
|
decision_phase: DecisionPhase
|
|
decisions_this_play: Record<string, boolean>
|
|
pending_defensive_decision: DefensiveDecision | null
|
|
pending_offensive_decision: OffensiveDecision | null
|
|
|
|
// Manual mode
|
|
pending_manual_roll: RollData | null
|
|
|
|
// Play history
|
|
play_count: number
|
|
last_play_result: string | null
|
|
|
|
// Timestamps
|
|
created_at: string
|
|
started_at: string | null
|
|
completed_at: string | null
|
|
}
|
|
|
|
/**
|
|
* Defensive strategic decision
|
|
* Backend: DefensiveDecision
|
|
*/
|
|
export interface DefensiveDecision {
|
|
infield_depth: 'infield_in' | 'normal' | 'corners_in'
|
|
outfield_depth: 'normal' | 'shallow'
|
|
hold_runners: number[] // Bases to hold (e.g., [1, 3])
|
|
}
|
|
|
|
/**
|
|
* Offensive strategic decision
|
|
* Backend: OffensiveDecision
|
|
*
|
|
* Session 2 Update (2025-01-14): Replaced approach/hit_and_run/bunt_attempt with action field.
|
|
*/
|
|
export interface OffensiveDecision {
|
|
action: 'swing_away' | 'steal' | 'check_jump' | 'hit_and_run' | 'sac_bunt' | 'squeeze_bunt'
|
|
steal_attempts: number[] // Bases to steal (2, 3, or 4) - only used when action="steal"
|
|
}
|
|
|
|
/**
|
|
* Dice roll data from server
|
|
* Backend: AbRoll (simplified for frontend)
|
|
*/
|
|
export interface RollData {
|
|
roll_id: string
|
|
d6_one: number
|
|
d6_two_a: number
|
|
d6_two_b: number
|
|
d6_two_total: number
|
|
chaos_d20: number
|
|
resolution_d20: number
|
|
check_wild_pitch: boolean
|
|
check_passed_ball: boolean
|
|
timestamp: string
|
|
}
|
|
|
|
/**
|
|
* Play outcome enumeration (matches backend PlayOutcome enum values)
|
|
* Note: These are the STRING VALUES, not enum names
|
|
*/
|
|
export type PlayOutcome =
|
|
// Standard outs
|
|
| 'strikeout'
|
|
| 'groundball_a'
|
|
| 'groundball_b'
|
|
| 'groundball_c'
|
|
| 'flyout_a'
|
|
| 'flyout_b'
|
|
| 'flyout_bq'
|
|
| 'flyout_c'
|
|
| 'lineout'
|
|
| 'popout'
|
|
|
|
// Walks and hits by pitch
|
|
| 'walk'
|
|
| 'intentional_walk'
|
|
| 'hbp'
|
|
|
|
// Hits (capped - specific bases)
|
|
| 'single_1'
|
|
| 'single_2'
|
|
| 'single_uncapped'
|
|
| 'double_2'
|
|
| 'double_3'
|
|
| 'double_uncapped'
|
|
| 'triple'
|
|
| 'homerun'
|
|
|
|
// Interrupt plays (baserunning events)
|
|
| 'stolen_base'
|
|
| 'caught_stealing'
|
|
| 'wild_pitch'
|
|
| 'passed_ball'
|
|
| 'balk'
|
|
| 'pick_off'
|
|
|
|
// Errors
|
|
| 'error'
|
|
| 'x_check'
|
|
|
|
// Ballpark power (PD specific)
|
|
| 'bp_homerun'
|
|
| 'bp_flyout'
|
|
| 'bp_single'
|
|
| 'bp_lineout'
|
|
|
|
/**
|
|
* Runner advancement during a play
|
|
*/
|
|
export interface RunnerAdvancement {
|
|
from: number // 0=batter, 1-3=bases
|
|
to: number // 1-4=bases (4=home/scored)
|
|
lineup_id: number // Player's lineup ID for name lookup
|
|
is_out?: boolean // Runner was out during advancement (renamed from 'out')
|
|
}
|
|
|
|
/**
|
|
* Play resolution result from server
|
|
* Backend: PlayResult
|
|
*/
|
|
export interface PlayResult {
|
|
// Play identification
|
|
play_number: number
|
|
inning?: number // Inning when play occurred
|
|
half?: InningHalf // 'top' or 'bottom'
|
|
outcome: PlayOutcome
|
|
|
|
// Play description
|
|
description: string
|
|
hit_type?: string
|
|
|
|
// Results
|
|
outs_recorded: number
|
|
runs_scored: number
|
|
|
|
// Player identification for display
|
|
batter_lineup_id?: number // Batter's lineup ID for name lookup
|
|
|
|
// Runner advancement
|
|
runners_advanced: RunnerAdvancement[]
|
|
batter_result: number | null // Where batter ended up (1-4, null=out)
|
|
|
|
// Updated state snapshot
|
|
new_state: Partial<GameState>
|
|
|
|
// Categorization helpers
|
|
is_hit: boolean
|
|
is_out: boolean
|
|
is_walk: boolean
|
|
is_strikeout: boolean
|
|
|
|
// Roll reference (if manual mode)
|
|
roll_id?: string
|
|
|
|
// X-Check details (if defensive play)
|
|
x_check_details?: XCheckResult
|
|
}
|
|
|
|
/**
|
|
* X-Check resolution audit trail
|
|
*/
|
|
export interface XCheckResult {
|
|
check_position: string
|
|
defense_range: number
|
|
error_rating: number
|
|
d20_roll: number
|
|
error_3d6: number
|
|
base_result: string
|
|
error_result: string
|
|
final_outcome: PlayOutcome
|
|
held_runners: string[]
|
|
}
|
|
|
|
/**
|
|
* Decision prompt from server
|
|
* Backend: DecisionPrompt (WebSocket event)
|
|
*/
|
|
export interface DecisionPrompt {
|
|
phase: DecisionPhase
|
|
role: 'home' | 'away'
|
|
timeout_seconds: number
|
|
options?: string[]
|
|
message?: string
|
|
}
|
|
|
|
/**
|
|
* Manual outcome submission to server
|
|
* Backend: ManualOutcomeSubmission
|
|
*/
|
|
export interface ManualOutcomeSubmission {
|
|
outcome: PlayOutcome
|
|
hit_location?: string // Required for groundballs/flyballs
|
|
}
|
|
|
|
/**
|
|
* Game list item for active/completed games
|
|
*/
|
|
export interface GameListItem {
|
|
game_id: string
|
|
league_id: LeagueId
|
|
home_team_id: number
|
|
away_team_id: number
|
|
status: GameStatus
|
|
current_inning: number
|
|
current_half: InningHalf
|
|
home_score: number
|
|
away_score: number
|
|
started_at: string | null
|
|
completed_at: string | null
|
|
}
|
|
|
|
/**
|
|
* Game creation request
|
|
*/
|
|
export interface CreateGameRequest {
|
|
league_id: LeagueId
|
|
home_team_id: number
|
|
away_team_id: number
|
|
game_mode: GameMode
|
|
visibility: GameVisibility
|
|
}
|
|
|
|
/**
|
|
* Game creation response
|
|
*/
|
|
export interface CreateGameResponse {
|
|
game_id: string
|
|
status: GameStatus
|
|
created_at: string
|
|
}
|