8-level progression with scaling difficulty and a finale

Maze size grows +2/level from 7x7 to 21x21 (cap MAX_MAP_SIZE=21, reached
at level 8). Additional difficulty axes scale with level:
- +1 ghost per level (up to MAX_ENEMIES=8); enemy state moved to arrays,
  enemy_logic.c rewritten as a multi-entity loop (own AI, render at OAM
  2+i*2, staggered cooldowns).
- Ghost cooldown shrinks: 60 -> ~11 frames between steps.
- Stamina recharge slows: 60 -> 144 frames per point.
- Fog tightens to 3x3 (fog_radius=1) from level 7.
Fog rendering uses a dynamic 16-row centered flush (with wrap) instead of
the fixed rows 2-17 / full 32x32, so the fog stays correct on big mazes
without the multi-frame stall of the full flush.

Game ends after level 8: reaching the hatch at level 8 sets game_over=3
(finale) instead of 2. Finale screen reloads the IBM font and writes
'YOU ESCAPED / THE DARKNESS / LEVEL 8 CLEARED / PRESS START' into the BG
map; START returns to the title (new game starts at level 1).

Verified via PyBoy: sizes 7..21, fog 2->1 at L7, enemies 1..8 (distinct,
on floor, can catch the player), cooldown/recharge scaling, L1 hatch ->
game_over=2, L8 hatch -> game_over=3, finale renders + START->title->L1.
This commit is contained in:
2026-06-24 13:04:39 +02:00
parent 5e8709ade0
commit 4968d2e885
10 changed files with 317 additions and 264 deletions
+27 -26
View File
@@ -7,71 +7,72 @@
* ==========================================
* GLOBAL GAME STATE
* ==========================================
* This file contains all the global variables that are shared across different
* logical modules (e.g., rendering, player logic, enemy logic, audio).
* We place them here to avoid circular dependencies between modules.
* Shared variables across modules (rendering, player, enemy, audio).
* Centralized here to avoid circular dependencies in C.
*/
// --- General Application State ---
// app_state = 0 means Title Screen, 1 means Gameplay
extern uint8_t app_state;
// game_over = 0 means Playing, 1 means Defeat (Ghost caught you), 2 means Victory (Reached portal)
// game_over: 0 = Playing, 1 = Defeat (caught), 2 = Victory/Going Deeper (next level),
// 3 = Finale (cleared level 8 -> game complete)
extern volatile uint8_t game_over;
// Timer used to delay actions after game over (e.g. before showing text or playing music)
extern volatile uint8_t game_over_timer;
// --- Map Data ---
#define MAP_SIZE 7 // initial maze size (level 1); must be odd
#define MAX_MAP_SIZE 17 // hard cap for maze growth (odd); used as the array bound
#define MAX_MAP_SIZE 21 // hard cap for maze growth (odd); array bound. Reached at level 8.
// The generated maze: 0 = Wall, 1 = Floor, 2 = Victory Tile (hatch)
extern uint8_t map_size; // current maze side length (grows with level, MAP_SIZE..MAX_MAP_SIZE)
extern uint8_t map_size; // current maze side length (MAP_SIZE + 2*(level-1), capped)
extern uint8_t maze[MAX_MAP_SIZE][MAX_MAP_SIZE];
// --- Fog of War ---
extern uint8_t fog_radius; // Chebyshev visibility radius (2 normally, 1 from level 7)
// --- Camera & Rendering ---
// Map buffer used to draw the isometric tiles into the Game Boy Background map
extern uint8_t map_buffer[32 * 32];
// Camera scroll coordinates to keep the player centered
extern uint8_t scroll_x;
extern int8_t scroll_y;
extern uint8_t stairs_lx;
extern uint8_t stairs_ly;
// --- Mappa e Entita ---
// --- Player ---
extern uint8_t player_lx;
extern uint8_t player_ly;
extern uint8_t player_dir; // 0=DR, 1=DL, 2=UL, 3=UR
// --- Movement & Physics State ---
// Shared variables for interpolated movement (walking/jumping)
extern uint8_t is_moving;
extern uint8_t is_jumping;
extern uint8_t is_running; // B+direction: step in 8 frames instead of 16, costs 10 stamina/tile
extern uint8_t move_progress; // 0 to 16, tracks the sub-tile animation progress
extern uint8_t is_running; // B+direction: 8-frame step, 10 stamina/tile
extern uint8_t move_progress; // 0 to 16
extern int8_t start_lx, start_ly;
extern int8_t target_lx, target_ly;
extern int16_t start_px, start_py;
extern int16_t target_px, target_py;
// --- Stamina System ---
// Stamina powers the jump/run mechanic. 100 = full. Recharges over time.
extern uint8_t stamina;
extern uint8_t stamina_recharge_timer;
extern uint8_t stamina_recharge_rate; // frames per +1 stamina (grows with level)
// --- Level Progression ---
// Current level (starts at 1, increments each time the hatch is reached).
extern uint8_t level;
extern uint8_t level; // starts at 1; game completes after clearing level 8
// --- Enemy State ---
extern uint8_t enemy_lx;
extern uint8_t enemy_ly;
extern uint8_t enemy_is_moving;
extern uint8_t enemy_move_progress;
extern int8_t enemy_start_lx, enemy_start_ly;
extern int8_t enemy_target_lx, enemy_target_ly;
extern int16_t enemy_start_px, enemy_start_py;
extern int16_t enemy_target_px, enemy_target_py;
extern uint8_t enemy_cooldown;
// --- Enemy System (multi-entity: up to MAX_ENEMIES ghosts) ---
#define MAX_ENEMIES 8
extern uint8_t num_enemies; // current ghost count (= level, capped at MAX_ENEMIES)
extern uint8_t enemy_step_cooldown; // base pause (frames) between ghost steps; shrinks with level
extern uint8_t enemy_lx[MAX_ENEMIES];
extern uint8_t enemy_ly[MAX_ENEMIES];
extern uint8_t enemy_is_moving[MAX_ENEMIES];
extern uint8_t enemy_move_progress[MAX_ENEMIES];
extern int8_t enemy_start_lx[MAX_ENEMIES], enemy_start_ly[MAX_ENEMIES];
extern int8_t enemy_target_lx[MAX_ENEMIES], enemy_target_ly[MAX_ENEMIES];
extern int16_t enemy_start_px[MAX_ENEMIES], enemy_start_py[MAX_ENEMIES];
extern int16_t enemy_target_px[MAX_ENEMIES], enemy_target_py[MAX_ENEMIES];
extern uint8_t enemy_cooldown[MAX_ENEMIES];
#endif