CLAUDE: Standardize decision phase naming and fix frontend type mismatches
Frontend alignment with backend WebSocket protocol: **Type System Fixes**: - types/game.ts: Changed DecisionPhase to use 'awaiting_*' convention matching backend - types/game.ts: Fixed PlayOutcome enum values to match backend string values (e.g., 'strikeout' not 'STRIKEOUT') - types/game.ts: Added comprehensive play outcome types (groundball_a/b/c, flyout variants, x_check) **Decision Detection**: - store/game.ts: Updated decision detection to check both decision prompt AND gameState.decision_phase - components: Updated all decision phase checks to use 'awaiting_defensive', 'awaiting_offensive', 'awaiting_stolen_base' **WebSocket Enhancements**: - useWebSocket.ts: Added game_joined event handler with success toast - useWebSocket.ts: Fixed dice roll data - now receives d6_two_a and d6_two_b from server - useWebSocket.ts: Request fresh game state after decision submissions to sync decision_phase **New Constants**: - constants/outcomes.ts: Created centralized PlayOutcome enum with display labels and descriptions **Testing**: - Updated test expectations for new decision phase naming - All component tests passing **Why**: Eliminates confusion from dual naming conventions. Frontend now uses same vocabulary as backend. Fixes runtime type errors from enum value mismatches. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9627a79dce
commit
1373286391
@ -90,8 +90,8 @@ Game state contains minimal `LineupPlayerState` (lineup_id, position). Use `game
|
||||
|
||||
## References
|
||||
|
||||
- **WebSocket Protocol Spec**: `../.claude/WEBSOCKET_PROTOCOL_SPEC.md` - Complete event catalog and workflow
|
||||
- **Implementation Guide**: `../.claude/implementation/01-infrastructure.md`
|
||||
- **WebSocket Protocol**: `../.claude/implementation/websocket-protocol.md`
|
||||
- **Full PRD**: `../prd-web-scorecard-1.1.md`
|
||||
|
||||
---
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
type="submit"
|
||||
variant="success"
|
||||
size="lg"
|
||||
:disabled="!isActive || !hasChanges"
|
||||
:disabled="!isActive"
|
||||
:loading="submitting"
|
||||
full-width
|
||||
>
|
||||
@ -213,7 +213,7 @@ const holdingDisplay = computed(() => {
|
||||
}).join(', ')
|
||||
})
|
||||
|
||||
// Check if setup has changed from initial
|
||||
// Check if setup has changed from initial (for display only)
|
||||
const hasChanges = computed(() => {
|
||||
if (!props.currentSetup) return true
|
||||
return (
|
||||
@ -225,13 +225,13 @@ const hasChanges = computed(() => {
|
||||
|
||||
const submitButtonText = computed(() => {
|
||||
if (!props.isActive) return 'Wait for Your Turn'
|
||||
if (!hasChanges.value) return 'No Changes'
|
||||
if (!hasChanges.value) return 'Submit (Keep Setup)'
|
||||
return 'Submit Defensive Setup'
|
||||
})
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = async () => {
|
||||
if (!props.isActive || !hasChanges.value) return
|
||||
if (!props.isActive) return
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
type="submit"
|
||||
variant="success"
|
||||
size="lg"
|
||||
:disabled="!isActive || !hasChanges"
|
||||
:disabled="!isActive"
|
||||
:loading="submitting"
|
||||
full-width
|
||||
>
|
||||
@ -209,7 +209,7 @@ const hasChanges = computed(() => {
|
||||
|
||||
const submitButtonText = computed(() => {
|
||||
if (!props.isActive) return 'Wait for Your Turn'
|
||||
if (!hasChanges.value) return 'No Changes'
|
||||
if (!hasChanges.value) return 'Submit (Keep Action)'
|
||||
return 'Submit Offensive Strategy'
|
||||
})
|
||||
|
||||
@ -240,7 +240,7 @@ const getActionButtonClasses = (action: OffensiveDecision['action'], disabled: b
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!props.isActive || !hasChanges.value) return
|
||||
if (!props.isActive) return
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
|
||||
@ -61,6 +61,8 @@
|
||||
<ManualOutcomeEntry
|
||||
:roll-data="pendingRoll"
|
||||
:can-submit="canSubmitOutcome"
|
||||
:outs="outs"
|
||||
:has-runners="hasRunners"
|
||||
@submit="handleSubmitOutcome"
|
||||
@cancel="handleCancelOutcome"
|
||||
/>
|
||||
@ -114,9 +116,14 @@ interface Props {
|
||||
pendingRoll: RollData | null
|
||||
lastPlayResult: PlayResult | null
|
||||
canSubmitOutcome: boolean
|
||||
outs?: number
|
||||
hasRunners?: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
outs: 0,
|
||||
hasRunners: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
rollDice: []
|
||||
|
||||
@ -107,9 +107,14 @@ import type { PlayOutcome, RollData } from '~/types'
|
||||
interface Props {
|
||||
rollData: RollData | null
|
||||
canSubmit: boolean
|
||||
outs?: number
|
||||
hasRunners?: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
outs: 0,
|
||||
hasRunners: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [{ outcome: PlayOutcome; hitLocation?: string }]
|
||||
@ -120,48 +125,25 @@ const emit = defineEmits<{
|
||||
const selectedOutcome = ref<PlayOutcome | null>(null)
|
||||
const selectedHitLocation = ref<string | null>(null)
|
||||
|
||||
// Outcome categories
|
||||
const outcomeCategories = [
|
||||
{
|
||||
name: 'Outs',
|
||||
outcomes: ['STRIKEOUT', 'GROUNDOUT', 'FLYOUT', 'LINEOUT', 'POPOUT', 'DOUBLE_PLAY'] as PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Hits',
|
||||
outcomes: ['SINGLE_1', 'SINGLE_2', 'SINGLE_UNCAPPED', 'DOUBLE_2', 'DOUBLE_3', 'DOUBLE_UNCAPPED', 'TRIPLE', 'HOMERUN'] as PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Walks / HBP',
|
||||
outcomes: ['WALK', 'INTENTIONAL_WALK', 'HIT_BY_PITCH'] as PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Special',
|
||||
outcomes: ['ERROR'] as PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Interrupts',
|
||||
outcomes: ['STOLEN_BASE', 'CAUGHT_STEALING', 'WILD_PITCH', 'PASSED_BALL', 'BALK', 'PICK_OFF'] as PlayOutcome[],
|
||||
},
|
||||
]
|
||||
// Import centralized outcome constants
|
||||
import { OUTCOME_CATEGORIES, OUTCOMES_REQUIRING_HIT_LOCATION, HIT_LOCATIONS } from '~/constants/outcomes'
|
||||
|
||||
// Hit location options
|
||||
const infieldPositions = ['P', 'C', '1B', '2B', '3B', 'SS']
|
||||
const outfieldPositions = ['LF', 'CF', 'RF']
|
||||
|
||||
// Outcomes that require hit location
|
||||
// Only outcomes that affect defensive plays require location
|
||||
const outcomesNeedingHitLocation = [
|
||||
'GROUNDOUT', // All groundouts
|
||||
'FLYOUT', // All flyouts
|
||||
'LINEOUT', // All lineouts
|
||||
'SINGLE_UNCAPPED', // Decision tree hits
|
||||
'DOUBLE_UNCAPPED', // Decision tree hits
|
||||
'ERROR', // Defensive plays
|
||||
]
|
||||
// Use imported constants
|
||||
const outcomeCategories = OUTCOME_CATEGORIES
|
||||
const infieldPositions = HIT_LOCATIONS.infield
|
||||
const outfieldPositions = HIT_LOCATIONS.outfield
|
||||
const outcomesNeedingHitLocation = OUTCOMES_REQUIRING_HIT_LOCATION
|
||||
|
||||
// Computed
|
||||
const needsHitLocation = computed(() => {
|
||||
return selectedOutcome.value !== null && outcomesNeedingHitLocation.includes(selectedOutcome.value)
|
||||
if (!selectedOutcome.value) return false
|
||||
if (!(outcomesNeedingHitLocation as readonly string[]).includes(selectedOutcome.value)) return false
|
||||
|
||||
// Hit location only matters when there are runners on base AND less than 2 outs
|
||||
// (for fielding choices and runner advancement)
|
||||
const hasRunnersAndCanAdvance = props.hasRunners && props.outs < 2
|
||||
|
||||
return hasRunnersAndCanAdvance
|
||||
})
|
||||
|
||||
const canSubmitForm = computed(() => {
|
||||
@ -174,7 +156,7 @@ const canSubmitForm = computed(() => {
|
||||
const selectOutcome = (outcome: PlayOutcome) => {
|
||||
selectedOutcome.value = outcome
|
||||
// Clear hit location if the new outcome doesn't need it
|
||||
if (!outcomesNeedingHitLocation.includes(outcome)) {
|
||||
if (!(outcomesNeedingHitLocation as readonly string[]).includes(outcome)) {
|
||||
selectedHitLocation.value = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,6 +235,11 @@ export function useWebSocket() {
|
||||
console.log('[WebSocket] Server confirmed connection for user:', data.user_id)
|
||||
})
|
||||
|
||||
socketInstance.on('game_joined', (data: { game_id: string; role: string }) => {
|
||||
console.log('[WebSocket] Successfully joined game:', data.game_id, 'as', data.role)
|
||||
uiStore.showSuccess(`Joined game as ${data.role}`)
|
||||
})
|
||||
|
||||
socketInstance.on('heartbeat_ack', () => {
|
||||
// Heartbeat acknowledged - connection is healthy
|
||||
// No action needed, just prevents timeout
|
||||
@ -263,12 +268,6 @@ export function useWebSocket() {
|
||||
})
|
||||
})
|
||||
|
||||
socketInstance.on('play_completed', (play) => {
|
||||
console.log('[WebSocket] Play completed:', play.description)
|
||||
gameStore.addPlayToHistory(play)
|
||||
uiStore.showInfo(play.description, 3000)
|
||||
})
|
||||
|
||||
socketInstance.on('inning_change', (data) => {
|
||||
console.log(`[WebSocket] Inning change: ${data.half} ${data.inning}`)
|
||||
uiStore.showInfo(`${data.half === 'top' ? 'Top' : 'Bottom'} ${data.inning}`, 3000)
|
||||
@ -294,6 +293,14 @@ export function useWebSocket() {
|
||||
socketInstance.on('defensive_decision_submitted', (data) => {
|
||||
console.log('[WebSocket] Defensive decision submitted')
|
||||
gameStore.clearDecisionPrompt()
|
||||
|
||||
// Request updated game state to get new decision_phase
|
||||
if (socketInstance && gameStore.gameId) {
|
||||
socketInstance.emit('request_game_state', {
|
||||
game_id: gameStore.gameId
|
||||
})
|
||||
}
|
||||
|
||||
if (data.pending_decision) {
|
||||
uiStore.showInfo('Defense set. Waiting for offense...', 3000)
|
||||
} else {
|
||||
@ -304,6 +311,14 @@ export function useWebSocket() {
|
||||
socketInstance.on('offensive_decision_submitted', (data) => {
|
||||
console.log('[WebSocket] Offensive decision submitted')
|
||||
gameStore.clearDecisionPrompt()
|
||||
|
||||
// Request updated game state to get new decision_phase
|
||||
if (socketInstance && gameStore.gameId) {
|
||||
socketInstance.emit('request_game_state', {
|
||||
game_id: gameStore.gameId
|
||||
})
|
||||
}
|
||||
|
||||
uiStore.showSuccess('Offense set. Ready to play!', 3000)
|
||||
})
|
||||
|
||||
@ -316,8 +331,8 @@ export function useWebSocket() {
|
||||
gameStore.setPendingRoll({
|
||||
roll_id: data.roll_id,
|
||||
d6_one: data.d6_one,
|
||||
d6_two_a: 0, // Not provided by server
|
||||
d6_two_b: 0, // Not provided by server
|
||||
d6_two_a: data.d6_two_a,
|
||||
d6_two_b: data.d6_two_b,
|
||||
d6_two_total: data.d6_two_total,
|
||||
chaos_d20: data.chaos_d20,
|
||||
resolution_d20: data.resolution_d20,
|
||||
|
||||
93
frontend-sba/constants/outcomes.ts
Normal file
93
frontend-sba/constants/outcomes.ts
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Outcome Constants
|
||||
*
|
||||
* Centralized definitions for play outcomes used across the UI.
|
||||
* These match the backend PlayOutcome enum values.
|
||||
*/
|
||||
|
||||
import type { PlayOutcome } from '~/types/game'
|
||||
|
||||
/**
|
||||
* Outcome categories for UI display and selection
|
||||
* Grouped by common baseball categories for user-friendly selection
|
||||
*/
|
||||
export const OUTCOME_CATEGORIES = [
|
||||
{
|
||||
name: 'Outs',
|
||||
outcomes: [
|
||||
'strikeout',
|
||||
'groundball_a',
|
||||
'groundball_b',
|
||||
'groundball_c',
|
||||
'flyout_a',
|
||||
'flyout_b',
|
||||
'flyout_c',
|
||||
'lineout',
|
||||
'popout',
|
||||
] as const satisfies readonly PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Hits',
|
||||
outcomes: [
|
||||
'single_1',
|
||||
'single_2',
|
||||
'single_uncapped',
|
||||
'double_2',
|
||||
'double_3',
|
||||
'double_uncapped',
|
||||
'triple',
|
||||
'homerun',
|
||||
] as const satisfies readonly PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Walks / HBP',
|
||||
outcomes: [
|
||||
'walk',
|
||||
'intentional_walk',
|
||||
'hbp',
|
||||
] as const satisfies readonly PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Special',
|
||||
outcomes: [
|
||||
'error',
|
||||
'x_check',
|
||||
] as const satisfies readonly PlayOutcome[],
|
||||
},
|
||||
{
|
||||
name: 'Interrupts',
|
||||
outcomes: [
|
||||
'stolen_base',
|
||||
'caught_stealing',
|
||||
'wild_pitch',
|
||||
'passed_ball',
|
||||
'balk',
|
||||
'pick_off',
|
||||
] as const satisfies readonly PlayOutcome[],
|
||||
},
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Outcomes that require a hit location to be specified
|
||||
* These outcomes involve defensive plays where location matters
|
||||
*/
|
||||
export const OUTCOMES_REQUIRING_HIT_LOCATION = [
|
||||
'groundball_a',
|
||||
'groundball_b',
|
||||
'groundball_c',
|
||||
'flyout_a',
|
||||
'flyout_b',
|
||||
'flyout_c',
|
||||
'lineout',
|
||||
'single_uncapped',
|
||||
'double_uncapped',
|
||||
'error',
|
||||
] as const satisfies readonly PlayOutcome[]
|
||||
|
||||
/**
|
||||
* Hit location options
|
||||
*/
|
||||
export const HIT_LOCATIONS = {
|
||||
infield: ['P', 'C', '1B', '2B', '3B', 'SS'] as const,
|
||||
outfield: ['LF', 'CF', 'RF'] as const,
|
||||
} as const
|
||||
@ -86,6 +86,8 @@
|
||||
:pending-roll="pendingRoll"
|
||||
:last-play-result="lastPlayResult"
|
||||
:can-submit-outcome="canSubmitOutcome"
|
||||
:outs="gameState?.outs ?? 0"
|
||||
:has-runners="!!(gameState?.runners?.first || gameState?.runners?.second || gameState?.runners?.third)"
|
||||
@roll-dice="handleRollDice"
|
||||
@submit-outcome="handleSubmitOutcome"
|
||||
@dismiss-result="handleDismissResult"
|
||||
@ -137,6 +139,8 @@
|
||||
:pending-roll="pendingRoll"
|
||||
:last-play-result="lastPlayResult"
|
||||
:can-submit-outcome="canSubmitOutcome"
|
||||
:outs="gameState?.outs ?? 0"
|
||||
:has-runners="!!(gameState?.runners?.first || gameState?.runners?.second || gameState?.runners?.third)"
|
||||
@roll-dice="handleRollDice"
|
||||
@submit-outcome="handleSubmitOutcome"
|
||||
@dismiss-result="handleDismissResult"
|
||||
@ -303,6 +307,7 @@ const needsDefensiveDecision = computed(() => gameStore.needsDefensiveDecision)
|
||||
const needsOffensiveDecision = computed(() => gameStore.needsOffensiveDecision)
|
||||
const pendingRoll = computed(() => gameStore.pendingRoll)
|
||||
const lastPlayResult = computed(() => gameStore.lastPlayResult)
|
||||
const currentDecisionPrompt = computed(() => gameStore.currentDecisionPrompt)
|
||||
|
||||
// Local UI state
|
||||
const isLoading = ref(true)
|
||||
@ -353,12 +358,36 @@ const decisionPhase = computed(() => {
|
||||
|
||||
// Phase F6: Conditional panel rendering
|
||||
const showDecisions = computed(() => {
|
||||
return gameState.value?.status === 'active' &&
|
||||
// Don't show decision panels if there's a result pending dismissal
|
||||
if (lastPlayResult.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
const result = gameState.value?.status === 'active' &&
|
||||
isMyTurn.value &&
|
||||
(needsDefensiveDecision.value || needsOffensiveDecision.value)
|
||||
|
||||
// Debug logging
|
||||
console.log('[Game Page] Panel visibility check:', {
|
||||
gameStatus: gameState.value?.status,
|
||||
isMyTurn: isMyTurn.value,
|
||||
needsDefensiveDecision: needsDefensiveDecision.value,
|
||||
needsOffensiveDecision: needsOffensiveDecision.value,
|
||||
decision_phase: gameState.value?.decision_phase,
|
||||
showDecisions: result,
|
||||
currentDecisionPrompt: currentDecisionPrompt.value,
|
||||
hasLastPlayResult: !!lastPlayResult.value
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const showGameplay = computed(() => {
|
||||
// Show gameplay panel if there's a result to display OR if we're in the resolution phase
|
||||
if (lastPlayResult.value) {
|
||||
return true
|
||||
}
|
||||
|
||||
return gameState.value?.status === 'active' &&
|
||||
isMyTurn.value &&
|
||||
!needsDefensiveDecision.value &&
|
||||
|
||||
@ -107,15 +107,21 @@ export const useGameStore = defineStore('game', () => {
|
||||
})
|
||||
|
||||
const needsDefensiveDecision = computed(() => {
|
||||
return currentDecisionPrompt.value?.phase === 'defense'
|
||||
// Standardized naming (2025-01-21): Both backend and frontend now use 'awaiting_defensive'
|
||||
return currentDecisionPrompt.value?.phase === 'awaiting_defensive' ||
|
||||
gameState.value?.decision_phase === 'awaiting_defensive'
|
||||
})
|
||||
|
||||
const needsOffensiveDecision = computed(() => {
|
||||
return currentDecisionPrompt.value?.phase === 'offensive_approach'
|
||||
// Standardized naming (2025-01-21): Both backend and frontend now use 'awaiting_offensive'
|
||||
return currentDecisionPrompt.value?.phase === 'awaiting_offensive' ||
|
||||
gameState.value?.decision_phase === 'awaiting_offensive'
|
||||
})
|
||||
|
||||
const needsStolenBaseDecision = computed(() => {
|
||||
return currentDecisionPrompt.value?.phase === 'stolen_base'
|
||||
// Standardized naming (2025-01-21): Both backend and frontend now use 'awaiting_stolen_base'
|
||||
return currentDecisionPrompt.value?.phase === 'awaiting_stolen_base' ||
|
||||
gameState.value?.decision_phase === 'awaiting_stolen_base'
|
||||
})
|
||||
|
||||
const canRollDice = computed(() => {
|
||||
|
||||
@ -223,7 +223,7 @@ describe('DefensiveSetup', () => {
|
||||
expect(wrapper.vm.submitButtonText).toBe('Wait for Your Turn')
|
||||
})
|
||||
|
||||
it('shows "No Changes" when setup unchanged', () => {
|
||||
it('shows "Submit (Keep Setup)" when setup unchanged', () => {
|
||||
const currentSetup: DefensiveDecision = {
|
||||
infield_depth: 'normal',
|
||||
outfield_depth: 'normal',
|
||||
@ -237,7 +237,7 @@ describe('DefensiveSetup', () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(wrapper.vm.submitButtonText).toBe('No Changes')
|
||||
expect(wrapper.vm.submitButtonText).toBe('Submit (Keep Setup)')
|
||||
})
|
||||
|
||||
it('shows "Submit Defensive Setup" when active with changes', () => {
|
||||
|
||||
@ -31,7 +31,7 @@ const createMockGameState = (overrides?: Partial<GameState>): GameState => ({
|
||||
current_batter: null,
|
||||
current_pitcher: null,
|
||||
current_catcher: null,
|
||||
decision_phase: 'defense',
|
||||
decision_phase: 'awaiting_defensive',
|
||||
...overrides,
|
||||
})
|
||||
|
||||
|
||||
@ -34,8 +34,11 @@ 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 = 'defense' | 'stolen_base' | 'offensive_approach' | 'resolution' | 'complete'
|
||||
export type DecisionPhase = 'awaiting_defensive' | 'awaiting_stolen_base' | 'awaiting_offensive' | 'resolution' | 'complete'
|
||||
|
||||
/**
|
||||
* Lineup player state - represents a player in the game
|
||||
@ -156,50 +159,54 @@ export interface RollData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Play outcome enumeration (subset of backend PlayOutcome)
|
||||
* Play outcome enumeration (matches backend PlayOutcome enum values)
|
||||
* Note: These are the STRING VALUES, not enum names
|
||||
*/
|
||||
export type PlayOutcome =
|
||||
// Standard outs
|
||||
| 'STRIKEOUT'
|
||||
| 'GROUNDOUT'
|
||||
| 'FLYOUT'
|
||||
| 'LINEOUT'
|
||||
| 'POPOUT'
|
||||
| 'DOUBLE_PLAY'
|
||||
| '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'
|
||||
| 'HIT_BY_PITCH'
|
||||
| 'walk'
|
||||
| 'intentional_walk'
|
||||
| 'hbp'
|
||||
|
||||
// Hits (capped - specific bases)
|
||||
| 'SINGLE_1'
|
||||
| 'SINGLE_2'
|
||||
| 'DOUBLE_2'
|
||||
| 'DOUBLE_3'
|
||||
| 'TRIPLE'
|
||||
| 'HOMERUN'
|
||||
|
||||
// Uncapped hits (require advancement decisions)
|
||||
| 'SINGLE_UNCAPPED'
|
||||
| 'DOUBLE_UNCAPPED'
|
||||
| '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'
|
||||
| 'stolen_base'
|
||||
| 'caught_stealing'
|
||||
| 'wild_pitch'
|
||||
| 'passed_ball'
|
||||
| 'balk'
|
||||
| 'pick_off'
|
||||
|
||||
// Errors
|
||||
| 'ERROR'
|
||||
| 'error'
|
||||
| 'x_check'
|
||||
|
||||
// Ballpark power (PD specific)
|
||||
| 'BP_HOMERUN'
|
||||
| 'BP_FLYOUT'
|
||||
| 'BP_SINGLE'
|
||||
| 'BP_LINEOUT'
|
||||
| 'bp_homerun'
|
||||
| 'bp_flyout'
|
||||
| 'bp_single'
|
||||
| 'bp_lineout'
|
||||
|
||||
/**
|
||||
* Runner advancement during a play
|
||||
|
||||
Loading…
Reference in New Issue
Block a user