#ifndef GAME_STATE_H #define GAME_STATE_H #include #include #include #include #include "consts.h" enum class Scene { Menu, Playing, }; constexpr int MENU_ITEM_COUNT = 3; 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; 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 barriers{}; volatile uint16_t barrierSpawnCooldown = 0; // Frames until the next spawn attempt std::deque> snake; std::vector> apples; std::vector bad_apples; std::vector cursed_apples; }; #endif