feat: initial commit with M1-M4 implementation
This commit is contained in:
75
docs/specs/2026-05-12-match-logic-design.md
Normal file
75
docs/specs/2026-05-12-match-logic-design.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Design: Match Logic & Game Rules (M5)
|
||||
|
||||
**Goal:** Implement a high-level match management system to handle scoring, game timing, and state transitions (Playing, Goal Pause, Match Ended).
|
||||
|
||||
**Architecture:**
|
||||
Introduction of a `MatchManager` component that acts as a controller over the `World`. The `World` remains responsible for physics and AI, while the `MatchManager` handles the "rules of the sport".
|
||||
|
||||
---
|
||||
|
||||
## 1. Components
|
||||
|
||||
### MatchManager
|
||||
The central authority for the match state.
|
||||
- **GameState**: `Playing`, `GoalPause`, `MatchEnded`.
|
||||
- **Score**: `ScoreRed`, `ScoreBlue` (integers).
|
||||
- **Match Timer**: `MatchDuration` (total time) and `CurrentTime` (elapsed).
|
||||
- **Pause Timer**: `PauseDuration` (fixed duration for goal celebration) and `PauseTimer` (countdown).
|
||||
- **Reference**: Pointer to `World`.
|
||||
|
||||
### World (Extensions)
|
||||
- **`CheckGoal() (team entities.Team, scored bool)`**: Checks if the puck has crossed the goal lines (X < 0 or X > WorldWidth).
|
||||
- **`ResetPositions()`**: Resets the puck to the center and all players to their `HomePosition`, zeroing out all velocities.
|
||||
|
||||
---
|
||||
|
||||
## 2. File Structure
|
||||
|
||||
- **`internal/game/match_manager.go` (New)**:
|
||||
- Definition of `GameState` and `MatchManager` struct.
|
||||
- `Update()` method to drive the match lifecycle.
|
||||
- **`internal/game/world.go` (Modified)**:
|
||||
- Implementation of `CheckGoal()` and `ResetPositions()`.
|
||||
- **`cmd/game/main.go` (Modified)**:
|
||||
- Replace `World` with `MatchManager` in the `Game` struct.
|
||||
- Update `Update()` and `Draw()` calls to go through `MatchManager`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Data Flow
|
||||
|
||||
### Main Loop $\rightarrow$ MatchManager
|
||||
`Game.Update()` $\rightarrow$ `MatchManager.Update()`:
|
||||
1. **If `Playing`**:
|
||||
- Call `World.CheckGoal()`.
|
||||
- If goal $\rightarrow$ transition to `GoalPause`, increment score, start `PauseTimer`.
|
||||
- If no goal $\rightarrow$ call `World.Update()` (physics/AI) and increment `CurrentTime`.
|
||||
- If `CurrentTime >= MatchDuration` $\rightarrow$ transition to `MatchEnded`.
|
||||
2. **If `GoalPause`**:
|
||||
- Decrement `PauseTimer`.
|
||||
- If `PauseTimer <= 0` $\rightarrow$ call `World.ResetPositions()` and transition to `Playing`.
|
||||
- `World.Update()` is skipped (game is frozen).
|
||||
3. **If `MatchEnded`**:
|
||||
- `World.Update()` is skipped.
|
||||
|
||||
### World $\rightarrow$ MatchManager (Goal Detection)
|
||||
- `X < 0` $\rightarrow$ `(TeamBlue, true)`
|
||||
- `X > WorldWidth` $\rightarrow$ `(TeamRed, true)`
|
||||
- Otherwise $\rightarrow$ `(_, false)`
|
||||
|
||||
---
|
||||
|
||||
## 4. Edge Cases & Handling
|
||||
|
||||
- **Double Scoring**: The `GoalPause` state prevents `CheckGoal` from being called repeatedly until the world is reset.
|
||||
- **Reset Collisions**: `ResetPositions` explicitly sets `Velocity = 0` for all entities to prevent immediate "explosions" from anti-clumping logic.
|
||||
- **Boundary Stuck**: `CheckGoal` is evaluated before physics clamping to ensure goals are registered even at high speeds.
|
||||
- **Last-second Goal**: Goals are processed before the match timer check, ensuring a goal on the final frame is counted.
|
||||
|
||||
---
|
||||
|
||||
## 5. Success Criteria
|
||||
- Puck crossing the boundary increments the correct team's score.
|
||||
- The game freezes for a few seconds after a goal.
|
||||
- All entities return to starting positions after the pause.
|
||||
- The match stops automatically when the timer reaches the limit.
|
||||
Reference in New Issue
Block a user