Files
snakePlus/include/game_state.h
T
phillipandAntigravity 496de1a31f fix: decouple VBlank ISR and main loop to prevent IRQ stack overflow
- 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>
2026-10-04 00:21:09 +02:00

78 lines
1.9 KiB
C++

#ifndef GAME_STATE_H
#define GAME_STATE_H
#include <array>
#include <deque>
#include <vector>
#include <nds/ndstypes.h>
#include "consts.h"
enum class Scene {
Menu,
Playing,
};
constexpr int MENU_ITEM_COUNT = 3;
enum class GameOverReason : uint8_t {
None = 0,
SelfCollision,
BarrierCollision,
RottenAppleDepleted,
};
enum class BarrierPhase : uint8_t {
Inactive, // Slot free
Warning, // Flashing announcement, harmless
Active, // Solid and fatal to the snake head
};
// Temporary hard-mode wall: a straight line of `length` cells starting at (x, y).
// Lifetime is tracked in VBlank frames (see tick_barriers_frame()).
struct Barrier {
BarrierPhase phase = BarrierPhase::Inactive;
int8_t x = 0;
int8_t y = 0;
bool vertical = false;
uint8_t length = 0;
uint16_t framesLeft = 0; // Frames remaining in the current phase
};
// Evil item (rotten apple or cursed apple) with position and lifetime in game ticks
struct EvilItem {
int8_t x = 0;
int8_t y = 0;
uint16_t ticksRemaining = 0;
};
struct GameState {
volatile uint16_t ticks = 0;
volatile uint16_t snakelength = 0;
volatile uint16_t snakeDirection = 1;
volatile uint16_t score = 0;
volatile uint16_t applesEaten = 0;
bool gameOver = false;
GameOverReason gameOverReason = GameOverReason::None;
bool paused = false;
bool hardMode = false;
Scene scene = Scene::Menu;
int menuSelection = 0;
uint16_t curseTicksRemaining = 0;
// Fixed-size (no heap) because barriers are touched from both the VBlank
// and Timer 0 IRQs. Those IRQs don't nest, so no further locking is needed.
std::array<Barrier, BARRIER_MAX_COUNT> barriers{};
volatile uint16_t barrierSpawnCooldown = 0; // Frames until the next spawn attempt
std::deque<std::array<int, 3>> snake;
std::vector<std::array<int, 2>> apples;
std::vector<EvilItem> bad_apples;
std::vector<EvilItem> cursed_apples;
};
#endif