Gameplay & Scoring Flow
Lifecycle summary
Create Game
→ Setup Sides
→ Start Game
→ Start Turn
→ Start Player Turn
→ Record Events
→ End Player Turn
→ End Turn
→ End Game
→ Finalise Game
Step 1: game creation and setup
A game begins as a games row, normally in a setup state. During setup:
- two game_sides rows are assigned
- each side is linked to an army_list
- first side and scenario/system information are established
- optional tournament/round context may also be attached
Step 2: game start
Starting a game is not just a status change. It also instantiates runtime unit data.
army_units
↓ copied/snapshotted
game_units
This is one of the most important design decisions in the system: gameplay state is stored separately from army construction state.
Step 3: turn structure
The runtime turn model has two levels:
- game_turns for the overall game turn number
- game_player_turns for side-specific turns inside that game turn
This structure matters because scoring and destruction state are often attributed to a specific side’s player turn rather than just to a whole game turn.
Step 4: event creation
The main scoring and action ledger is game_events. When something happens in the game, the app records an event with:
- game context
- turn/player-turn context
- scoring side
- event type
- points delta
- unit reference where relevant
Step 5: runtime unit state updates
Some state is not left purely in the event ledger. It is also applied to game_units.
Examples include:
- destroyed state
- below 25% state
- which side scored the state
- which player turn caused the state
This denormalization exists so play-state reads are simpler and faster.
Step 6: score derivation
Score is built from two related sources:
- the event ledger for points deltas and history
- game-unit state for runtime projection and rule enforcement
The UI page Game Play and related backend endpoints use both sources to present current state and totals.
Step 7: ending and finalising
Ending a game and finalising a game are separate concepts:
- End game marks gameplay as complete
- Finalise game stores final outcome and, where relevant, applies tournament round constraints
Final scores are written back to the games header
as final_score_a and final_score_b.
Gameplay flow by layer
| Layer | Responsibility during play |
|---|---|
| App UI | Render state, initiate events, show score progression |
| API | Validate rules, create/delete events, update runtime state |
| SQL | Persist game header, turns, runtime units and event ledger |
Why the model is strong
- Original army data stays separate from gameplay runtime
- Detailed event history remains available for breakdown/reporting
- Current play state does not require full event replay for every read
Main scoring-flow risks
- Event deletion can desynchronize runtime unit state if not handled carefully
- Soft references in runtime fields mean integrity is partly application-controlled
- Rule changes in event logic can affect historical interpretation if not isolated