76 lines
3.2 KiB
Markdown
76 lines
3.2 KiB
Markdown
# 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.
|