diff --git a/src/game/gameEvent.ts b/src/game/gameEvent.ts index 5ae1e7b..ab755f9 100644 --- a/src/game/gameEvent.ts +++ b/src/game/gameEvent.ts @@ -131,6 +131,8 @@ export interface OutgoingBaseTargetedEvent extends OutgoingBaseEvent { | OutgoingEventType.StartRoll | OutgoingEventType.StartTurn | OutgoingEventType.StartRound + | OutgoingEventType.FailTurn + | OutgoingEventType.AbandonTurn readonly target: PlayerSide } @@ -140,6 +142,8 @@ export interface OutgoingSimpleTargetedEvent extends OutgoingBaseTargetedEvent { | OutgoingEventType.StartTurn | OutgoingEventType.StartRound | OutgoingEventType.PassTurn + | OutgoingEventType.FailTurn + | OutgoingEventType.AbandonTurn } export interface OutgoingDamageEvent extends OutgoingBaseTargetedEvent { @@ -172,16 +176,20 @@ export type OutgoingEvent = | OutgoingStartRollEvent | OutgoingScoreDiceEvent -export interface BaseEventResult { - readonly newState: T +export enum EventResultClass { + SUCCESS = 'success', + FAILURE = 'failure', } -export interface SuccessResult extends BaseEventResult { +export interface SuccessResult { + readonly type: EventResultClass.SUCCESS + readonly newState: T readonly events: readonly OutgoingEvent[] } -export interface FailedResult extends BaseEventResult { +export interface FailedResult { + readonly type: EventResultClass.FAILURE readonly error: GameError } -export type EventResult = SuccessResult | FailedResult +export type EventResult = SuccessResult | FailedResult diff --git a/src/game/handleEvent.ts b/src/game/handleEvent.ts index f93220a..27da9d5 100644 --- a/src/game/handleEvent.ts +++ b/src/game/handleEvent.ts @@ -1,4 +1,12 @@ -import { ErrorType, EventResult, IncomingEvent, IncomingEventType, OutgoingEventType } from './gameEvent' +import { + ErrorType, + EventResult, + EventResultClass, + IncomingEvent, + IncomingEventType, + OutgoingEvent, + OutgoingEventType, +} from './gameEvent' import { GamePhase, GameState, oppositePlayer, PlayerSide, PlayerState, RoundStarter } from './gameState' import { DieFace, @@ -41,7 +49,7 @@ export function handleEvent(state: GameState, event: IncomingEvent): EventResult return handleAbortEvent(state, event) default: return { - newState: state, + type: EventResultClass.FAILURE, error: { type: ErrorType.UnexpectedError, message: 'Event type not yet implemented', @@ -50,7 +58,7 @@ export function handleEvent(state: GameState, event: IncomingEvent): EventResult } } catch (e) { return { - newState: state, + type: EventResultClass.FAILURE, error: { type: ErrorType.UnexpectedError, message: `Failed handling event: ${e}`, @@ -73,23 +81,24 @@ export function handleSelectActionEvent>( ): EventResult { if (state.gamePhase !== GamePhase.ROUND_START) { return { + type: EventResultClass.FAILURE, 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 { + type: EventResultClass.FAILURE, error: { type: ErrorType.InvalidIndex, message: `Select an integer action index between 0 (inclusive) and ${state.theme.actions.length} (exclusive)`, }, - newState: state, } } return { + type: EventResultClass.SUCCESS, newState: { ...state, action: state.theme.actions[index], @@ -109,16 +118,16 @@ export function handleToggleSelectIndexEvent { if (state.gamePhase !== GamePhase.TURN_ROLLED) { return { + type: EventResultClass.FAILURE, 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, + type: EventResultClass.FAILURE, error: { type: ErrorType.InvalidIndex, message: `Select an integer action index between 0 (inclusive) and ${state.lastRoll.length} (exclusive)`, @@ -126,6 +135,7 @@ export function handleToggleSelectIndexEvent { if (state.gamePhase !== GamePhase.TURN_ROLLED) { return { + type: EventResultClass.FAILURE, error: { type: ErrorType.NotValidRightNow, message: `You can only change the selected state of the dice when dice have been rolled.`, }, - newState: state, } } return { + type: EventResultClass.SUCCESS, newState: { ...state, lastRoll: state.lastRoll.map((x) => ({ @@ -172,14 +183,15 @@ function handleHoldDiceEvent { if (state.gamePhase !== GamePhase.TURN_ROLLED) { return { + type: EventResultClass.FAILURE, error: { type: ErrorType.NotValidRightNow, message: `You can only toggle the hold state of dice when dice have been rolled.`, }, - newState: state, } } return { + type: EventResultClass.SUCCESS, newState: { ...state, lastRoll: state.lastRoll.map((v) => { @@ -249,11 +261,11 @@ export interface EndTurnState

{ function handleEndTurnEvent

>(state: T): EventResult { if (state.gamePhase !== GamePhase.TURN_ROLLED && state.gamePhase !== GamePhase.TURN_FAILED) { return { + type: EventResultClass.FAILURE, error: { type: ErrorType.NotValidRightNow, message: `You can only end your turn when it's in progress.`, }, - newState: state, } } const failed = state.gamePhase === GamePhase.TURN_FAILED || !state.currentDiceEndTurn @@ -262,6 +274,7 @@ function handleEndTurnEvent

>( // The winner of this round has not yet been decided; this is better than the last turn this round. // Moving on to the next turn... return { + type: EventResultClass.SUCCESS, newState: { ...state, lastTurnTotal: state.currentTurnTotal, @@ -324,7 +337,12 @@ function handleEndTurnEvent

>( newPhase === GamePhase.VICTORY ? oppositePlayer(state.phaseOwner) : determineNextRoundStarter(state, state.difficulty) + const events: OutgoingEvent[] = [] + const endRoundEvent = + state.gamePhase === GamePhase.TURN_FAILED ? OutgoingEventType.FailTurn : OutgoingEventType.AbandonTurn + return { + type: EventResultClass.SUCCESS, newState: { ...state, players, @@ -338,7 +356,24 @@ function handleEndTurnEvent

>( countedFails: 0, selectedDiceValue: 0, }, - events: [], + events: [ + { + type: OutgoingEventType.PassTurn, + target: state.phaseOwner, + }, + ...(newPhase === GamePhase.VICTORY + ? [] + : [ + { + type: endRoundEvent, + target: state.phaseOwner, + }, + { + type: OutgoingEventType.StartRound, + target: newRoundOwner, + }, + ]), + ], } } }