refactor: remove blinking expiration animations from rotten and cursed apples

This commit is contained in:
phillip committed 2026-10-03 22:26:46 +02:00
1 parent adda5db2f4
commit 8559159fc0
4 files changed
+7 -17

No files matched your search

+4 -2
View File
@@ -95,9 +95,10 @@ 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.
- **Immediate Head Rotation Feedback:** When the player presses a D-Pad direction while moving, `handle_input_frame()` validates that the input is not a reverse into the neck. If valid, `state.snakeDirection` is immediately updated and `draw_game()` is called on the main thread before the next movement tick. In `draw_snake_head()`, the head sprite tile pointer (`gfx_head_v` vs `gfx_head_h`) and hardware flip flags (`hflip`, `vflip`) are selected directly from `state.snakeDirection` and written to shadow OAM (`oamSub`). On the subsequent 60 FPS VBlank (~16.6 ms), `handle_vblank()` commits shadow OAM to hardware registers via `oamUpdate(&oamSub)`, providing instantaneous visual feedback that the snake has turned to face the new heading before the slower Timer 0 tick (5.0–17.0 Hz) advances grid coordinates via `update_snake()`.
- **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. 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.
- **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).
- **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), 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
@@ -193,6 +194,7 @@ The repository includes preconfigured tasks in [`.vscode/tasks.json`](.vscode/ta
- [`gfx/tilemap.png`](gfx/tilemap.png) + [`gfx/tilemap.grit`](gfx/tilemap.grit) are processed by `grit` into `${CMAKE_CURRENT_BINARY_DIR}/tilemap.s` and `tilemap.h`.
- Sprites are referenced by index: tile 1 (segment), 2 (small segment), 3 (head horizontal), 4 (head vertical), 5/6 (apple flash frames), 7/8/9/10 (tail variations), 11 (rotten apple). All 12 sprite tiles are preloaded once into VRAM at startup; directional and blinking variants are rendered via pointer selection and hardware flipping flags without mid-frame DMA transfers.
- Active OAM sprites are clamped at hardware limit (128) and trailing unused sprites are hidden every frame via `oamClear(&oamSub, oamId, 128 - oamId)`.
- **Decoupled Steering & Render Flow:** Steering inputs update `state.snakeDirection` and invoke `draw_game()` immediately on the main thread, updating the snake head's sprite pointer and flipping flags in shadow OAM (`oamSub`). Display updates are synchronized via VBlank (`handle_vblank()`), which flushes `oamSub` to hardware registers with `oamUpdate(&oamSub)` at 60 FPS (~16.6 ms) independently of the slower Timer 0 movement ticks (5.0–17.0 Hz).
- Console text HUD rendering in `draw_stats()` is throttled via dirty-flag caching (`HudCache`), eliminating redundant `iprintf` calls on VBlank.
- **Audio Pipeline:**
- Audio files in [`sounds/`](sounds/) are compiled by `mmutil` into `soundbank.bin` and header `soundbank.h`.