feat: add finite lifetimes and despawn mechanics for rotten and cursed apples
This commit is contained in:
1 parent
9a3a0bd9fd
commit
adda5db2f4
8 files changed
+159
-82
No files matched your search
@@ -96,8 +96,8 @@ The game operates in one of two scenes ([`Scene`](include/game_state.h#L9-L12)):
|
||||
### 3.3 Game Elements & Apples
|
||||
- **Snake Body:** Represented as `std::deque<std::array<int, 3>>` where each entry is `{x, y, direction}`. Alternating segments use regular and small sprite tiles. Head and tail sprites flip dynamically according to direction.
|
||||
- **Normal Apple (Red):** Spawns if count < 1. Awards +10 score, increases length by 1, plays pickup sound. Flashes/blinks every 4 ticks.
|
||||
- **Rotten Apple (Brown/Green):** Spawns in Hard Mode (periodic chance up to 20 on screen). Decreases length by 1, plays explosion SFX. If length drops below 2, triggers Game Over.
|
||||
- **Cursed Apple (Electric Neon Purple & Radioactive Green):** Spawns occasionally. Inverts player steering controls (Up ↔ Down, Left ↔ Right) for `CURSE_DURATION_TICKS` (40 ticks). Rendered using a dedicated 8x8 sprite with a glowing core rune and toxic green horn, using high-contrast dedicated palette entries (`CURSE_PAL_OUTLINE`, `CURSE_PAL_FILL`, `CURSE_PAL_GLOW`, `CURSE_PAL_CORE`, `CURSE_PAL_LEAF`). Its palette dynamically breathes and pulses with radioactive energy every frame at 60 FPS in [`handle_vblank()`](source/main.cpp#L134-L153), and its sprite sways/twitches with mystical instability in [`draw_apples_cursed()`](source/game_render.cpp#L139-L151).
|
||||
- **Rotten Apple (Brown/Green):** Spawns in Hard Mode (periodic chance up to 20 on screen). Decreases length by 1, plays explosion SFX. If length drops below 2, triggers Game Over. Despawns after `BAD_APPLE_LIFETIME_TICKS` (80 ticks); flashes during its last `EVIL_ITEM_BLINK_TICKS` (20 ticks, accelerating to rapid strobe during the final 8 ticks) to signal expiration.
|
||||
- **Cursed Apple (Electric Neon Purple & Radioactive Green):** Spawns occasionally. Inverts player steering controls (Up ↔ Down, Left ↔ Right) for `CURSE_DURATION_TICKS` (40 ticks). Rendered using a dedicated 8x8 sprite with a glowing core rune and toxic green horn, using high-contrast dedicated palette entries (`CURSE_PAL_OUTLINE`, `CURSE_PAL_FILL`, `CURSE_PAL_GLOW`, `CURSE_PAL_CORE`, `CURSE_PAL_LEAF`). Its palette dynamically breathes and pulses with radioactive energy every frame at 60 FPS in [`handle_vblank()`](source/main.cpp#L134-L153), and its sprite sways/twitches with mystical instability in [`draw_apples_cursed()`](source/game_render.cpp#L139-L151). Despawns after `CURSED_APPLE_LIFETIME_TICKS` (90 ticks) with the same warning blink, clearing the way for new cursed apples to spawn if previously avoided.
|
||||
- **Reverse-Direction Denial:** Pressing the D-Pad in the exact opposite direction (i.e. into the snake's own neck) is silently denied instead of causing an immediate Game Over. The game computes the would-be next head position (including edge-wrapping) and compares it to the neck segment; if they match, the direction change is rejected and a subtle "denied" thud SFX plays.
|
||||
|
||||
### 3.4 Game Modes & Progressive Speed Mechanics
|
||||
@@ -226,6 +226,10 @@ The repository includes preconfigured tasks in [`.vscode/tasks.json`](.vscode/ta
|
||||
- Be mindful of OAM hardware limitations (Sub engine supports up to 128 sprites; current game allocates sprite slots per apple and snake segment).
|
||||
2. **Interrupt & Concurrency Safety:**
|
||||
- Variables accessed across both the main loop, timer ticks, and VBlank IRQ (such as `ticks`, `frame`, `snakelength`, `snakeDirection`, `internals`) must maintain appropriate `volatile` qualifiers or thread-safe synchronization.
|
||||
- **IRQ Stack Limit (256 bytes):** In `calico/lib/ds9.ld`, the ARM9 IRQ stack (`sp_irq`) has a total budget of only 256 bytes (`__sp_irq = __sp_usr + 0x100`), growing downwards directly into the user stack area. Therefore:
|
||||
- **Zero dynamic allocations in ISRs:** Never perform dynamic heap allocation (`malloc`, `operator new`, vector reallocation) inside hardware interrupt handlers (`do_tick`, `handle_vblank`). Dynamic vectors like `bad_apples`, `cursed_apples`, and `apples` must pre-reserve capacity upfront on the main thread in `setup()` / `init_game()`.
|
||||
- **Context objects in `.bss`:** Large structs such as `InputContext` must be allocated in `.bss` (global or static) rather than on `main()`'s DTCM stack frame to avoid stack-smash corruption if an interrupt handler pushes registers.
|
||||
- **Safe state re-initialization:** In `init_game()`, call `timerStop(0)` before resetting game state or clearing containers, and only call `timerStart(0, ...)` at the very end when all state members and HUD caches are cleanly initialized.
|
||||
3. **Hardware Registers & Memory Banking:**
|
||||
- Graphic tiles and palettes must be transferred using DMA (`dmaCopy`) or direct memory copies into allocated VRAM / OAM memory banks.
|
||||
- Do not modify VRAM allocation configurations without ensuring compatibility with console text generators and OAM sub-engines.
|
||||
|
||||
Reference in new issue
Block a user