feat: Add rat AI, 4-channel tracker, interactive cursor, and test suite updates
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
#include "cursor.h"
|
||||
#include "maze.h"
|
||||
#include <gb/gb.h>
|
||||
|
||||
const unsigned char CursorSpriteData[] = {
|
||||
// Bordo quadrato 8x8 (Nero, colore 3 -> 11)
|
||||
0xFF,0xFF, 0x81,0x81, 0x81,0x81, 0x81,0x81,
|
||||
0x81,0x81, 0x81,0x81, 0x81,0x81, 0xFF,0xFF
|
||||
};
|
||||
|
||||
static uint8_t cursor_x = 1;
|
||||
static uint8_t cursor_y = 1;
|
||||
static uint8_t previous_keys = 0;
|
||||
static uint8_t blink = 0;
|
||||
|
||||
void init_cursor(void) {
|
||||
cursor_x = 1;
|
||||
cursor_y = 1;
|
||||
previous_keys = 0;
|
||||
blink = 0;
|
||||
|
||||
// Carica il tile del cursore nell'indice 4 della VRAM degli Sprite
|
||||
// (Gli indici 0-3 sono usati dai tile del topo)
|
||||
set_sprite_data(4, 1, CursorSpriteData);
|
||||
|
||||
// Usa lo sprite hardware numero 2 (0 e 1 sono il topo)
|
||||
set_sprite_tile(2, 4);
|
||||
}
|
||||
|
||||
void update_cursor(void) {
|
||||
uint8_t keys = joypad();
|
||||
|
||||
// Movimento controllato dal D-PAD
|
||||
if ((keys & J_UP) && !(previous_keys & J_UP)) {
|
||||
if (cursor_y > 0) cursor_y--;
|
||||
}
|
||||
if ((keys & J_DOWN) && !(previous_keys & J_DOWN)) {
|
||||
if (cursor_y < MAZE_HEIGHT - 1) cursor_y++;
|
||||
}
|
||||
if ((keys & J_LEFT) && !(previous_keys & J_LEFT)) {
|
||||
if (cursor_x > 0) cursor_x--;
|
||||
}
|
||||
if ((keys & J_RIGHT) && !(previous_keys & J_RIGHT)) {
|
||||
if (cursor_x < MAZE_WIDTH - 1) cursor_x++;
|
||||
}
|
||||
|
||||
previous_keys = keys;
|
||||
|
||||
// Effetto lampeggiante: nascondi il cursore per metà del ciclo (ogni 16 frame)
|
||||
blink++;
|
||||
if (blink & 0x10) {
|
||||
move_sprite(2, 0, 0); // Nascondi spostandolo fuori schermo
|
||||
} else {
|
||||
// Posiziona il cursore calcolando offset (12, 20) per centrarlo sulla griglia 8x8
|
||||
move_sprite(2, cursor_x * 8 + 12, cursor_y * 8 + 20);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef CURSOR_H
|
||||
#define CURSOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void init_cursor(void);
|
||||
void update_cursor(void);
|
||||
|
||||
#endif
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @file main.c
|
||||
* @brief Entry point e inizializzazione hardware del Game Boy.
|
||||
*
|
||||
* Questo modulo funge da collante: inizializza le periferiche video,
|
||||
* semina la randomicità per il generatore, invoca l'algoritmo logico e
|
||||
* disegna i tile sullo schermo del Game Boy, manipolando i registri hardware.
|
||||
*/
|
||||
|
||||
#include <gb/gb.h>
|
||||
#include <stdint.h>
|
||||
#include <rand.h>
|
||||
|
||||
#include "maze.h"
|
||||
#include "tiles.h"
|
||||
|
||||
#include "rat.h"
|
||||
#include "music.h"
|
||||
#include "cursor.h"
|
||||
|
||||
void main(void) {
|
||||
uint8_t y, x;
|
||||
uint8_t black = 1;
|
||||
|
||||
// Attende il termine dell'aggiornamento verticale dello schermo (VBLANK).
|
||||
// Caricare dati in VRAM al di fuori di questo periodo può causare tearing o artefatti.
|
||||
wait_vbl_done();
|
||||
|
||||
// Invia i dati grafici (array TileData) dal banco ROM alla VRAM a partire dall'indice 0.
|
||||
set_bkg_data(0, 2, TileData);
|
||||
|
||||
// La mappa background in memoria hardware è grande 32x32 tiles.
|
||||
// Riempiamo preventivamente tutta quest'area nascosta con il tile Nero (indice 1),
|
||||
// utile per mascherare i bordi quando lo schermo verrà "scrollato" successivamente.
|
||||
for (y = 0; y < 32; y++) {
|
||||
for (x = 0; x < 32; x++) {
|
||||
set_bkg_tiles(x, y, 1, 1, &black);
|
||||
}
|
||||
}
|
||||
|
||||
// Inizializza il seme dei numeri casuali.
|
||||
initrand(DIV_REG);
|
||||
|
||||
// Invoca la funzione logica per popolare l'array `maze` in memoria.
|
||||
generate_maze();
|
||||
|
||||
// Riversa l'array generato `maze` nella memoria del Background (VRAM Mappa).
|
||||
for (y = 0; y < MAZE_HEIGHT; y++) {
|
||||
for (x = 0; x < MAZE_WIDTH; x++) {
|
||||
set_bkg_tiles(x, y, 1, 1, &maze[y][x]);
|
||||
}
|
||||
}
|
||||
|
||||
// Trucco di centraggio (Hardware Scrolling):
|
||||
move_bkg(252, 252);
|
||||
|
||||
// Inizializza il topo autonomo nel punto di partenza (1, 1)
|
||||
init_rat(1, 1);
|
||||
|
||||
// Inizializza il cursore controllato dal giocatore
|
||||
init_cursor();
|
||||
|
||||
// Inizializza il sequencer musicale
|
||||
init_music();
|
||||
|
||||
SHOW_BKG;
|
||||
SHOW_SPRITES;
|
||||
DISPLAY_ON;
|
||||
|
||||
// Game Loop primario
|
||||
while (1) {
|
||||
// Aggiorna la musica in background
|
||||
update_music();
|
||||
|
||||
// Aggiorna l'intelligenza artificiale e l'animazione del topo
|
||||
update_rat();
|
||||
|
||||
// Aggiorna l'input del cursore del giocatore
|
||||
update_cursor();
|
||||
|
||||
// Attendi la sincronizzazione con il monitor
|
||||
wait_vbl_done();
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file maze.c
|
||||
* @brief Implementazione dell'algoritmo di generazione del labirinto.
|
||||
*/
|
||||
|
||||
#include "maze.h"
|
||||
#include <rand.h>
|
||||
|
||||
// Istanza globale in RAM (WRAM) della mappa del labirinto.
|
||||
uint8_t maze[MAZE_HEIGHT][MAZE_WIDTH];
|
||||
|
||||
// Stack customizzato utilizzato per l'algoritmo Recursive Backtracker.
|
||||
// Si evitano le chiamate di funzione ricorsive per non esaurire
|
||||
// lo spazio dello stack hardware del Game Boy.
|
||||
uint8_t stack_x[100];
|
||||
uint8_t stack_y[100];
|
||||
uint8_t stack_ptr = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserisce una nuova coordinata in cima allo stack.
|
||||
* @param x Posizione X della stanza.
|
||||
* @param y Posizione Y della stanza.
|
||||
*/
|
||||
static void push(uint8_t x, uint8_t y) {
|
||||
stack_x[stack_ptr] = x;
|
||||
stack_y[stack_ptr] = y;
|
||||
stack_ptr++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Estrae e rimuove l'ultima coordinata dallo stack.
|
||||
* @param x Puntatore su cui scrivere la X rimossa.
|
||||
* @param y Puntatore su cui scrivere la Y rimossa.
|
||||
*/
|
||||
static void pop(uint8_t *x, uint8_t *y) {
|
||||
stack_ptr--;
|
||||
*x = stack_x[stack_ptr];
|
||||
*y = stack_y[stack_ptr];
|
||||
}
|
||||
|
||||
void generate_maze(void) {
|
||||
uint8_t x, y;
|
||||
uint8_t nx, ny;
|
||||
uint8_t dirs[4];
|
||||
uint8_t count, r;
|
||||
|
||||
// Fase 1: Inizializza l'intera griglia con blocchi di muro (1)
|
||||
for (y = 0; y < MAZE_HEIGHT; y++) {
|
||||
for (x = 0; x < MAZE_WIDTH; x++) {
|
||||
maze[y][x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fase 2: Imposta il punto iniziale di scavo.
|
||||
// Il punto in (1, 1) è la prima "stanza" calpestabile.
|
||||
stack_ptr = 0;
|
||||
push(1, 1);
|
||||
maze[1][1] = 0; // 0 = calpestabile
|
||||
|
||||
// Fase 3: Scavo iterativo (Recursive Backtracker)
|
||||
while (stack_ptr > 0) {
|
||||
// Leggi la cella attuale in cima allo stack
|
||||
x = stack_x[stack_ptr - 1];
|
||||
y = stack_y[stack_ptr - 1];
|
||||
|
||||
// Cerca direzioni inesplorate a distanza 2 (oltre il potenziale muro)
|
||||
count = 0;
|
||||
if (x >= 2 && maze[y][x - 2] == 1) dirs[count++] = 0; // Controllo Sinistra
|
||||
if (x <= MAZE_WIDTH - 3 && maze[y][x + 2] == 1) dirs[count++] = 1; // Controllo Destra
|
||||
if (y >= 2 && maze[y - 2][x] == 1) dirs[count++] = 2; // Controllo Su
|
||||
if (y <= MAZE_HEIGHT - 3 && maze[y + 2][x] == 1) dirs[count++] = 3; // Controllo Giù
|
||||
|
||||
if (count > 0) {
|
||||
// È stata trovata almeno una direzione valida:
|
||||
// si sceglie casualmente una tra quelle esplorabili
|
||||
r = rand() % count;
|
||||
|
||||
nx = x; ny = y;
|
||||
// Calcola la cella adiacente finale e demolisci il muro intermedio
|
||||
if (dirs[r] == 0) { nx -= 2; maze[y][x - 1] = 0; }
|
||||
else if (dirs[r] == 1) { nx += 2; maze[y][x + 1] = 0; }
|
||||
else if (dirs[r] == 2) { ny -= 2; maze[y - 1][x] = 0; }
|
||||
else if (dirs[r] == 3) { ny += 2; maze[y + 1][x] = 0; }
|
||||
|
||||
// Imposta la nuova cella come esplorata (calpestabile)
|
||||
maze[ny][nx] = 0;
|
||||
// Pusha la nuova coordinata nello stack per espandere il ramo
|
||||
push(nx, ny);
|
||||
} else {
|
||||
// Vicolo cieco: estrae la coordinata corrente per risalire
|
||||
// al nodo precedente (backtracking)
|
||||
pop(&x, &y);
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file maze.h
|
||||
* @brief Strutture e funzioni per la generazione procedurale del labirinto.
|
||||
*/
|
||||
|
||||
#ifndef MAZE_H
|
||||
#define MAZE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Dimensioni logiche del labirinto in numero di tiles (1 tile = 8x8 px).
|
||||
// Manteniamo dimensioni dispari (19x17) in modo da poter creare un
|
||||
// labirinto simmetrico circondato da muri uniformi una volta centrato.
|
||||
#define MAZE_WIDTH 19
|
||||
#define MAZE_HEIGHT 17
|
||||
|
||||
/**
|
||||
* @brief Matrice globale che rappresenta la mappa del labirinto in RAM.
|
||||
*
|
||||
* Il valore 1 rappresenta un Muro (Tile Nero).
|
||||
* Il valore 0 rappresenta un Percorso (Tile Bianco).
|
||||
*/
|
||||
extern uint8_t maze[MAZE_HEIGHT][MAZE_WIDTH];
|
||||
|
||||
/**
|
||||
* @brief Esegue l'algoritmo di generazione del labirinto.
|
||||
*
|
||||
* Utilizza la logica di tipo Recursive Backtracker (implementata iterativamente
|
||||
* con array custom per non sforare lo stack di sistema limitato).
|
||||
*/
|
||||
void generate_maze(void);
|
||||
|
||||
#endif
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
#include "music.h"
|
||||
#include <gb/gb.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define N_C4 0x60B
|
||||
#define N_CS4 0x624
|
||||
#define N_D4 0x641
|
||||
#define N_DS4 0x65A
|
||||
#define N_E4 0x672
|
||||
#define N_F4 0x689
|
||||
#define N_FS4 0x69E
|
||||
#define N_G4 0x6B2
|
||||
#define N_GS4 0x6C6
|
||||
#define N_A4 0x6D6
|
||||
#define N_AS4 0x6E8
|
||||
#define N_B4 0x6F7
|
||||
#define N_C5 0x706
|
||||
#define N_CS5 0x712
|
||||
#define N_D5 0x721
|
||||
#define N_DS5 0x72D
|
||||
#define N_E5 0x739
|
||||
#define N_F5 0x744
|
||||
#define N_FS5 0x74F
|
||||
#define N_G5 0x759
|
||||
#define N_GS5 0x764
|
||||
#define N_A5 0x76B
|
||||
#define N_AS5 0x774
|
||||
#define N_B5 0x77B
|
||||
#define N_C6 0x783
|
||||
#define N_CS6 0x789
|
||||
#define N_D6 0x790
|
||||
#define N_DS6 0x796
|
||||
#define N_E6 0x79C
|
||||
#define N_REST 0x000
|
||||
|
||||
// ==========================================
|
||||
// TRACK 1: ARPEGGIO (CH1)
|
||||
// ==========================================
|
||||
#define A_C N_C4
|
||||
#define A_E N_E4
|
||||
#define A_F N_F4
|
||||
#define A_G N_G4
|
||||
#define A_GS N_GS4
|
||||
#define A_A N_A4
|
||||
#define A_B N_B4
|
||||
#define A_D N_D4
|
||||
#define A_C5 N_C5
|
||||
#define A_E5 N_E5
|
||||
|
||||
const uint16_t trk_arp_0[64] = {
|
||||
A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E,
|
||||
A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A,
|
||||
A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B,
|
||||
A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_C5, A_E, A_G, A_C5, A_E
|
||||
};
|
||||
const uint16_t trk_arp_1[64] = {
|
||||
A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E,
|
||||
A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A,
|
||||
A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B,
|
||||
A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E
|
||||
};
|
||||
const uint16_t trk_arp_2[64] = {
|
||||
A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A,
|
||||
A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B,
|
||||
A_E, A_GS, A_B, A_GS, A_E, A_GS, A_B, A_GS, A_E, A_GS, A_B, A_GS, A_E, A_GS, A_B, A_GS,
|
||||
A_A, A_C5, A_E5, A_C5, A_A, A_C5, A_E5, A_C5, A_A, A_C5, A_E5, A_C5, A_A, A_C5, A_E5, A_C5
|
||||
};
|
||||
const uint16_t trk_arp_3[64] = {
|
||||
A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A, A_F, A_A, A_C5, A_A,
|
||||
A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B, A_G, A_B, A_D, A_B,
|
||||
A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E,
|
||||
A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_E, A_C, A_E, A_G, A_C5, A_E, A_G, A_C5, A_E
|
||||
};
|
||||
const uint16_t* const arp_parts[4] = { trk_arp_0, trk_arp_1, trk_arp_2, trk_arp_3 };
|
||||
|
||||
// ==========================================
|
||||
// TRACK 2: MELODY (CH2)
|
||||
// ==========================================
|
||||
#define M_C N_C5
|
||||
#define M_D N_D5
|
||||
#define M_E N_E5
|
||||
#define M_F N_F5
|
||||
#define M_G N_G5
|
||||
#define M_GS N_GS5
|
||||
#define M_A N_A5
|
||||
#define M_B N_B5
|
||||
#define M_C6 N_C6
|
||||
#define M_D6 N_D6
|
||||
#define M_E6 N_E6
|
||||
#define M__ N_REST
|
||||
|
||||
const uint16_t trk_mel_0[64] = {
|
||||
M_E, M__, M_D, M_C, M__, M_G, M__, M__, M_E, M__, M_D, M_C, M__, M_G, M__, M__,
|
||||
M_F, M__, M_E, M_D, M__, M_A, M__, M__, M_F, M__, M_E, M_D, M__, M_A, M__, M__,
|
||||
M_G, M__, M_F, M_E, M__, M_B, M__, M__, M_G, M__, M_F, M_E, M__, M_B, M__, M__,
|
||||
M_C6, M__, M_B, M_A, M_G, M_F, M_E, M_D, M_C, M__, M__, M__, M__, M__, M__, M__
|
||||
};
|
||||
const uint16_t trk_mel_1[64] = {
|
||||
M_E, M__, M_E, M__, M_D, M_C, M_G, M__, M_E, M__, M_D, M_C, M__, M_G, M__, M__,
|
||||
M_F, M__, M_F, M__, M_E, M_D, M_A, M__, M_F, M__, M_E, M_D, M__, M_A, M__, M__,
|
||||
M_G, M__, M_G, M__, M_F, M_E, M_B, M__, M_G, M__, M_F, M_E, M__, M_B, M__, M__,
|
||||
M_C6, M_G, M_A, M_B, M_C6, M__, M__, M__, M_C, M__, M__, M__, M__, M__, M__, M__
|
||||
};
|
||||
const uint16_t trk_mel_2[64] = {
|
||||
M_A, M__, M_C6, M_A, M_F, M__, M__, M__, M_A, M__, M_C6, M_A, M_F, M__, M__, M__,
|
||||
M_B, M__, M_D6, M_B, M_G, M__, M__, M__, M_B, M__, M_D6, M_B, M_G, M__, M__, M__,
|
||||
M_GS, M__, M_B, M_GS, M_E, M__, M__, M__, M_GS, M__, M_B, M_GS, M_E, M__, M__, M__,
|
||||
M_A, M__, M_C6, M_A, M_E, M__, M__, M__, M_A, M__, M_A, M__, M_A, M__, M_A, M__
|
||||
};
|
||||
const uint16_t trk_mel_3[64] = {
|
||||
M_A, M__, M_G, M_F, M__, M_C6, M__, M__, M_A, M__, M_G, M_F, M__, M_C6, M__, M__,
|
||||
M_B, M__, M_A, M_G, M__, M_D6, M__, M__, M_B, M__, M_A, M_G, M__, M_D6, M__, M__,
|
||||
M_E6, M__, M_D6, M_C6, M_B, M_A, M_G, M_F, M_E, M_D, M_C, M__, M__, M__, M__, M__,
|
||||
M_C, M__, M_D, M__, M_E, M__, M_G, M__, M_C6, M__, M__, M__, M__, M__, M__, M__
|
||||
};
|
||||
const uint16_t* const mel_parts[4] = { trk_mel_0, trk_mel_1, trk_mel_2, trk_mel_3 };
|
||||
|
||||
// ==========================================
|
||||
// TRACK 3: BASS (CH3 - Wave)
|
||||
// ==========================================
|
||||
#define B_C N_C4
|
||||
#define B_D N_D4
|
||||
#define B_E N_E4
|
||||
#define B_F N_F4
|
||||
#define B_G N_G4
|
||||
#define B_GS N_GS4
|
||||
#define B_A N_A4
|
||||
#define B_B N_B4
|
||||
#define B_C5 N_C5
|
||||
#define B_E5 N_E5
|
||||
#define B__ N_REST
|
||||
|
||||
const uint16_t trk_bass_0[64] = {
|
||||
B_C, B__, B_C, B__, B_E, B__, B_G, B__, B_C, B__, B_C, B__, B_G, B__, B_E, B__,
|
||||
B_F, B__, B_F, B__, B_A, B__, B_C5, B__, B_F, B__, B_F, B__, B_C5, B__, B_A, B__,
|
||||
B_G, B__, B_G, B__, B_B, B__, B_D, B__, B_G, B__, B_G, B__, B_D, B__, B_B, B__,
|
||||
B_C, B__, B_C, B__, B_E, B__, B_G, B__, B_C, B__, B_E, B__, B_G, B__, B_C5, B__
|
||||
};
|
||||
const uint16_t trk_bass_1[64] = {
|
||||
B_C, B__, B_C, B__, B_E, B__, B_G, B__, B_C, B__, B_C, B__, B_G, B__, B_E, B__,
|
||||
B_F, B__, B_F, B__, B_A, B__, B_C5, B__, B_F, B__, B_F, B__, B_C5, B__, B_A, B__,
|
||||
B_G, B__, B_G, B__, B_B, B__, B_D, B__, B_G, B__, B_G, B__, B_D, B__, B_B, B__,
|
||||
B_C, B__, B_C, B__, B_E, B__, B_G, B__, B_C, B__, B__, B__, B__, B__, B__, B__
|
||||
};
|
||||
const uint16_t trk_bass_2[64] = {
|
||||
B_F, B__, B_A, B__, B_C5, B__, B_A, B__, B_F, B__, B_A, B__, B_C5, B__, B_A, B__,
|
||||
B_G, B__, B_B, B__, B_D, B__, B_B, B__, B_G, B__, B_B, B__, B_D, B__, B_B, B__,
|
||||
B_E, B__, B_GS, B__, B_B, B__, B_GS, B__, B_E, B__, B_GS, B__, B_B, B__, B_GS, B__,
|
||||
B_A, B__, B_C5, B__, B_E5, B__, B_C5, B__, B_A, B__, B__, B__, B_G, B__, B__, B__
|
||||
};
|
||||
const uint16_t trk_bass_3[64] = {
|
||||
B_F, B__, B_A, B__, B_C5, B__, B_A, B__, B_F, B__, B_A, B__, B_C5, B__, B_A, B__,
|
||||
B_G, B__, B_B, B__, B_D, B__, B_B, B__, B_G, B__, B_B, B__, B_D, B__, B_B, B__,
|
||||
B_C, B__, B_E, B__, B_G, B__, B_E, B__, B_C, B__, B_E, B__, B_G, B__, B_E, B__,
|
||||
B_C, B__, B_G, B__, B_C5, B__, B_G, B__, B_C, B__, B__, B__, B__, B__, B__, B__
|
||||
};
|
||||
const uint16_t* const bass_parts[4] = { trk_bass_0, trk_bass_1, trk_bass_2, trk_bass_3 };
|
||||
|
||||
const uint8_t wave_ram[16] = {
|
||||
0x02, 0x46, 0x8A, 0xCE, 0xFF, 0xFE, 0xEC, 0xA8,
|
||||
0x64, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// ==========================================
|
||||
// TRACK 4: DRUMS (CH4 - Noise)
|
||||
// ==========================================
|
||||
const uint8_t trk_drum_0[64] = {
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 3, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 1, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 3, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 1, 3, 3, 2, 2, 1, 1
|
||||
};
|
||||
const uint8_t trk_drum_1[64] = {
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 3, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 1, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 3, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 2, 1, 2, 1, 2, 1, 2
|
||||
};
|
||||
const uint8_t trk_drum_2[64] = {
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 3, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 1, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 1, 3, 3, 3, 2, 3, 3, 3,
|
||||
1, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2
|
||||
};
|
||||
const uint8_t trk_drum_3[64] = {
|
||||
1, 3, 1, 3, 2, 3, 1, 3, 1, 3, 1, 3, 2, 3, 1, 3,
|
||||
1, 3, 1, 3, 2, 3, 1, 3, 1, 3, 1, 3, 2, 3, 1, 3,
|
||||
1, 3, 1, 3, 2, 3, 1, 3, 1, 3, 1, 3, 2, 3, 1, 3,
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
const uint8_t* const drum_parts[4] = { trk_drum_0, trk_drum_1, trk_drum_2, trk_drum_3 };
|
||||
|
||||
static uint16_t tick = 0;
|
||||
static uint8_t frame_counter = 0;
|
||||
#define FRAMES_PER_TICK 8 // Velocità del tracker
|
||||
|
||||
void init_music(void) {
|
||||
// Reset e attivazione master sound
|
||||
NR52_REG = 0x00;
|
||||
NR52_REG = 0x80;
|
||||
NR50_REG = 0x77;
|
||||
NR51_REG = 0xFF;
|
||||
|
||||
// Inizializza CH3 Wave RAM (0xFF30 - 0xFF3F)
|
||||
NR30_REG = 0x00; // Disable DAC
|
||||
volatile uint8_t *wave_ptr = (volatile uint8_t *)0xFF30;
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
wave_ptr[i] = wave_ram[i];
|
||||
}
|
||||
NR30_REG = 0x80; // Enable DAC
|
||||
|
||||
tick = 0;
|
||||
frame_counter = 0;
|
||||
}
|
||||
|
||||
void update_music(void) {
|
||||
if (frame_counter == 0) {
|
||||
|
||||
uint8_t part = (tick >> 6) & 0x03; // tick / 64
|
||||
uint8_t sub = tick & 0x3F; // tick % 64
|
||||
|
||||
// --- CH1: Arpeggio ---
|
||||
uint16_t f1 = arp_parts[part][sub];
|
||||
NR10_REG = 0x00;
|
||||
NR11_REG = 0x80;
|
||||
NR12_REG = 0x51; // Volume basso, decadimento rapido
|
||||
NR13_REG = (uint8_t)(f1 & 0xFF);
|
||||
NR14_REG = 0x80 | ((f1 >> 8) & 0x07);
|
||||
|
||||
// --- CH2: Melody ---
|
||||
uint16_t f2 = mel_parts[part][sub];
|
||||
if (f2 == N_REST) {
|
||||
NR21_REG = 0x00; NR22_REG = 0x00; NR23_REG = 0x00; NR24_REG = 0x80;
|
||||
} else {
|
||||
NR21_REG = 0x80;
|
||||
NR22_REG = 0xF2; // Volume alto
|
||||
NR23_REG = (uint8_t)(f2 & 0xFF);
|
||||
NR24_REG = 0x80 | ((f2 >> 8) & 0x07);
|
||||
}
|
||||
|
||||
// --- CH3: Bass ---
|
||||
uint16_t f3 = bass_parts[part][sub];
|
||||
if (f3 == N_REST) {
|
||||
NR32_REG = 0x00; // Vol 0
|
||||
NR34_REG = 0x80;
|
||||
} else {
|
||||
NR32_REG = 0x20; // Vol 100%
|
||||
NR33_REG = (uint8_t)(f3 & 0xFF);
|
||||
NR34_REG = 0x80 | ((f3 >> 8) & 0x07);
|
||||
}
|
||||
|
||||
// --- CH4: Drums ---
|
||||
uint8_t d4 = drum_parts[part][sub];
|
||||
if (d4 == 1) { // Kick
|
||||
NR41_REG = 0x00;
|
||||
NR42_REG = 0xF1;
|
||||
NR43_REG = 0x11;
|
||||
NR44_REG = 0x80;
|
||||
} else if (d4 == 2) { // Snare
|
||||
NR41_REG = 0x00;
|
||||
NR42_REG = 0xF2;
|
||||
NR43_REG = 0x51;
|
||||
NR44_REG = 0x80;
|
||||
} else if (d4 == 3) { // Hi-Hat
|
||||
NR41_REG = 0x00;
|
||||
NR42_REG = 0x51;
|
||||
NR43_REG = 0x21;
|
||||
NR44_REG = 0x80;
|
||||
}
|
||||
|
||||
// Avanza il tracker
|
||||
tick++;
|
||||
if (tick >= 256) tick = 0; // Loop completo
|
||||
|
||||
frame_counter = FRAMES_PER_TICK;
|
||||
} else {
|
||||
frame_counter--;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef MUSIC_H
|
||||
#define MUSIC_H
|
||||
|
||||
void init_music(void);
|
||||
void update_music(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file rat.c
|
||||
* @brief Implementazione del topo autonomo usando Meta-Sprite (16x8 / 8x16).
|
||||
*/
|
||||
|
||||
#include "rat.h"
|
||||
#include "maze.h"
|
||||
#include <gb/gb.h>
|
||||
#include <rand.h>
|
||||
|
||||
// Dati dei 4 tile per creare i meta-sprite del topo. Usa solo la forma "Giù" ruotata per tutte le direzioni!
|
||||
// Colori: 0=Trasparente (Bianco), 1=Grigio Chiaro (Corpo), 2=Grigio Scuro (Macchie/Naso), 3=Nero (Contorni/Pupille)
|
||||
const unsigned char RatSpriteData[] = {
|
||||
// 0: Destra - Metà Sinistra (Coda/Posteriore) ruotata a partire da Giù
|
||||
0x00, 0x00, 0x07, 0x07, 0x3F, 0x38, 0x5E, 0x71, 0x5B, 0x74, 0x3F, 0x38, 0x07, 0x07, 0x00, 0x00,
|
||||
// 1: Destra - Metà Destra (Testa/Occhi/Naso) ruotata a partire da Giù
|
||||
0x00, 0x00, 0xF0, 0xF0, 0xC8, 0x08, 0xD4, 0x1C, 0xD4, 0x1C, 0xC8, 0x08, 0xF0, 0xF0, 0x00, 0x00,
|
||||
|
||||
// 2: Giù - Metà Superiore (Coda/Schiena)
|
||||
0x00, 0x00, 0x18, 0x18, 0x24, 0x3C, 0x3C, 0x3C, 0x3C, 0x24, 0x6E, 0x52, 0x7E, 0x42, 0x76, 0x4A,
|
||||
// 3: Giù - Metà Inferiore (Occhi/Naso)
|
||||
0x7E, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x5A, 0x5A, 0x24, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static uint8_t rat_x = 1;
|
||||
static uint8_t rat_y = 1;
|
||||
static uint8_t target_x = 1;
|
||||
static uint8_t target_y = 1;
|
||||
static uint8_t pixel_x = 0;
|
||||
static uint8_t pixel_y = 0;
|
||||
static uint8_t current_dir = 255;
|
||||
|
||||
static uint8_t get_opposite(uint8_t dir) {
|
||||
if (dir == 0) return 1;
|
||||
if (dir == 1) return 0;
|
||||
if (dir == 2) return 3;
|
||||
if (dir == 3) return 2;
|
||||
return 255;
|
||||
}
|
||||
|
||||
void init_rat(uint8_t start_x, uint8_t start_y) {
|
||||
rat_x = target_x = start_x;
|
||||
rat_y = target_y = start_y;
|
||||
pixel_x = rat_x * 8;
|
||||
pixel_y = rat_y * 8;
|
||||
current_dir = 255;
|
||||
|
||||
// Carica tutti i 4 tile per le combinazioni 16x8 e 8x16
|
||||
set_sprite_data(0, 4, RatSpriteData);
|
||||
OBP0_REG = 0xE4; // Palette standard
|
||||
|
||||
// Imposta tile di default (Giù)
|
||||
set_sprite_tile(0, 2);
|
||||
set_sprite_tile(1, 3);
|
||||
}
|
||||
|
||||
void update_rat(void) {
|
||||
if (rat_x == target_x && rat_y == target_y) {
|
||||
uint8_t valid_dirs[4];
|
||||
uint8_t num_valid = 0;
|
||||
|
||||
if (rat_y < MAZE_HEIGHT - 1 && maze[rat_y + 1][rat_x] == 0) valid_dirs[num_valid++] = 0;
|
||||
if (rat_y > 0 && maze[rat_y - 1][rat_x] == 0) valid_dirs[num_valid++] = 1;
|
||||
if (rat_x > 0 && maze[rat_y][rat_x - 1] == 0) valid_dirs[num_valid++] = 2;
|
||||
if (rat_x < MAZE_WIDTH - 1 && maze[rat_y][rat_x + 1] == 0) valid_dirs[num_valid++] = 3;
|
||||
|
||||
if (num_valid > 0) {
|
||||
if (num_valid > 1 && current_dir != 255) {
|
||||
uint8_t opp = get_opposite(current_dir);
|
||||
uint8_t filtered[4];
|
||||
uint8_t num_filtered = 0;
|
||||
for (uint8_t i = 0; i < num_valid; i++) {
|
||||
if (valid_dirs[i] != opp) {
|
||||
filtered[num_filtered++] = valid_dirs[i];
|
||||
}
|
||||
}
|
||||
if (num_filtered > 0) current_dir = filtered[rand() % num_filtered];
|
||||
else current_dir = valid_dirs[rand() % num_valid];
|
||||
} else {
|
||||
current_dir = valid_dirs[rand() % num_valid];
|
||||
}
|
||||
|
||||
if (current_dir == 0) target_y++;
|
||||
else if (current_dir == 1) target_y--;
|
||||
else if (current_dir == 2) target_x--;
|
||||
else if (current_dir == 3) target_x++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t target_px = target_x * 8;
|
||||
uint8_t target_py = target_y * 8;
|
||||
|
||||
static uint8_t frame_counter = 0;
|
||||
frame_counter++;
|
||||
if (frame_counter & 1) {
|
||||
if (pixel_x < target_px) pixel_x++;
|
||||
else if (pixel_x > target_px) pixel_x--;
|
||||
if (pixel_y < target_py) pixel_y++;
|
||||
else if (pixel_y > target_py) pixel_y--;
|
||||
}
|
||||
|
||||
if (pixel_x == target_px && pixel_y == target_py) {
|
||||
rat_x = target_x;
|
||||
rat_y = target_y;
|
||||
}
|
||||
|
||||
// Aggiorna gli hardware sprite (Meta-Sprite system)
|
||||
uint8_t base_x = pixel_x + 12;
|
||||
uint8_t base_y = pixel_y + 20;
|
||||
|
||||
if (current_dir == 0 || current_dir == 255) { // Giù (Verticale 8x16)
|
||||
set_sprite_tile(0, 2);
|
||||
set_sprite_tile(1, 3);
|
||||
set_sprite_prop(0, 0);
|
||||
set_sprite_prop(1, 0);
|
||||
move_sprite(0, base_x, base_y - 4);
|
||||
move_sprite(1, base_x, base_y + 4);
|
||||
} else if (current_dir == 1) { // Su (Verticale 8x16, FLIPPATO IN Y)
|
||||
set_sprite_tile(0, 3); // Naso diventa parte alta
|
||||
set_sprite_tile(1, 2); // Coda diventa parte bassa
|
||||
set_sprite_prop(0, S_FLIPY);
|
||||
set_sprite_prop(1, S_FLIPY);
|
||||
move_sprite(0, base_x, base_y - 4);
|
||||
move_sprite(1, base_x, base_y + 4);
|
||||
} else if (current_dir == 2) { // Sinistra (Flippato orizzontalmente per puntare a sinistra)
|
||||
set_sprite_tile(0, 1); // Naso flippato (va a sinistra)
|
||||
set_sprite_tile(1, 0); // Coda flippata (va a destra)
|
||||
set_sprite_prop(0, S_FLIPX);
|
||||
set_sprite_prop(1, S_FLIPX);
|
||||
move_sprite(0, base_x - 4, base_y);
|
||||
move_sprite(1, base_x + 4, base_y);
|
||||
} else if (current_dir == 3) { // Destra (Punta a destra nativamente)
|
||||
set_sprite_tile(0, 0); // Coda (metà sinistra)
|
||||
set_sprite_tile(1, 1); // Naso (metà destra)
|
||||
set_sprite_prop(0, 0);
|
||||
set_sprite_prop(1, 0);
|
||||
move_sprite(0, base_x - 4, base_y);
|
||||
move_sprite(1, base_x + 4, base_y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file rat.h
|
||||
* @brief Header per la gestione del personaggio "ratto" autonomo.
|
||||
*/
|
||||
|
||||
#ifndef RAT_H
|
||||
#define RAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Inizializza il ratto alle coordinate logiche specificate.
|
||||
* @param start_x Coordinata X (in tiles) nel labirinto.
|
||||
* @param start_y Coordinata Y (in tiles) nel labirinto.
|
||||
*/
|
||||
void init_rat(uint8_t start_x, uint8_t start_y);
|
||||
|
||||
/**
|
||||
* @brief Aggiorna la posizione e la logica del ratto.
|
||||
*
|
||||
* Da chiamare ogni frame nel game loop principale.
|
||||
*/
|
||||
void update_rat(void);
|
||||
|
||||
#endif
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @file tiles.c
|
||||
* @brief Implementazione dei dati grafici dei tile.
|
||||
*/
|
||||
|
||||
#include "tiles.h"
|
||||
|
||||
// Definizione dei dati dei tile. Ogni tile su Game Boy è 8x8 pixel.
|
||||
// Ogni riga del tile è rappresentata da 2 byte (planar format).
|
||||
const unsigned char TileData[] = {
|
||||
// Tile 0: Completamente bianco (tutti i bit a 0)
|
||||
// Indica la stanza scavata e i percorsi.
|
||||
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
|
||||
|
||||
// Tile 1: Completamente nero (tutti i bit a 1)
|
||||
// Usato per disegnare i muri del labirinto.
|
||||
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
||||
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @file tiles.h
|
||||
* @brief Definizioni per i dati grafici dei tile.
|
||||
*
|
||||
* Questo file dichiara le risorse grafiche utilizzate dal gioco,
|
||||
* in particolare i tile per disegnare il labirinto e i muri.
|
||||
*/
|
||||
|
||||
#ifndef TILES_H
|
||||
#define TILES_H
|
||||
|
||||
/**
|
||||
* @brief Array contenente i dati grezzi dei tile.
|
||||
*
|
||||
* Formato planare Game Boy (2 bit per pixel, 16 byte per tile).
|
||||
* - Tile 0: Bianco (usato per i percorsi)
|
||||
* - Tile 1: Nero (usato per i muri)
|
||||
*/
|
||||
extern const unsigned char TileData[];
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user