Compare commits
No commits in common. "53938f22b5985d55383bc9873d356eeffe107b1c" and "ad2037a7a58362352b230685d717dcd275157cab" have entirely different histories.
53938f22b5
...
ad2037a7a5
64 changed files with 82 additions and 923 deletions
1
GEMINI.md
Symbolic link
1
GEMINI.md
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
CLAUDE.md
|
||||||
74
TODO.md
Normal file
74
TODO.md
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
**1. Core Gameplay Loop & State Management**
|
||||||
|
|
||||||
|
* [x] **GAL-1: Player Lives**
|
||||||
|
* [x] GAL-2: Add a `PlayerLives` resource.
|
||||||
|
* [x] GAL-3: Decrement lives on collision.
|
||||||
|
* [x] GAL-4: Implement player destruction and respawn with temporary invincibility.
|
||||||
|
* [x] **GAL-5: Game Over State**
|
||||||
|
* [x] GAL-6: Use Bevy's `States` (`Playing`, `GameOver`).
|
||||||
|
* [x] GAL-7: Transition to `GameOver` when lives reach zero.
|
||||||
|
* [x] GAL-8: Display a "Game Over" message.
|
||||||
|
* [x] **GAL-9: Scoring**
|
||||||
|
* [x] GAL-10: Add a `Score` resource.
|
||||||
|
* [x] GAL-11: Increment score when an enemy is hit.
|
||||||
|
* [x] **GAL-12: Levels/Stages**
|
||||||
|
* [x] GAL-13: Add `CurrentStage` and `StageConfigurations` resources.
|
||||||
|
* [x] GAL-14: Define criteria for clearing a stage.
|
||||||
|
* [x] GAL-15: Advance to the next stage with increasing difficulty.
|
||||||
|
|
||||||
|
**2. Enemy Behavior - Formations & Attack Patterns**
|
||||||
|
|
||||||
|
* [x] **GAL-16: Enemy Formations**
|
||||||
|
* [x] GAL-17: Define target positions for formations (`FormationLayout`).
|
||||||
|
* [x] GAL-18: Enemies have an `Entering` state to fly to their position.
|
||||||
|
* [x] GAL-19: Enemies have an `InFormation` state.
|
||||||
|
* [x] **GAL-20: Enemy Attack Dives**
|
||||||
|
* [x] GAL-21: Enemies have an `Attacking` state with different `AttackPattern`s (Swoop, Direct, Kamikaze).
|
||||||
|
* [x] GAL-22: Periodically trigger random enemies to start an attack dive.
|
||||||
|
* [x] GAL-23: Enemies fire bullets during their dives.
|
||||||
|
* [x] GAL-24: Enemies are despawned when they fly off-screen after an attack.
|
||||||
|
* [ ] **GAL-25: Enemy Variety**
|
||||||
|
* [x] GAL-26: `EnemyType` enum (`Grunt`, `Boss`).
|
||||||
|
* [ ] GAL-27: Different behaviors, points, and colors based on type.
|
||||||
|
* [x] Colors differ (Grunt red, Boss purple) — `enemy.rs:80-83`
|
||||||
|
* [x] Behaviors differ (Boss has `CaptureBeam`, distinct SwoopDive) — `enemy.rs:254-318`
|
||||||
|
* [ ] Points: Boss currently awards same 100 points as Grunt — see `bullet.rs:48-51` (`// Same points as Grunt for now`). Boss should award more.
|
||||||
|
|
||||||
|
**3. Advanced Galaga Mechanics**
|
||||||
|
|
||||||
|
* [ ] **GAL-28: Boss Galaga & Capture Beam**
|
||||||
|
* [x] GAL-29: Create a "Boss" enemy type.
|
||||||
|
* [x] GAL-30: Implement the tractor beam attack logic (`boss_capture_attack` system).
|
||||||
|
* [x] GAL-31: Player has a `Captured` component when hit by the beam.
|
||||||
|
* [x] GAL-32: Boss has a `ReturningWithCaptive` state to go back to the formation.
|
||||||
|
* [x] GAL-33: Improve the tractor beam visual effect (currently a simple rectangle).
|
||||||
|
* Completed on branch gal-33-improve-tractor-beam-visual, commit 52b0919 — 2-layer glow beam with pulse animation, 8 unit tests
|
||||||
|
* [ ] **GAL-34: Dual Fighter (Rescuing Captured Ship)**
|
||||||
|
* [ ] GAL-35: Allow the player to shoot a Boss that is holding a captured ship.
|
||||||
|
* [ ] GAL-36: Implement the logic to free the captured ship upon shooting the Boss.
|
||||||
|
* [ ] GAL-37: Implement the dual fighter mode (controlling two ships, firing two bullets).
|
||||||
|
* [ ] **GAL-38: Challenging Stages**
|
||||||
|
* [ ] GAL-39: Implement a special stage type (e.g., every 3-4 levels).
|
||||||
|
* [ ] GAL-40: Design and implement intricate flight patterns for enemies that do not shoot.
|
||||||
|
* [ ] GAL-41: Award bonus points for destroying all enemies in the stage.
|
||||||
|
|
||||||
|
**4. Polish and User Interface**
|
||||||
|
|
||||||
|
* [ ] **GAL-42: Visuals**
|
||||||
|
* [ ] GAL-43: Replace placeholder geometric shapes with actual sprites.
|
||||||
|
* [x] GAL-44: Add explosion animations/effects.
|
||||||
|
* **Branch:** `gal-44-add-explosion-effects`
|
||||||
|
* **Comment:** 2026-05-06 — Implementation complete with commit `2ff561e`
|
||||||
|
* [x] GAL-45: Implement a scrolling starfield background.
|
||||||
|
* [ ] **GAL-46: Audio**
|
||||||
|
* [ ] GAL-47: Integrate `bevy_audio`.
|
||||||
|
* [ ] GAL-48: Add sound effects (shooting, explosions, player death, tractor beam, etc.).
|
||||||
|
* [ ] GAL-49: Add background music.
|
||||||
|
* [ ] **GAL-50: UI**
|
||||||
|
* [x] GAL-51: Display Score, Lives, and Stage in the window title.
|
||||||
|
* [ ] GAL-52: Display Score, Lives, and Stage on the screen using `bevy_ui`.
|
||||||
|
* [ ] GAL-53: Implement a High Score system (saving/loading).
|
||||||
|
* [x] GAL-54: Create a Start Menu state with a "Start Game" button.
|
||||||
|
* [ ] GAL-55: Add a "Press R to Restart" message to the `GameOver` screen and implement restart logic.
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-1
|
|
||||||
title: Player Lives
|
|
||||||
status: Done
|
|
||||||
parent: null
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-1: Player Lives
|
|
||||||
|
|
||||||
Player lives system: lives counter, decrement on hit, destruction and respawn flow.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-2](GAL-2.md) — Add a `PlayerLives` resource.
|
|
||||||
- [x] [GAL-3](GAL-3.md) — Decrement lives on collision.
|
|
||||||
- [x] [GAL-4](GAL-4.md) — Implement player destruction and respawn with temporary invincibility.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-10
|
|
||||||
title: Add a Score resource
|
|
||||||
status: Done
|
|
||||||
parent: GAL-9
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-10: Add a `Score` resource
|
|
||||||
|
|
||||||
Add a `Score` resource.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-11
|
|
||||||
title: Increment score when an enemy is hit
|
|
||||||
status: Done
|
|
||||||
parent: GAL-9
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-11: Increment score when an enemy is hit
|
|
||||||
|
|
||||||
Increment score when an enemy is hit.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-12
|
|
||||||
title: Levels/Stages
|
|
||||||
status: Done
|
|
||||||
parent: null
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-12: Levels/Stages
|
|
||||||
|
|
||||||
Stage progression with increasing difficulty driven by configuration resources.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-13](GAL-13.md) — Add `CurrentStage` and `StageConfigurations` resources.
|
|
||||||
- [x] [GAL-14](GAL-14.md) — Define criteria for clearing a stage.
|
|
||||||
- [x] [GAL-15](GAL-15.md) — Advance to the next stage with increasing difficulty.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-13
|
|
||||||
title: Add CurrentStage and StageConfigurations resources
|
|
||||||
status: Done
|
|
||||||
parent: GAL-12
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-13: Add `CurrentStage` and `StageConfigurations` resources
|
|
||||||
|
|
||||||
Add `CurrentStage` and `StageConfigurations` resources.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-14
|
|
||||||
title: Define criteria for clearing a stage
|
|
||||||
status: Done
|
|
||||||
parent: GAL-12
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-14: Define criteria for clearing a stage
|
|
||||||
|
|
||||||
Define criteria for clearing a stage.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-15
|
|
||||||
title: Advance to the next stage with increasing difficulty
|
|
||||||
status: Done
|
|
||||||
parent: GAL-12
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-15: Advance to the next stage with increasing difficulty
|
|
||||||
|
|
||||||
Advance to the next stage with increasing difficulty.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-16
|
|
||||||
title: Enemy Formations
|
|
||||||
status: Done
|
|
||||||
parent: null
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-16: Enemy Formations
|
|
||||||
|
|
||||||
Enemies fly into target positions and hold formation.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-17](GAL-17.md) — Define target positions for formations (`FormationLayout`).
|
|
||||||
- [x] [GAL-18](GAL-18.md) — Enemies have an `Entering` state to fly to their position.
|
|
||||||
- [x] [GAL-19](GAL-19.md) — Enemies have an `InFormation` state.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-17
|
|
||||||
title: Define target positions for formations (FormationLayout)
|
|
||||||
status: Done
|
|
||||||
parent: GAL-16
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-17: Define target positions for formations (`FormationLayout`)
|
|
||||||
|
|
||||||
Define target positions for formations (`FormationLayout`).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-18
|
|
||||||
title: Enemies have an Entering state to fly to their position
|
|
||||||
status: Done
|
|
||||||
parent: GAL-16
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-18: Enemies have an `Entering` state to fly to their position
|
|
||||||
|
|
||||||
Enemies have an `Entering` state to fly to their position.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-19
|
|
||||||
title: Enemies have an InFormation state
|
|
||||||
status: Done
|
|
||||||
parent: GAL-16
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-19: Enemies have an `InFormation` state
|
|
||||||
|
|
||||||
Enemies have an `InFormation` state.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-2
|
|
||||||
title: Add a PlayerLives resource
|
|
||||||
status: Done
|
|
||||||
parent: GAL-1
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-2: Add a `PlayerLives` resource
|
|
||||||
|
|
||||||
Add a `PlayerLives` resource.
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-20
|
|
||||||
title: Enemy Attack Dives
|
|
||||||
status: Done
|
|
||||||
parent: null
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-20: Enemy Attack Dives
|
|
||||||
|
|
||||||
Enemies leave formation to dive at the player along varied attack patterns.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-21](GAL-21.md) — Enemies have an `Attacking` state with different `AttackPattern`s (Swoop, Direct, Kamikaze).
|
|
||||||
- [x] [GAL-22](GAL-22.md) — Periodically trigger random enemies to start an attack dive.
|
|
||||||
- [x] [GAL-23](GAL-23.md) — Enemies fire bullets during their dives.
|
|
||||||
- [x] [GAL-24](GAL-24.md) — Enemies are despawned when they fly off-screen after an attack.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-21
|
|
||||||
title: Attacking state with different AttackPatterns
|
|
||||||
status: Done
|
|
||||||
parent: GAL-20
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-21: Enemies have an `Attacking` state with different `AttackPattern`s
|
|
||||||
|
|
||||||
Enemies have an `Attacking` state with different `AttackPattern`s (Swoop, Direct, Kamikaze).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-22
|
|
||||||
title: Periodically trigger random enemies to start an attack dive
|
|
||||||
status: Done
|
|
||||||
parent: GAL-20
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-22: Periodically trigger random enemies to start an attack dive
|
|
||||||
|
|
||||||
Periodically trigger random enemies to start an attack dive.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-23
|
|
||||||
title: Enemies fire bullets during their dives
|
|
||||||
status: Done
|
|
||||||
parent: GAL-20
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-23: Enemies fire bullets during their dives
|
|
||||||
|
|
||||||
Enemies fire bullets during their dives.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-24
|
|
||||||
title: Despawn enemies when they fly off-screen after an attack
|
|
||||||
status: Done
|
|
||||||
parent: GAL-20
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-24: Enemies are despawned when they fly off-screen after an attack
|
|
||||||
|
|
||||||
Enemies are despawned when they fly off-screen after an attack.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-25
|
|
||||||
title: Enemy Variety
|
|
||||||
status: In Progress
|
|
||||||
parent: null
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-25: Enemy Variety
|
|
||||||
|
|
||||||
Different enemy types with distinct visuals, behaviors, and point values.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-26](GAL-26.md) — `EnemyType` enum (`Grunt`, `Boss`).
|
|
||||||
- [x] [GAL-27](GAL-27.md) — Different behaviors, points, and colors based on type.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-26
|
|
||||||
title: EnemyType enum (Grunt, Boss)
|
|
||||||
status: Done
|
|
||||||
parent: GAL-25
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-26: `EnemyType` enum (`Grunt`, `Boss`)
|
|
||||||
|
|
||||||
`EnemyType` enum (`Grunt`, `Boss`).
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-27
|
|
||||||
title: Different behaviors, points, and colors based on type
|
|
||||||
status: Done
|
|
||||||
parent: GAL-25
|
|
||||||
labels: [enemy, gameplay]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-27: Different behaviors, points, and colors based on type
|
|
||||||
|
|
||||||
Bosses are visually, behaviorally, and economically distinct from Grunts.
|
|
||||||
|
|
||||||
## Acceptance criteria
|
|
||||||
|
|
||||||
- [x] Colors differ (Grunt red, Boss purple) — `enemy.rs:80-83`
|
|
||||||
- [x] Behaviors differ (Boss has `CaptureBeam`, distinct SwoopDive) — `enemy.rs:254-318`
|
|
||||||
- [x] Points: Boss awards 300 points (3x Grunt) — `bullet.rs:13-14`
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
- Completed on branch GAL-27, commit 98b74a7.
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-28
|
|
||||||
title: Boss Galaga & Capture Beam
|
|
||||||
status: In Progress
|
|
||||||
parent: null
|
|
||||||
labels: [enemy, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-28: Boss Galaga & Capture Beam
|
|
||||||
|
|
||||||
Boss enemy with tractor beam capture mechanic and return-to-formation behavior.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-29](GAL-29.md) — Create a "Boss" enemy type.
|
|
||||||
- [x] [GAL-30](GAL-30.md) — Implement the tractor beam attack logic (`boss_capture_attack` system).
|
|
||||||
- [x] [GAL-31](GAL-31.md) — Player has a `Captured` component when hit by the beam.
|
|
||||||
- [x] [GAL-32](GAL-32.md) — Boss has a `ReturningWithCaptive` state to go back to the formation.
|
|
||||||
- [x] [GAL-33](GAL-33.md) — Improve the tractor beam visual effect (currently a simple rectangle).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-29
|
|
||||||
title: Create a Boss enemy type
|
|
||||||
status: Done
|
|
||||||
parent: GAL-28
|
|
||||||
labels: [enemy, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-29: Create a "Boss" enemy type
|
|
||||||
|
|
||||||
Create a "Boss" enemy type.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-3
|
|
||||||
title: Decrement lives on collision
|
|
||||||
status: Done
|
|
||||||
parent: GAL-1
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-3: Decrement lives on collision
|
|
||||||
|
|
||||||
Decrement lives on collision.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-30
|
|
||||||
title: Implement the tractor beam attack logic
|
|
||||||
status: Done
|
|
||||||
parent: GAL-28
|
|
||||||
labels: [enemy, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-30: Implement the tractor beam attack logic (`boss_capture_attack` system)
|
|
||||||
|
|
||||||
Implement the tractor beam attack logic (`boss_capture_attack` system).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-31
|
|
||||||
title: Player has a Captured component when hit by the beam
|
|
||||||
status: Done
|
|
||||||
parent: GAL-28
|
|
||||||
labels: [enemy, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-31: Player has a `Captured` component when hit by the beam
|
|
||||||
|
|
||||||
Player has a `Captured` component when hit by the beam.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-32
|
|
||||||
title: Boss has a ReturningWithCaptive state
|
|
||||||
status: Done
|
|
||||||
parent: GAL-28
|
|
||||||
labels: [enemy, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-32: Boss has a `ReturningWithCaptive` state to go back to the formation
|
|
||||||
|
|
||||||
Boss has a `ReturningWithCaptive` state to go back to the formation.
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-33
|
|
||||||
title: Improve the tractor beam visual effect
|
|
||||||
status: Done
|
|
||||||
parent: GAL-28
|
|
||||||
labels: [enemy, advanced-mechanics, visuals]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-33: Improve the tractor beam visual effect
|
|
||||||
|
|
||||||
Improve the tractor beam visual effect (currently a simple rectangle).
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
- Completed on branch `gal-33-improve-tractor-beam-visual`, commit 52b0919 — 2-layer glow beam with pulse animation, 8 unit tests.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-34
|
|
||||||
title: Dual Fighter (Rescuing Captured Ship)
|
|
||||||
status: Todo
|
|
||||||
parent: null
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-34: Dual Fighter (Rescuing Captured Ship)
|
|
||||||
|
|
||||||
Allow the player to rescue a captured ship and operate in dual-fighter mode.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [ ] [GAL-35](GAL-35.md) — Allow the player to shoot a Boss that is holding a captured ship.
|
|
||||||
- [ ] [GAL-36](GAL-36.md) — Implement the logic to free the captured ship upon shooting the Boss.
|
|
||||||
- [ ] [GAL-37](GAL-37.md) — Implement the dual fighter mode (controlling two ships, firing two bullets).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-35
|
|
||||||
title: Allow the player to shoot a Boss holding a captured ship
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-34
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-35: Allow the player to shoot a Boss that is holding a captured ship
|
|
||||||
|
|
||||||
Allow the player to shoot a Boss that is holding a captured ship.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-36
|
|
||||||
title: Free the captured ship upon shooting the Boss
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-34
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-36: Implement the logic to free the captured ship upon shooting the Boss
|
|
||||||
|
|
||||||
Implement the logic to free the captured ship upon shooting the Boss.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-37
|
|
||||||
title: Implement the dual fighter mode
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-34
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-37: Implement the dual fighter mode (controlling two ships, firing two bullets)
|
|
||||||
|
|
||||||
Implement the dual fighter mode (controlling two ships, firing two bullets).
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-38
|
|
||||||
title: Challenging Stages
|
|
||||||
status: In Progress
|
|
||||||
parent: null
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-38: Challenging Stages
|
|
||||||
|
|
||||||
Special stages every few levels with intricate non-shooting flight patterns and bonus rewards.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-39](GAL-39.md) — Implement a special stage type (e.g., every 3-4 levels).
|
|
||||||
- [ ] [GAL-40](GAL-40.md) — Design and implement intricate flight patterns for enemies that do not shoot.
|
|
||||||
- [ ] [GAL-41](GAL-41.md) — Award bonus points for destroying all enemies in the stage.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-39
|
|
||||||
title: Implement a special stage type
|
|
||||||
status: Done
|
|
||||||
parent: GAL-38
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-39: Implement a special stage type (e.g., every 3-4 levels)
|
|
||||||
|
|
||||||
Implement a special stage type (e.g., every 3-4 levels).
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
- 2026-05-06 — Status set to In Progress.
|
|
||||||
- 2026-05-06 — Branch `GAL-39`, commit 9e6d538 — every 3rd level has no enemy attacks.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-4
|
|
||||||
title: Player destruction and respawn with temporary invincibility
|
|
||||||
status: Done
|
|
||||||
parent: GAL-1
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-4: Implement player destruction and respawn with temporary invincibility
|
|
||||||
|
|
||||||
Implement player destruction and respawn with temporary invincibility.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-40
|
|
||||||
title: Design intricate flight patterns for non-shooting enemies
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-38
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-40: Design and implement intricate flight patterns for enemies that do not shoot
|
|
||||||
|
|
||||||
Design and implement intricate flight patterns for enemies that do not shoot.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-41
|
|
||||||
title: Award bonus points for clearing the special stage
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-38
|
|
||||||
labels: [gameplay, advanced-mechanics]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-41: Award bonus points for destroying all enemies in the stage
|
|
||||||
|
|
||||||
Award bonus points for destroying all enemies in the stage.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-42
|
|
||||||
title: Visuals
|
|
||||||
status: In Progress
|
|
||||||
parent: null
|
|
||||||
labels: [polish, visuals]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-42: Visuals
|
|
||||||
|
|
||||||
Replace placeholder geometry with sprites, add explosions, and add a starfield background.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [ ] [GAL-43](GAL-43.md) — Replace placeholder geometric shapes with actual sprites.
|
|
||||||
- [x] [GAL-44](GAL-44.md) — Add explosion animations/effects.
|
|
||||||
- [x] [GAL-45](GAL-45.md) — Implement a scrolling starfield background.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-43
|
|
||||||
title: Replace placeholder geometric shapes with actual sprites
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-42
|
|
||||||
labels: [polish, visuals]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-43: Replace placeholder geometric shapes with actual sprites
|
|
||||||
|
|
||||||
Replace placeholder geometric shapes with actual sprites.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-44
|
|
||||||
title: Add explosion animations/effects
|
|
||||||
status: Done
|
|
||||||
parent: GAL-42
|
|
||||||
labels: [polish, visuals]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-44: Add explosion animations/effects
|
|
||||||
|
|
||||||
Add explosion animations/effects.
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
- Branch: `gal-44-add-explosion-effects`.
|
|
||||||
- 2026-05-06 — Implementation complete with commit `2ff561e`.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-45
|
|
||||||
title: Implement a scrolling starfield background
|
|
||||||
status: Done
|
|
||||||
parent: GAL-42
|
|
||||||
labels: [polish, visuals]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-45: Implement a scrolling starfield background
|
|
||||||
|
|
||||||
Implement a scrolling starfield background.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-46
|
|
||||||
title: Audio
|
|
||||||
status: Todo
|
|
||||||
parent: null
|
|
||||||
labels: [polish, audio]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-46: Audio
|
|
||||||
|
|
||||||
Wire up audio plugin, sound effects, and background music.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [ ] [GAL-47](GAL-47.md) — Integrate `bevy_audio`.
|
|
||||||
- [ ] [GAL-48](GAL-48.md) — Add sound effects (shooting, explosions, player death, tractor beam, etc.).
|
|
||||||
- [ ] [GAL-49](GAL-49.md) — Add background music.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-47
|
|
||||||
title: Integrate bevy_audio
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-46
|
|
||||||
labels: [polish, audio]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-47: Integrate `bevy_audio`
|
|
||||||
|
|
||||||
Integrate `bevy_audio`.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-48
|
|
||||||
title: Add sound effects
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-46
|
|
||||||
labels: [polish, audio]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-48: Add sound effects (shooting, explosions, player death, tractor beam, etc.)
|
|
||||||
|
|
||||||
Add sound effects (shooting, explosions, player death, tractor beam, etc.).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-49
|
|
||||||
title: Add background music
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-46
|
|
||||||
labels: [polish, audio]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-49: Add background music
|
|
||||||
|
|
||||||
Add background music.
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-5
|
|
||||||
title: Game Over State
|
|
||||||
status: Done
|
|
||||||
parent: null
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-5: Game Over State
|
|
||||||
|
|
||||||
State machine covering `Playing` and `GameOver`, with a transition trigger and on-screen message.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-6](GAL-6.md) — Use Bevy's `States` (`Playing`, `GameOver`).
|
|
||||||
- [x] [GAL-7](GAL-7.md) — Transition to `GameOver` when lives reach zero.
|
|
||||||
- [x] [GAL-8](GAL-8.md) — Display a "Game Over" message.
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-50
|
|
||||||
title: UI
|
|
||||||
status: In Progress
|
|
||||||
parent: null
|
|
||||||
labels: [polish, ui]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-50: UI
|
|
||||||
|
|
||||||
Score, lives, stage, high-score, start menu, and restart UI.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-51](GAL-51.md) — Display Score, Lives, and Stage in the window title.
|
|
||||||
- [ ] [GAL-52](GAL-52.md) — Display Score, Lives, and Stage on the screen using `bevy_ui`.
|
|
||||||
- [ ] [GAL-53](GAL-53.md) — Implement a High Score system (saving/loading).
|
|
||||||
- [x] [GAL-54](GAL-54.md) — Create a Start Menu state with a "Start Game" button.
|
|
||||||
- [ ] [GAL-55](GAL-55.md) — Add a "Press R to Restart" message to the `GameOver` screen and implement restart logic.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-51
|
|
||||||
title: Display Score, Lives, and Stage in the window title
|
|
||||||
status: Done
|
|
||||||
parent: GAL-50
|
|
||||||
labels: [polish, ui]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-51: Display Score, Lives, and Stage in the window title
|
|
||||||
|
|
||||||
Display Score, Lives, and Stage in the window title.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-52
|
|
||||||
title: Display Score, Lives, and Stage on screen using bevy_ui
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-50
|
|
||||||
labels: [polish, ui]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-52: Display Score, Lives, and Stage on the screen using `bevy_ui`
|
|
||||||
|
|
||||||
Display Score, Lives, and Stage on the screen using `bevy_ui`.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-53
|
|
||||||
title: Implement a High Score system (saving/loading)
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-50
|
|
||||||
labels: [polish, ui]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-53: Implement a High Score system (saving/loading)
|
|
||||||
|
|
||||||
Implement a High Score system (saving/loading).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-54
|
|
||||||
title: Create a Start Menu state with a Start Game button
|
|
||||||
status: Done
|
|
||||||
parent: GAL-50
|
|
||||||
labels: [polish, ui]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-54: Create a Start Menu state with a "Start Game" button
|
|
||||||
|
|
||||||
Create a Start Menu state with a "Start Game" button.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-55
|
|
||||||
title: Add Press R to Restart message and restart logic
|
|
||||||
status: Todo
|
|
||||||
parent: GAL-50
|
|
||||||
labels: [polish, ui]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-55: Add a "Press R to Restart" message to the `GameOver` screen and implement restart logic
|
|
||||||
|
|
||||||
Add a "Press R to Restart" message to the `GameOver` screen and implement restart logic.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-6
|
|
||||||
title: Use Bevy's States (Playing, GameOver)
|
|
||||||
status: Done
|
|
||||||
parent: GAL-5
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-6: Use Bevy's `States` (`Playing`, `GameOver`)
|
|
||||||
|
|
||||||
Use Bevy's `States` (`Playing`, `GameOver`).
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-7
|
|
||||||
title: Transition to GameOver when lives reach zero
|
|
||||||
status: Done
|
|
||||||
parent: GAL-5
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-7: Transition to `GameOver` when lives reach zero
|
|
||||||
|
|
||||||
Transition to `GameOver` when lives reach zero.
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-8
|
|
||||||
title: Display a Game Over message
|
|
||||||
status: Done
|
|
||||||
parent: GAL-5
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-8: Display a "Game Over" message
|
|
||||||
|
|
||||||
Display a "Game Over" message.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
id: GAL-9
|
|
||||||
title: Scoring
|
|
||||||
status: Done
|
|
||||||
parent: null
|
|
||||||
labels: [gameplay, core-loop]
|
|
||||||
---
|
|
||||||
|
|
||||||
# GAL-9: Scoring
|
|
||||||
|
|
||||||
Add a score resource and increment when an enemy is destroyed.
|
|
||||||
|
|
||||||
## Sub-issues
|
|
||||||
|
|
||||||
- [x] [GAL-10](GAL-10.md) — Add a `Score` resource.
|
|
||||||
- [x] [GAL-11](GAL-11.md) — Increment score when an enemy is hit.
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# Project Issues
|
|
||||||
|
|
||||||
Linear-style issue tracker for bglga. Each issue lives in its own `GAL-N.md` file in this folder.
|
|
||||||
|
|
||||||
Statuses: `Todo`, `In Progress`, `Done`.
|
|
||||||
|
|
||||||
## 1. Core Gameplay Loop & State Management
|
|
||||||
|
|
||||||
- [x] [GAL-1](GAL-1.md) — Player Lives
|
|
||||||
- [x] [GAL-5](GAL-5.md) — Game Over State
|
|
||||||
- [x] [GAL-9](GAL-9.md) — Scoring
|
|
||||||
- [x] [GAL-12](GAL-12.md) — Levels/Stages
|
|
||||||
|
|
||||||
## 2. Enemy Behavior — Formations & Attack Patterns
|
|
||||||
|
|
||||||
- [x] [GAL-16](GAL-16.md) — Enemy Formations
|
|
||||||
- [x] [GAL-20](GAL-20.md) — Enemy Attack Dives
|
|
||||||
- [ ] [GAL-25](GAL-25.md) — Enemy Variety
|
|
||||||
|
|
||||||
## 3. Advanced Galaga Mechanics
|
|
||||||
|
|
||||||
- [ ] [GAL-28](GAL-28.md) — Boss Galaga & Capture Beam
|
|
||||||
- [ ] [GAL-34](GAL-34.md) — Dual Fighter (Rescuing Captured Ship)
|
|
||||||
- [ ] [GAL-38](GAL-38.md) — Challenging Stages
|
|
||||||
|
|
||||||
## 4. Polish and User Interface
|
|
||||||
|
|
||||||
- [ ] [GAL-42](GAL-42.md) — Visuals
|
|
||||||
- [ ] [GAL-46](GAL-46.md) — Audio
|
|
||||||
- [ ] [GAL-50](GAL-50.md) — UI
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::resources::{PlayerLives, PlayerRespawnTimer, Score};
|
||||||
use crate::systems::spawn_explosion;
|
use crate::systems::spawn_explosion;
|
||||||
|
|
||||||
const GRUNT_POINTS: u32 = 100;
|
const GRUNT_POINTS: u32 = 100;
|
||||||
const BOSS_POINTS: u32 = 300;
|
const BOSS_POINTS: u32 = 100; // TODO(GAL-27): differentiate Boss from Grunt scoring
|
||||||
|
|
||||||
pub fn move_bullets(
|
pub fn move_bullets(
|
||||||
mut query: Query<(Entity, &mut Transform), With<Bullet>>,
|
mut query: Query<(Entity, &mut Transform), With<Bullet>>,
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,6 @@ pub const BEAM_CORE_COLOR: Color = Color::srgba(0.7, 0.2, 1.0, 0.7);
|
||||||
pub const BEAM_PULSE_FREQ: f32 = 3.0;
|
pub const BEAM_PULSE_FREQ: f32 = 3.0;
|
||||||
pub const BEAM_PULSE_AMPLITUDE: f32 = 0.15;
|
pub const BEAM_PULSE_AMPLITUDE: f32 = 0.15;
|
||||||
|
|
||||||
// Special stages
|
|
||||||
pub const SPECIAL_STAGE_INTERVAL: u32 = 3;
|
|
||||||
|
|
||||||
// Starfield
|
// Starfield
|
||||||
pub const STAR_COUNT: usize = 150;
|
pub const STAR_COUNT: usize = 150;
|
||||||
pub const STAR_MIN_SIZE: f32 = 1.0;
|
pub const STAR_MIN_SIZE: f32 = 1.0;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ use crate::constants::{
|
||||||
};
|
};
|
||||||
use crate::resources::{
|
use crate::resources::{
|
||||||
AttackDiveTimer, CurrentStage, EnemySpawnTimer, FormationState, StageConfigurations,
|
AttackDiveTimer, CurrentStage, EnemySpawnTimer, FormationState, StageConfigurations,
|
||||||
is_special_stage,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const BOSS_BASE_CHANCE: f32 = 0.30;
|
const BOSS_BASE_CHANCE: f32 = 0.30;
|
||||||
|
|
@ -342,11 +341,7 @@ pub fn enemy_shoot(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut enemy_query: Query<(&Transform, &mut Enemy, &EnemyState), Without<FormationTarget>>,
|
mut enemy_query: Query<(&Transform, &mut Enemy, &EnemyState), Without<FormationTarget>>,
|
||||||
stage: Res<CurrentStage>,
|
|
||||||
) {
|
) {
|
||||||
if is_special_stage(stage.number) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (transform, mut enemy, state) in enemy_query.iter_mut() {
|
for (transform, mut enemy, state) in enemy_query.iter_mut() {
|
||||||
if !matches!(state, EnemyState::Attacking(_)) {
|
if !matches!(state, EnemyState::Attacking(_)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,9 @@ use bevy::prelude::*;
|
||||||
use crate::components::AttackPattern;
|
use crate::components::AttackPattern;
|
||||||
use crate::constants::{
|
use crate::constants::{
|
||||||
ENEMY_SHOOT_INTERVAL, FORMATION_BASE_Y, FORMATION_COLS, FORMATION_ENEMY_COUNT,
|
ENEMY_SHOOT_INTERVAL, FORMATION_BASE_Y, FORMATION_COLS, FORMATION_ENEMY_COUNT,
|
||||||
FORMATION_X_SPACING, FORMATION_Y_SPACING, SPECIAL_STAGE_INTERVAL, WINDOW_WIDTH,
|
FORMATION_X_SPACING, FORMATION_Y_SPACING, WINDOW_WIDTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns `true` when `stage_number` is a special stage (every 3rd stage).
|
|
||||||
pub fn is_special_stage(stage_number: u32) -> bool {
|
|
||||||
stage_number > 0 && stage_number.is_multiple_of(SPECIAL_STAGE_INTERVAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct EnemySpawnTimer {
|
pub struct EnemySpawnTimer {
|
||||||
pub timer: Timer,
|
pub timer: Timer,
|
||||||
|
|
@ -63,21 +58,13 @@ pub struct StageConfig {
|
||||||
#[derive(Resource, Debug, Clone)]
|
#[derive(Resource, Debug, Clone)]
|
||||||
pub struct StageConfigurations {
|
pub struct StageConfigurations {
|
||||||
pub stages: Vec<StageConfig>,
|
pub stages: Vec<StageConfig>,
|
||||||
pub special_stage: StageConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StageConfigurations {
|
impl StageConfigurations {
|
||||||
/// Returns the config for the given stage, using the special stage config
|
/// Cycles through configured stages once `stage_number` exceeds the count.
|
||||||
/// when `stage_number` is a multiple of `SPECIAL_STAGE_INTERVAL`.
|
|
||||||
pub fn for_stage(&self, stage_number: u32) -> &StageConfig {
|
pub fn for_stage(&self, stage_number: u32) -> &StageConfig {
|
||||||
if is_special_stage(stage_number) {
|
let idx = (stage_number.saturating_sub(1) as usize) % self.stages.len();
|
||||||
return &self.special_stage;
|
&self.stages[idx]
|
||||||
}
|
|
||||||
// Count how many special stages come before this one
|
|
||||||
let special_before = stage_number / SPECIAL_STAGE_INTERVAL;
|
|
||||||
let normal_idx =
|
|
||||||
(stage_number.saturating_sub(1) - special_before) as usize % self.stages.len();
|
|
||||||
&self.stages[normal_idx]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,18 +89,8 @@ impl Default for StageConfigurations {
|
||||||
enemy_shoot_interval: ENEMY_SHOOT_INTERVAL * 0.8,
|
enemy_shoot_interval: ENEMY_SHOOT_INTERVAL * 0.8,
|
||||||
};
|
};
|
||||||
|
|
||||||
let special_stage = StageConfig {
|
|
||||||
formation_layout: FormationLayout::default(),
|
|
||||||
enemy_count: FORMATION_ENEMY_COUNT,
|
|
||||||
attack_patterns: vec![],
|
|
||||||
attack_dive_interval: f32::MAX,
|
|
||||||
enemy_speed_multiplier: 1.0,
|
|
||||||
enemy_shoot_interval: ENEMY_SHOOT_INTERVAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
stages: vec![stage1, stage2],
|
stages: vec![stage1, stage2],
|
||||||
special_stage,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
||||||
use crate::components::{Explosion, Invincible, Player};
|
use crate::components::{Explosion, Invincible, Player};
|
||||||
use crate::constants::{EXPLOSION_BASE_SIZE, EXPLOSION_COLOR, EXPLOSION_DURATION, EXPLOSION_MAX_SIZE};
|
use crate::constants::{EXPLOSION_BASE_SIZE, EXPLOSION_COLOR, EXPLOSION_DURATION, EXPLOSION_MAX_SIZE};
|
||||||
use crate::player::spawn_player_ship;
|
use crate::player::spawn_player_ship;
|
||||||
use crate::resources::{CurrentStage, PlayerLives, Score, is_special_stage};
|
use crate::resources::{CurrentStage, PlayerLives, Score};
|
||||||
use crate::starfield::spawn_starfield;
|
use crate::starfield::spawn_starfield;
|
||||||
|
|
||||||
pub fn setup(mut commands: Commands) {
|
pub fn setup(mut commands: Commands) {
|
||||||
|
|
@ -38,15 +38,10 @@ pub fn update_window_title(
|
||||||
if !(lives.is_changed() || score.is_changed() || stage.is_changed()) {
|
if !(lives.is_changed() || score.is_changed() || stage.is_changed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let stage_label = if is_special_stage(stage.number) {
|
|
||||||
format!("{}*", stage.number)
|
|
||||||
} else {
|
|
||||||
stage.number.to_string()
|
|
||||||
};
|
|
||||||
if let Ok(mut window) = windows.single_mut() {
|
if let Ok(mut window) = windows.single_mut() {
|
||||||
window.title = format!(
|
window.title = format!(
|
||||||
"Galaga :: Stage: {} Lives: {} Score: {}",
|
"Galaga :: Stage: {} Lives: {} Score: {}",
|
||||||
stage_label, lives.count, score.value
|
stage.number, lives.count, score.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
use bglga::constants::SPECIAL_STAGE_INTERVAL;
|
|
||||||
use bglga::resources::is_special_stage;
|
|
||||||
use bglga::resources::StageConfigurations;
|
|
||||||
|
|
||||||
// ── SPECIAL_STAGE_INTERVAL constant ──
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn special_stage_interval_is_three() {
|
|
||||||
assert_eq!(SPECIAL_STAGE_INTERVAL, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── is_special_stage function ──
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_zero_is_not_special() {
|
|
||||||
assert!(!is_special_stage(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_1_is_not_special() {
|
|
||||||
assert!(!is_special_stage(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_2_is_not_special() {
|
|
||||||
assert!(!is_special_stage(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_3_is_special() {
|
|
||||||
assert!(is_special_stage(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_4_is_not_special() {
|
|
||||||
assert!(!is_special_stage(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_5_is_not_special() {
|
|
||||||
assert!(!is_special_stage(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_6_is_special() {
|
|
||||||
assert!(is_special_stage(6));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stage_9_is_special() {
|
|
||||||
assert!(is_special_stage(9));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── for_stage returns special config for special stages ──
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_stage_returns_special_config_at_stage_3() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(3);
|
|
||||||
// Special stages have no dive attacks
|
|
||||||
assert!(
|
|
||||||
config.attack_patterns.is_empty(),
|
|
||||||
"Stage 3 (special) should have empty attack_patterns"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_stage_returns_special_config_at_stage_6() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(6);
|
|
||||||
assert!(
|
|
||||||
config.attack_patterns.is_empty(),
|
|
||||||
"Stage 6 (special) should have empty attack_patterns"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_stage_returns_normal_config_at_stage_1() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(1);
|
|
||||||
assert!(
|
|
||||||
!config.attack_patterns.is_empty(),
|
|
||||||
"Stage 1 (normal) should have attack patterns"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_stage_returns_normal_config_at_stage_2() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(2);
|
|
||||||
assert!(
|
|
||||||
!config.attack_patterns.is_empty(),
|
|
||||||
"Stage 2 (normal) should have attack patterns"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_stage_returns_normal_config_at_stage_4() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(4);
|
|
||||||
assert!(
|
|
||||||
!config.attack_patterns.is_empty(),
|
|
||||||
"Stage 4 (normal) should have attack patterns"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Special stage config properties ──
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn special_stage_has_full_formation() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(3);
|
|
||||||
assert_eq!(config.enemy_count, 32, "Special stage should have 32 enemies");
|
|
||||||
assert_eq!(
|
|
||||||
config.formation_layout.positions.len(),
|
|
||||||
32,
|
|
||||||
"Special stage formation should have 32 positions"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn special_stage_has_max_dive_interval() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(3);
|
|
||||||
assert_eq!(
|
|
||||||
config.attack_dive_interval,
|
|
||||||
f32::MAX,
|
|
||||||
"Special stage should have max dive interval"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn special_stage_has_base_speed() {
|
|
||||||
let configs = StageConfigurations::default();
|
|
||||||
let config = configs.for_stage(3);
|
|
||||||
assert_eq!(
|
|
||||||
config.enemy_speed_multiplier, 1.0,
|
|
||||||
"Special stage should have 1.0x speed multiplier"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue