feat: initial commit with M1-M4 implementation

This commit is contained in:
Vladimir V Maksimov
2026-05-12 10:54:09 +03:00
commit aece34fe73
24 changed files with 1770 additions and 0 deletions

View 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.