Files
T
enne2 e5c37c7c86 Aggiorna e approfondisci tutta la documentazione
Riscritti tutti i file doc/ e README.md per riflettere lo stato attuale
del gioco (8 livelli, multi-nemico, corsa, finale tragico, musica
dedicata, fog scalabile, flush dinamico, claimed.png death screen, ecc.):

- index.md: nuovo indice con riferimenti ai 7 capitoli aggiornati.
- architecture.md: tabella moduli con righe, variabili chiave globals,
  pipeline asset, vincoli hardware (ROM/WRAM/VRAM/OAM).
- graphics.md: proiezione iso, fog scalabile (fog_radius), auto-tiling
  multi-pass, flush dinamico 16-righe con wrap, HUD stamina + livello.
- generation.md: DFS con stack WRAM, dimensioni crescenti 7->21, botola
  a distanza scalabile, spawn multi-nemico.
- gameplay.md: DAS, camminata, corsa, salto, stamina scalabile,
  progressione 8 livelli, schermate (death/going deeper/finale).
- ai.md: multi-entity (8 fantasmi), greedy, cooldown scalabile, hitbox,
  culling.
- audio.md: 4 canali APU, 5 tracce (title 112/3canali, gameplay 96,
  gameover 128, finale 192 loop, going deeper 96), SFX.
- AScreamFromTheDark_report.md: riscritto completamente (~200 righe),
  tutte le sezioni aggiornate, workaround storici, debiti tecnici.
- README.md: intro, caratteristiche, soundtrack, architettura, test.
2026-06-24 20:42:35 +02:00

2.5 KiB
Raw Permalink Blame History

Intelligenza Artificiale (Fantasmi)

Multi-Entity (fino a 8 fantasmi)

Il gioco supporta fino a MAX_ENEMIES = 8 fantasmi coesistenti. Il numero attivo è num_enemies = level (1 al livello 1, 8 al livello 8). Lo stato di ciascun fantasma è in array indicizzati:

uint8_t enemy_lx[8], enemy_ly[8];
uint8_t enemy_is_moving[8], enemy_move_progress[8];
int8_t  enemy_start_lx[8], enemy_start_ly[8];
int8_t  enemy_target_lx[8], enemy_target_ly[8];
int16_t enemy_start_px[8], enemy_start_py[8];
int16_t enemy_target_px[8], enemy_target_py[8];
uint8_t enemy_cooldown[8];

Ogni fantasma usa 2 slot OAM a partire da 2 + i*2 (il player usa 0-1). Tutti condividono la stessa tile data (ghost sprite), con palette invertita (OBP1 = 0x1B, bianco su scuro).

Pathfinding: Greedy invece di A*

A* richiederebbe heap, nodi e calcoli pesanti — inammissibile su 4 MHz, soprattutto con 8 fantasmi. L'approccio Greedy è leggero:

Per ciascun fantasma, tra le 4 celle adiacenti calpestabili, sceglie quella che minimizza la distanza al quadrato verso il giocatore (dx² + dy²). No sqrt (i quadrati bastano per il confronto A < B ⇔ A² < B²).

Difetto voluto: il Greedy si incastra nei vicoli a U. In un labirinto con loop, questo diventa una dinamica di gioco — il giocatore astuto sfrutta il level design per seminare i fantasmi.

Attivazione e Cooldown Scalabile

Attivazione

Il fantasma insegue solo se entro il raggio di nebbia:

if (dist <= fog_radius) { // inizia a inseguire }

fog_radius = 2 (5×5) ai livelli 1-6, = 1 (3×3) ai livelli 7-8.

Cooldown

Dopo ogni passo (16 frame di LERP), il fantasma aspetta enemy_step_cooldown frame:

  • Livello 1: 60 frame (1 secondo) — lento, gestibile.
  • Livello 8: 11 frame — implacabile, quasi continuo.

I cooldown iniziali sono sfasati (enemy_step_cooldown + i*8) così i fantasmi non si muovono in sincrono.

Hitbox Pixel-Perfect

La morte non si basa sulla coincidenza di cella logica, ma sulla sovrapposizione dei pixel fisici renderizzati:

if (|p_px - enemy_px| < 12 && |p_py - enemy_py| < 6) {
    game_over = 1;  // sconfitta
}

Questo rende la morte "giusta" agli occhi del giocatore e permette scambi per il rotto della cuffia. Al primo fantasma che catcha il player, il loop si ferma (return).

Culling

I fantasmi sono renderizzati solo se:

  1. Entro fog_radius dal giocatore (Chebyshev).
  2. On-screen (con wrap & 255 + soglie fisiche -8..168 / -8..152).

Altrimenti lo sprite è spostato offscreen (0,0).