- Make handle_vblank strictly increment frame counter (frame++) - Move heavy frame logic (background updates, OAM updates, palette animations, and console stats) to update_frame() on the main thread synchronized with swiWaitForVBlank() - Eliminate 256-byte ARM9 IRQ stack (sp_irq) overflow caused by bg_update and iprintf that was corrupting DTCM stack and causing data aborts - Preserve all playfield entities on game over with top-screen defeat summary and smooth background deceleration Co-authored-by: Antigravity <antigravity@google.com>
137 lines
4.8 KiB
Markdown
137 lines
4.8 KiB
Markdown
# Snake Plus (Nintendo DS)
|
||
|
||
> **Snake with a twist** — A homebrew Snake game for the Nintendo DS developed in modern C++ with `libnds` and `devkitPro`.
|
||
|
||
---
|
||
|
||
## Features
|
||
|
||
- **Classic Gameplay with a Twist:**
|
||
- Classic grid-based snake action wrapped on a 32×24 playfield with edge-wrapping.
|
||
- **Normal Apples:** Boost length and score (+10), with flashing animation and pickup sounds.
|
||
- **Rotten Apples:** Shrink snake length by 1; shrinking below length 2 results in Game Over! Rendered with custom 8x8 pixel art featuring bruised amber decaying skin, dark rot cavities, a withered twig stem, and festering green mold spores with 60 FPS bioluminescent respiration. Despawn after 80 ticks.
|
||
- **Cursed Apples:** Invert steering controls (Up ↔ Down, Left ↔ Right) for 40 ticks, requiring quick reflexes and adapted steering. Despawn after 90 ticks if avoided, allowing new curses to spawn later.
|
||
- **Dual-Screen Layout:**
|
||
- **Top Screen:** Real-time scoreboard, status alerts (*"CONTROLS INVERTED!"*), pause overlay, and optional debug stats.
|
||
- **Bottom Screen:** Smooth 2D hardware sprite rendering using DS Sub-engine OAM.
|
||
- **LCD Swap:** Switch top and bottom screens on the fly with the **R** shoulder button.
|
||
- **Audio & Sound Effects:**
|
||
- Background tracker music ([`0rigin.xm`](sounds/0rigin.xm)) powered by MaxMod 9.
|
||
- Sound effects for movement, pickups, rotten/cursed explosions, pause, and game over.
|
||
- On-the-fly music toggle from the menu or pause screen.
|
||
- **Game Modes:**
|
||
- **Normal Mode:** Relaxed tick rate (`TIMER_FREQ_1024(5)`), starting length 4.
|
||
- **Hard Mode:** High-speed tick rate (`TIMER_FREQ_1024(9)`), starting length 2, and periodic rotten apple hazards.
|
||
- **Main Menu & Pause Screen:**
|
||
- Integrated start menu to toggle Hard Mode and Music.
|
||
- In-game pause menu with instant restarts and mode toggles.
|
||
|
||
---
|
||
|
||
## Controls
|
||
|
||
| Screen / Context | Button | Action |
|
||
|---|---|---|
|
||
| **Main Menu** | **D-Pad Up / Down** | Navigate menu options |
|
||
| **Main Menu** | **A** or **START** | Select option / Start game |
|
||
| **In-Game** | **D-Pad** | Steer snake (inverted while cursed) |
|
||
| **In-Game** | **START** | Pause / Resume |
|
||
| **In-Game** | **R Trigger** | Swap top and bottom screens (`lcdSwap`) |
|
||
| **In-Game** | **SELECT** | Toggle debug HUD (coordinates, ticks, raw keys) |
|
||
| **In-Game** | **START + L** | Exit game |
|
||
| **Paused** | **B** | Return to Main Menu |
|
||
| **Paused** | **X** | Toggle background music ON/OFF |
|
||
| **Paused** | **Y** | Toggle Normal / Hard mode and restart |
|
||
| **Game Over** | **A** or **START** | Restart game |
|
||
| **Game Over** | **B** | Return to Main Menu |
|
||
| **Game Over** | **R Trigger** | Swap top and bottom screens (`lcdSwap`) |
|
||
|
||
---
|
||
|
||
## Building from Source
|
||
|
||
### Prerequisites
|
||
|
||
Ensure you have the following installed:
|
||
1. **[devkitPro](https://devkitpro.org)** with the `nds-dev` payload:
|
||
- `devkitARM` toolchain
|
||
- `libnds`, `calico`, `maxmod9`
|
||
- Utilities: `ndstool`, `grit`, `mmutil`, `bin2s`
|
||
2. **CMake** (>= 3.13)
|
||
3. **Ninja** build system
|
||
4. **[melonDS](https://melonds.kuribo64.net/)** (optional, for emulation and testing)
|
||
|
||
Ensure the `DEVKITPRO` environment variable is set:
|
||
```bash
|
||
export DEVKITPRO=/opt/devkitpro
|
||
```
|
||
|
||
### Build Instructions
|
||
|
||
```bash
|
||
# Clone the repository
|
||
git clone <repo-url> snakePlus
|
||
cd snakePlus
|
||
|
||
# Configure build with Ninja
|
||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||
|
||
# Compile the .nds ROM and .elf binary
|
||
cmake --build build
|
||
```
|
||
|
||
This generates:
|
||
- [`snakePlus.nds`](snakePlus.nds) — Ready to run on an emulator or flashcard.
|
||
- [`snakePlus.elf`](snakePlus.elf) — ELF binary for GDB debugging.
|
||
|
||
---
|
||
|
||
## Running & Emulation
|
||
|
||
### Using melonDS via CMake target
|
||
|
||
```bash
|
||
cmake --build build --target run
|
||
```
|
||
|
||
### Direct launch
|
||
|
||
```bash
|
||
melonDS snakePlus.nds
|
||
```
|
||
|
||
### Installing to Flashcard / SD Card
|
||
|
||
A convenience target is included in CMake to copy the `.nds` ROM to an SD card path:
|
||
|
||
```bash
|
||
cmake --build build --target install_sd
|
||
```
|
||
*(Configure `OUTPUT_DIRECTORY` in [`CMakeLists.txt`](CMakeLists.txt) as desired).*
|
||
|
||
---
|
||
|
||
## Development & Code Quality
|
||
|
||
- **VS Code Tasks:** Built-in tasks in [`.vscode/tasks.json`](.vscode/tasks.json) support one-key build, run in melonDS, and debug with GDB.
|
||
- **Clang-Tidy:** Run `run-clang-tidy` or launch the watch script:
|
||
```bash
|
||
bash tools/clang_tidy_watch.sh
|
||
```
|
||
- **Language Standards:** C11 / C++14 targeting ARM9 (`armv5te`).
|
||
|
||
---
|
||
|
||
## Architecture & Developer Documentation
|
||
|
||
For an in-depth architectural breakdown, hardware register mapping, OAM details, and guidelines for AI agents or contributors, refer to [`AGENTS.md`](AGENTS.md).
|
||
|
||
---
|
||
|
||
## Credits & Author
|
||
|
||
- **Game Design & Code:** Phillip Kuehne
|
||
- **Music:** `0rigin.xm`
|
||
- **Sound Effects:** Freesound.org contributors (`treasuresounds`, `pacomav`, `crisstanza`, `timbre`, `benboncan`)
|
||
- **Platform Libraries:** `libnds`, `maxmod` by devkitPro team
|