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.
 
 
temptress-bot/src/game/handleEvent.ts

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: [],
}
}