diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 864d98f..a48f8b5 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,11 @@
-
-
+
+
+
+
@@ -51,7 +53,7 @@
"nodejs_package_manager_path": "npm",
"prettierjs.PrettierConfiguration.Package": "/home/reya/WebstormProjects/steppies/node_modules/prettier",
"settings.editor.selected.configurable": "configurable.group.appearance",
- "ts.external.directory.path": "/opt/WebStorm/plugins/JavaScriptLanguage/jsLanguageServicesImpl/external",
+ "ts.external.directory.path": "/home/reya/WebstormProjects/steppies/node_modules/typescript/lib",
"vue.rearranger.settings.migration": "true"
}
}]]>
@@ -92,8 +94,23 @@
1669828165304
-
+
+
+ 1669853979470
+
+
+
+ 1669853979470
+
+
+ 1669854047768
+
+
+
+ 1669854047768
+
+
@@ -110,6 +127,10 @@
+
+
+
+
diff --git a/src/state/gamestate.spec.ts b/src/state/gameData.spec.ts
similarity index 97%
rename from src/state/gamestate.spec.ts
rename to src/state/gameData.spec.ts
index 1ff5e5c..3414f55 100644
--- a/src/state/gamestate.spec.ts
+++ b/src/state/gameData.spec.ts
@@ -1,5 +1,5 @@
import { describe, expect, test } from '@jest/globals'
-import { DieState, isHeldState, isSelectedState, setDeselected, setSelected, toggleSelected } from './gamestate'
+import { DieState, isHeldState, isSelectedState, setDeselected, setSelected, toggleSelected } from './gameData'
describe('isHeldState', () => {
test.each<[DieState, boolean]>([
diff --git a/src/state/gamestate.ts b/src/state/gameData.ts
similarity index 95%
rename from src/state/gamestate.ts
rename to src/state/gameData.ts
index cc0eff9..76a10db 100644
--- a/src/state/gamestate.ts
+++ b/src/state/gameData.ts
@@ -200,6 +200,7 @@ export interface GameTheme {
readonly difficulties: readonly Difficulty[]
+ readonly narratorName: string
readonly topText: PlayerText
readonly bottomText: PlayerText
}
@@ -296,9 +297,18 @@ export interface PlayerState {
readonly timesRecovered: number
}
-export interface GameState {
- // The version of the serialization format this state was saved with.
+export enum GameState {
+ ONGOING = 'ongoing',
+ ABORTED = 'aborted',
+ TOP_WINS = 'top_wins',
+ BOTTOM_WINS = 'bottom_wins',
+}
+
+export interface GameData {
+ // The version of the serialization format this data was saved with.
readonly version: number
+ // Whether the game is still ongoing, or if not, who won (if anyone).
+ readonly gameState: GameState
// The state of the top player
readonly top: PlayerState
@@ -310,21 +320,18 @@ export interface GameState {
// The difficulty selected for this game.
readonly difficulty: Difficulty
- // Null if the top is a computer.
- readonly topHumanId: string | null
- // Null if the bottom is a computer.
- readonly bottomHumanId: string | null
-
- // If true, the top is choosing the action this round.
+ // If true, the top chose/is choosing the action this round.
readonly isTopRound: boolean
- // The action chosen for this round, or null if the current player is choosing an action
+ // The action chosen for this round, or null if the current player has not chosen an action yet.
+ // The action is locked in if lastRoll is not null or lastTurnTotal is not 0.
readonly action: GameAction | null
// If true, the top is rolling this turn.
readonly isTopTurn: boolean
// The total for the previous turn, or 0 if the current player is taking the first turn
// If the current player fails when the turn total is 0, they take the penalty and pass the initiative without taking damage.
- // This can still end the game if the current action is capable of ending the game for this player.
+ // This can still end the game if the current action is capable of ending the game for this player and
+ // this player has more damage than their maximum.
readonly lastTurnTotal: number
// The dice available for the current turn, or null if the current player has not rolled yet this turn.
readonly lastRoll: readonly DieResult[] | null
diff --git a/src/state/gameEvent.ts b/src/state/gameEvent.ts
new file mode 100644
index 0000000..483f4ff
--- /dev/null
+++ b/src/state/gameEvent.ts
@@ -0,0 +1,168 @@
+import { DieResult, GameData } from './gameData'
+
+export enum IncomingEventType {
+ // Chooses the action to be performed for this round but does not lock it in until the player rolls.
+ SelectAction = 'select_action',
+ // Dice buttons toggle the selection of that die
+ ToggleSelectDie = 'toggle_select_die',
+ // Button 1 is Select All/Deselect All
+ // Select All appears if no dice are selected
+ // Deselect All displays if at least one die is selected
+ SelectAllDice = 'select_all_dice',
+ DeselectAllDice = 'deselect_all_dice',
+ // Button 2 is Hold/Release selected dice
+ // If only held dice are selected, Release appears
+ // For selections with at least one unheld die, Hold appears
+ HoldSelectedDice = 'hold_selected_dice',
+ ReleaseSelectedDice = 'release_selected_dice',
+ // Button 3 is End Turn/Score Selected Dice
+ // End Turn appears if at least one Stop die is selected, but is only enabled if sufficient Stop dice are selected
+ // and no non-Stop dice are selected
+ // Score Selected Dice appears otherwise, but is only enabled if at least one scoreable die is selected and no
+ // non-scoreable dice are selected
+ EndTurn = 'end_turn',
+ ScoreSelectedDice = 'score_selected_dice',
+ // Button 4 is Roll Dice
+ // It's always visible, but is disabled if there are no unheld dice
+ RollDice = 'roll_dice',
+}
+
+export interface IncomingBaseEvent {
+ // The type of event to execute.
+ readonly type: IncomingEventType
+}
+
+export interface IncomingIndexedEvent extends IncomingBaseEvent {
+ readonly type: IncomingEventType.SelectAction | IncomingEventType.ToggleSelectDie
+ readonly index: number
+}
+
+export interface IncomingSimpleEvent extends IncomingBaseEvent {
+ readonly type:
+ | IncomingEventType.SelectAllDice
+ | IncomingEventType.HoldSelectedDice
+ | IncomingEventType.ReleaseSelectedDice
+ | IncomingEventType.ScoreSelectedDice
+ | IncomingEventType.EndTurn
+ | IncomingEventType.DeselectAllDice
+ | IncomingEventType.RollDice
+}
+
+export type IncomingEvent = IncomingIndexedEvent | IncomingSimpleEvent
+
+export enum ErrorType {
+ NotValidRightNow = 'not_valid_right_now',
+ InvalidIndex = 'invalid_index',
+ InsufficientMatchingDice = 'insufficient_matching_dice',
+ DataLoadFailed = 'data_load_failed',
+ UnexpectedError = 'unexpected_error',
+}
+
+export interface GameError {
+ readonly type: ErrorType
+ readonly message: string
+}
+
+export enum OutgoingEventType {
+ Dialogue = 'dialogue',
+ Description = 'description',
+
+ DamageOrRecovery = 'damage',
+ ChangeStops = 'change_stops',
+ ChangeFails = 'change_fails',
+ ChangeDamageBonus = 'change_damage_bonus',
+
+ ScoreDice = 'score_dice',
+ GainFailures = 'gain_failures',
+
+ LoseRound = 'lose_round',
+ LoseGame = 'lose_game',
+
+ StartRoll = 'new_roll',
+ StartTurn = 'new_turn',
+ StartRound = 'new_round',
+}
+
+export enum EventTarget {
+ Top = 'top',
+ Bottom = 'bottom',
+}
+
+export enum TextSender {
+ Top = 'top',
+ Bottom = 'bottom',
+ Narrator = 'narrator',
+}
+
+export interface OutgoingBaseEvent {
+ readonly type: OutgoingEventType
+}
+
+export interface OutgoingTextEvent extends OutgoingBaseEvent {
+ readonly type: OutgoingEventType.Description | OutgoingEventType.Dialogue
+ readonly sender: TextSender
+ readonly text: string
+}
+
+export interface OutgoingBaseTargetedEvent extends OutgoingBaseEvent {
+ readonly type:
+ | OutgoingEventType.DamageOrRecovery
+ | OutgoingEventType.ChangeStops
+ | OutgoingEventType.ChangeFails
+ | OutgoingEventType.ChangeDamageBonus
+ | OutgoingEventType.LoseRound
+ | OutgoingEventType.LoseGame
+ | OutgoingEventType.ScoreDice
+ | OutgoingEventType.GainFailures
+ | OutgoingEventType.StartRoll
+ | OutgoingEventType.StartTurn
+ | OutgoingEventType.StartRound
+ readonly target: EventTarget
+}
+
+export interface OutgoingSimpleTargetedEvent extends OutgoingBaseTargetedEvent {
+ readonly type: OutgoingEventType.LoseGame | OutgoingEventType.StartTurn | OutgoingEventType.StartRound
+}
+
+export interface OutgoingDamageEvent extends OutgoingBaseTargetedEvent {
+ readonly type:
+ | OutgoingEventType.DamageOrRecovery
+ | OutgoingEventType.ChangeStops
+ | OutgoingEventType.ChangeFails
+ | OutgoingEventType.ChangeDamageBonus
+ | OutgoingEventType.LoseRound
+ | OutgoingEventType.GainFailures
+ readonly delta: number
+}
+
+export interface OutgoingStartRollEvent extends OutgoingBaseTargetedEvent {
+ readonly type: OutgoingEventType.StartRoll
+ readonly newDice: DieResult[]
+}
+
+export interface OutgoingScoreDiceEvent extends OutgoingBaseTargetedEvent {
+ readonly type: OutgoingEventType.ScoreDice
+ readonly scoredDice: DieResult[]
+ readonly scoreDelta: number
+}
+
+export type OutgoingEvent =
+ | OutgoingTextEvent
+ | OutgoingSimpleTargetedEvent
+ | OutgoingDamageEvent
+ | OutgoingStartRollEvent
+ | OutgoingScoreDiceEvent
+
+export interface BaseEventResult {
+ readonly newState: GameData
+}
+
+export interface SuccessResult extends BaseEventResult {
+ readonly events: OutgoingEvent[]
+}
+
+export interface FailedResult extends BaseEventResult {
+ readonly error: GameError
+}
+
+export type EventResult = SuccessResult | FailedResult