You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
6.3 KiB
198 lines
6.3 KiB
import { ErrorType, EventResult, IncomingEvent, IncomingEventType } from './gameEvent'
|
|
import { GamePhase, GameState } from './gameState'
|
|
import { DieResult, DieState, isSelectedState, setDeselected, setSelected, toggleSelected } from './dieState'
|
|
import { isValidIndex } from '../util/precondition'
|
|
|
|
export function handleEvent(state: GameState, event: IncomingEvent): EventResult<GameState> {
|
|
try {
|
|
switch (event.type) {
|
|
case IncomingEventType.SelectAction:
|
|
return handleSelectActionEvent<GameState>(state, event)
|
|
case IncomingEventType.ToggleSelectDie:
|
|
return handleToggleSelectIndexEvent<DieResult, GameState>(state, event)
|
|
case IncomingEventType.SelectAllDice:
|
|
return handleSelectAllEvent<DieResult, GameState>(state, true)
|
|
case IncomingEventType.DeselectAllDice:
|
|
return handleSelectAllEvent<DieResult, GameState>(state, false)
|
|
case IncomingEventType.HoldSelectedDice:
|
|
return handleHoldDiceEvent<DieResult, GameState>(state, true)
|
|
case IncomingEventType.ReleaseSelectedDice:
|
|
return handleHoldDiceEvent<DieResult, GameState>(state, false)
|
|
case IncomingEventType.Abort:
|
|
return handleAbortEvent<GameState>(state)
|
|
default:
|
|
return {
|
|
newState: state,
|
|
error: {
|
|
type: ErrorType.UnexpectedError,
|
|
message: 'Event type not yet implemented',
|
|
},
|
|
}
|
|
}
|
|
} catch (e) {
|
|
return {
|
|
newState: state,
|
|
error: {
|
|
type: ErrorType.UnexpectedError,
|
|
message: `Failed handling event: ${e}`,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
export interface SelectActionState<A> {
|
|
readonly gamePhase: GamePhase
|
|
readonly theme: {
|
|
readonly actions: readonly A[]
|
|
}
|
|
readonly action: A
|
|
}
|
|
|
|
export function handleSelectActionEvent<T extends SelectActionState<unknown>>(
|
|
state: T,
|
|
{ index }: { readonly index: number },
|
|
): EventResult<T> {
|
|
if (state.gamePhase !== GamePhase.ROUND_START) {
|
|
return {
|
|
error: {
|
|
type: ErrorType.NotValidRightNow,
|
|
message: `You can only change actions when the turn has not started yet.`,
|
|
},
|
|
newState: state,
|
|
}
|
|
}
|
|
if (index < 0 || index >= state.theme.actions.length || index !== Math.floor(index)) {
|
|
return {
|
|
error: {
|
|
type: ErrorType.InvalidIndex,
|
|
message: `Select an integer action index between 0 (inclusive) and ${state.theme.actions.length} (exclusive)`,
|
|
},
|
|
newState: state,
|
|
}
|
|
}
|
|
return {
|
|
newState: {
|
|
...state,
|
|
action: state.theme.actions[index],
|
|
},
|
|
events: [],
|
|
}
|
|
}
|
|
|
|
export interface DiceStatesState<D extends { readonly state: DieState }> {
|
|
readonly gamePhase: GamePhase
|
|
readonly lastRoll: readonly D[]
|
|
}
|
|
|
|
export function handleToggleSelectIndexEvent<D extends { readonly state: DieState }, T extends DiceStatesState<D>>(
|
|
state: T,
|
|
{ index }: { readonly index: number },
|
|
): EventResult<T> {
|
|
if (state.gamePhase !== GamePhase.TURN_ROLLED) {
|
|
return {
|
|
error: {
|
|
type: ErrorType.NotValidRightNow,
|
|
message: `You can only toggle the selected state of a die when dice have been rolled.`,
|
|
},
|
|
newState: state,
|
|
}
|
|
}
|
|
if (!isValidIndex(index, state.lastRoll.length)) {
|
|
return {
|
|
newState: state,
|
|
error: {
|
|
type: ErrorType.InvalidIndex,
|
|
message: `Select an integer action index between 0 (inclusive) and ${state.lastRoll.length} (exclusive)`,
|
|
},
|
|
}
|
|
}
|
|
return {
|
|
newState: {
|
|
...state,
|
|
lastRoll: [
|
|
...state.lastRoll.slice(0, index),
|
|
{
|
|
...state.lastRoll[index],
|
|
state: toggleSelected(state.lastRoll[index].state),
|
|
},
|
|
...state.lastRoll.slice(index + 1),
|
|
],
|
|
},
|
|
events: [],
|
|
}
|
|
}
|
|
|
|
function handleSelectAllEvent<D extends { readonly state: DieState }, T extends DiceStatesState<D>>(
|
|
state: T,
|
|
select: boolean,
|
|
): EventResult<T> {
|
|
if (state.gamePhase !== GamePhase.TURN_ROLLED) {
|
|
return {
|
|
error: {
|
|
type: ErrorType.NotValidRightNow,
|
|
message: `You can only change the selected state of the dice when dice have been rolled.`,
|
|
},
|
|
newState: state,
|
|
}
|
|
}
|
|
return {
|
|
newState: {
|
|
...state,
|
|
lastRoll: state.lastRoll.map<D>((x) => ({
|
|
...x,
|
|
state: select ? setSelected(x.state) : setDeselected(x.state),
|
|
})),
|
|
},
|
|
events: [],
|
|
}
|
|
}
|
|
|
|
function handleHoldDiceEvent<D extends { readonly state: DieState }, T extends DiceStatesState<D>>(
|
|
state: T,
|
|
hold: boolean,
|
|
): EventResult<T> {
|
|
if (state.gamePhase !== GamePhase.TURN_ROLLED) {
|
|
return {
|
|
error: {
|
|
type: ErrorType.NotValidRightNow,
|
|
message: `You can only toggle the hold state of dice when dice have been rolled.`,
|
|
},
|
|
newState: state,
|
|
}
|
|
}
|
|
return {
|
|
newState: {
|
|
...state,
|
|
lastRoll: state.lastRoll.map<D>((v) => {
|
|
if (isSelectedState(v.state)) {
|
|
return {
|
|
...v,
|
|
state: hold ? DieState.HELD_SELECTED : DieState.SELECTED,
|
|
}
|
|
} else {
|
|
return v
|
|
}
|
|
}),
|
|
},
|
|
events: [],
|
|
}
|
|
}
|
|
|
|
function handleAbortEvent<T extends { readonly gamePhase: GamePhase }>(state: T): EventResult<T> {
|
|
if (state.gamePhase === GamePhase.ABORTED || state.gamePhase === GamePhase.VICTORY) {
|
|
return {
|
|
error: {
|
|
type: ErrorType.NotValidRightNow,
|
|
message: `The game is already over.`,
|
|
},
|
|
newState: state,
|
|
}
|
|
}
|
|
return {
|
|
newState: {
|
|
...state,
|
|
gamePhase: GamePhase.ABORTED,
|
|
},
|
|
events: [],
|
|
}
|
|
}
|
|
|