fix: Synchronize rat reproduction pause with global tile movement grid to prevent desync
This commit is contained in:
+4
-4
@@ -23,8 +23,8 @@ void init_cursor(void) {
|
||||
// (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);
|
||||
// Usa lo sprite hardware numero 39 (lasciando 0-38 liberi per i ratti)
|
||||
set_sprite_tile(39, 4);
|
||||
}
|
||||
|
||||
void update_cursor(void) {
|
||||
@@ -49,9 +49,9 @@ void update_cursor(void) {
|
||||
// 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
|
||||
move_sprite(39, 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);
|
||||
move_sprite(39, cursor_x * 8 + 12, cursor_y * 8 + 20);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -58,8 +58,8 @@ void main(void) {
|
||||
// Trucco di centraggio (Hardware Scrolling):
|
||||
move_bkg(252, 252);
|
||||
|
||||
// Inizializza il topo autonomo nel punto di partenza (1, 1)
|
||||
init_rat(1, 1);
|
||||
// Inizializza i topi autonomi
|
||||
init_rats();
|
||||
|
||||
// Inizializza il cursore controllato dal giocatore
|
||||
init_cursor();
|
||||
@@ -76,8 +76,8 @@ void main(void) {
|
||||
// Aggiorna la musica in background
|
||||
update_music();
|
||||
|
||||
// Aggiorna l'intelligenza artificiale e l'animazione del topo
|
||||
update_rat();
|
||||
// Aggiorna l'intelligenza artificiale e l'animazione dei topi
|
||||
update_rats();
|
||||
|
||||
// Aggiorna l'input del cursore del giocatore
|
||||
update_cursor();
|
||||
|
||||
+26
@@ -192,8 +192,29 @@ const uint8_t* const drum_parts[4] = { trk_drum_0, trk_drum_1, trk_drum_2, trk_d
|
||||
|
||||
static uint16_t tick = 0;
|
||||
static uint8_t frame_counter = 0;
|
||||
static uint8_t sfx_timer = 0;
|
||||
#define FRAMES_PER_TICK 8 // Velocità del tracker
|
||||
|
||||
void play_sfx_moan(void) {
|
||||
// Gemito: sweep up e vibrato
|
||||
NR10_REG = 0x45;
|
||||
NR11_REG = 0x80;
|
||||
NR12_REG = 0xF3;
|
||||
NR13_REG = 0x40;
|
||||
NR14_REG = 0x86;
|
||||
sfx_timer = 40; // Blocca CH1 per 40 frame
|
||||
}
|
||||
|
||||
void play_sfx_plop(void) {
|
||||
// Plop: tono acuto e brevissimo
|
||||
NR10_REG = 0x00;
|
||||
NR11_REG = 0x80;
|
||||
NR12_REG = 0xF1;
|
||||
NR13_REG = 0xFF;
|
||||
NR14_REG = 0x87;
|
||||
sfx_timer = 15;
|
||||
}
|
||||
|
||||
void init_music(void) {
|
||||
// Reset e attivazione master sound
|
||||
NR52_REG = 0x00;
|
||||
@@ -211,21 +232,26 @@ void init_music(void) {
|
||||
|
||||
tick = 0;
|
||||
frame_counter = 0;
|
||||
sfx_timer = 0;
|
||||
}
|
||||
|
||||
void update_music(void) {
|
||||
if (sfx_timer > 0) sfx_timer--;
|
||||
|
||||
if (frame_counter == 0) {
|
||||
|
||||
uint8_t part = (tick >> 6) & 0x03; // tick / 64
|
||||
uint8_t sub = tick & 0x3F; // tick % 64
|
||||
|
||||
// --- CH1: Arpeggio ---
|
||||
if (sfx_timer == 0) {
|
||||
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];
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
void init_music(void);
|
||||
void update_music(void);
|
||||
|
||||
void play_sfx_moan(void);
|
||||
void play_sfx_plop(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
/**
|
||||
* @file rat.c
|
||||
* @brief Implementazione del topo autonomo usando Meta-Sprite (16x8 / 8x16).
|
||||
*/
|
||||
|
||||
#include "rat.h"
|
||||
#include "maze.h"
|
||||
#include "music.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,
|
||||
@@ -22,13 +16,22 @@ const unsigned char RatSpriteData[] = {
|
||||
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;
|
||||
typedef struct {
|
||||
uint8_t active;
|
||||
uint8_t rat_x;
|
||||
uint8_t rat_y;
|
||||
uint8_t target_x;
|
||||
uint8_t target_y;
|
||||
uint8_t pixel_x;
|
||||
uint8_t pixel_y;
|
||||
uint8_t current_dir;
|
||||
uint8_t sprite_base_idx;
|
||||
uint8_t reproduce_timer;
|
||||
uint8_t cooldown_timer;
|
||||
uint8_t is_mother; // flag per far partorire un solo ratto
|
||||
} Rat;
|
||||
|
||||
static Rat rats[MAX_RATS];
|
||||
|
||||
static uint8_t get_opposite(uint8_t dir) {
|
||||
if (dir == 0) return 1;
|
||||
@@ -38,103 +41,191 @@ static uint8_t get_opposite(uint8_t dir) {
|
||||
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;
|
||||
|
||||
void init_rats(void) {
|
||||
// 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);
|
||||
// Posizioni iniziali garantite vuote (coordinate dispari)
|
||||
uint8_t start_pos[2][2] = {
|
||||
{1, 1},
|
||||
{17, 15}
|
||||
};
|
||||
|
||||
for (uint8_t i = 0; i < MAX_RATS; i++) {
|
||||
rats[i].active = 0;
|
||||
rats[i].reproduce_timer = 0;
|
||||
rats[i].cooldown_timer = 0;
|
||||
rats[i].is_mother = 0;
|
||||
rats[i].sprite_base_idx = i * 2;
|
||||
// Nascondi sprite
|
||||
move_sprite(rats[i].sprite_base_idx, 0, 0);
|
||||
move_sprite(rats[i].sprite_base_idx + 1, 0, 0);
|
||||
}
|
||||
|
||||
// Attiva i primi due topi
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
rats[i].active = 1;
|
||||
rats[i].rat_x = start_pos[i][0];
|
||||
rats[i].rat_y = start_pos[i][1];
|
||||
rats[i].target_x = start_pos[i][0];
|
||||
rats[i].target_y = start_pos[i][1];
|
||||
rats[i].pixel_x = rats[i].rat_x * 8;
|
||||
rats[i].pixel_y = rats[i].rat_y * 8;
|
||||
rats[i].current_dir = 255;
|
||||
}
|
||||
}
|
||||
|
||||
void update_rat(void) {
|
||||
if (rat_x == target_x && rat_y == target_y) {
|
||||
void spawn_rat(uint8_t x, uint8_t y) {
|
||||
for (uint8_t i = 0; i < MAX_RATS; i++) {
|
||||
if (!rats[i].active) {
|
||||
rats[i].active = 1;
|
||||
rats[i].rat_x = x;
|
||||
rats[i].rat_y = y;
|
||||
rats[i].target_x = x;
|
||||
rats[i].target_y = y;
|
||||
rats[i].pixel_x = x * 8;
|
||||
rats[i].pixel_y = y * 8;
|
||||
rats[i].current_dir = 255;
|
||||
rats[i].reproduce_timer = 0;
|
||||
rats[i].cooldown_timer = 120; // Il cucciolo non si riproduce subito
|
||||
rats[i].is_mother = 0;
|
||||
play_sfx_plop(); // Suono di nascita!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_rats(void) {
|
||||
static uint8_t frame_counter = 0;
|
||||
frame_counter++;
|
||||
uint8_t do_move = (frame_counter & 1); // Muovi di 1 pixel ogni 2 frame
|
||||
|
||||
// 1. Controlla collisioni per riproduzione (se sono sulla stessa cella e fermi)
|
||||
for (uint8_t i = 0; i < MAX_RATS; i++) {
|
||||
if (!rats[i].active || rats[i].reproduce_timer > 0 || rats[i].cooldown_timer > 0) continue;
|
||||
for (uint8_t j = i + 1; j < MAX_RATS; j++) {
|
||||
if (!rats[j].active || rats[j].reproduce_timer > 0 || rats[j].cooldown_timer > 0) continue;
|
||||
|
||||
if (rats[i].rat_x == rats[j].rat_x && rats[i].rat_y == rats[j].rat_y &&
|
||||
rats[i].pixel_x == rats[j].pixel_x && rats[i].pixel_y == rats[j].pixel_y) {
|
||||
// Incontro riproduttivo!
|
||||
// Usa 64 frames invece di 60 perché è un multiplo esatto di 16 (il tempo che un topo impiega
|
||||
// per attraversare esattamente un tile di 8 pixel). Così non perdono la sincronia di fase globale!
|
||||
rats[i].reproduce_timer = 64;
|
||||
rats[j].reproduce_timer = 64;
|
||||
rats[i].is_mother = 1; // Solo i farà spawnare il cucciolo
|
||||
rats[j].is_mother = 0;
|
||||
play_sfx_moan(); // Suono d'amore
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Aggiorna lo stato di ogni topo
|
||||
for (uint8_t i = 0; i < MAX_RATS; i++) {
|
||||
Rat* r = &rats[i];
|
||||
if (!r->active) continue;
|
||||
|
||||
if (r->cooldown_timer > 0) {
|
||||
r->cooldown_timer--;
|
||||
}
|
||||
|
||||
// Se si stanno riproducendo, aspetta
|
||||
if (r->reproduce_timer > 0) {
|
||||
r->reproduce_timer--;
|
||||
if (r->reproduce_timer == 0) {
|
||||
// Finito di riprodursi, imposta il cooldown
|
||||
r->cooldown_timer = 120; // 2 secondi di pausa
|
||||
if (r->is_mother) {
|
||||
r->is_mother = 0;
|
||||
spawn_rat(r->rat_x, r->rat_y);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r->rat_x == r->target_x && r->rat_y == r->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 (r->rat_y < MAZE_HEIGHT - 1 && maze[r->rat_y + 1][r->rat_x] == 0) valid_dirs[num_valid++] = 0;
|
||||
if (r->rat_y > 0 && maze[r->rat_y - 1][r->rat_x] == 0) valid_dirs[num_valid++] = 1;
|
||||
if (r->rat_x > 0 && maze[r->rat_y][r->rat_x - 1] == 0) valid_dirs[num_valid++] = 2;
|
||||
if (r->rat_x < MAZE_WIDTH - 1 && maze[r->rat_y][r->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);
|
||||
if (num_valid > 1 && r->current_dir != 255) {
|
||||
uint8_t opp = get_opposite(r->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];
|
||||
for (uint8_t j = 0; j < num_valid; j++) {
|
||||
if (valid_dirs[j] != opp) {
|
||||
filtered[num_filtered++] = valid_dirs[j];
|
||||
}
|
||||
}
|
||||
if (num_filtered > 0) current_dir = filtered[rand() % num_filtered];
|
||||
else current_dir = valid_dirs[rand() % num_valid];
|
||||
if (num_filtered > 0) r->current_dir = filtered[rand() % num_filtered];
|
||||
else r->current_dir = valid_dirs[rand() % num_valid];
|
||||
} else {
|
||||
current_dir = valid_dirs[rand() % num_valid];
|
||||
r->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++;
|
||||
if (r->current_dir == 0) r->target_y++;
|
||||
else if (r->current_dir == 1) r->target_y--;
|
||||
else if (r->current_dir == 2) r->target_x--;
|
||||
else if (r->current_dir == 3) r->target_x++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t target_px = target_x * 8;
|
||||
uint8_t target_py = target_y * 8;
|
||||
uint8_t target_px = r->target_x * 8;
|
||||
uint8_t target_py = r->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 (do_move) {
|
||||
if (r->pixel_x < target_px) r->pixel_x++;
|
||||
else if (r->pixel_x > target_px) r->pixel_x--;
|
||||
if (r->pixel_y < target_py) r->pixel_y++;
|
||||
else if (r->pixel_y > target_py) r->pixel_y--;
|
||||
}
|
||||
|
||||
if (pixel_x == target_px && pixel_y == target_py) {
|
||||
rat_x = target_x;
|
||||
rat_y = target_y;
|
||||
if (r->pixel_x == target_px && r->pixel_y == target_py) {
|
||||
r->rat_x = r->target_x;
|
||||
r->rat_y = r->target_y;
|
||||
}
|
||||
|
||||
// Aggiorna gli hardware sprite (Meta-Sprite system)
|
||||
uint8_t base_x = pixel_x + 12;
|
||||
uint8_t base_y = pixel_y + 20;
|
||||
uint8_t base_x = r->pixel_x + 12;
|
||||
uint8_t base_y = r->pixel_y + 20;
|
||||
uint8_t s0 = r->sprite_base_idx;
|
||||
uint8_t s1 = r->sprite_base_idx + 1;
|
||||
|
||||
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);
|
||||
if (r->current_dir == 0 || r->current_dir == 255) { // Giù
|
||||
set_sprite_tile(s0, 2);
|
||||
set_sprite_tile(s1, 3);
|
||||
set_sprite_prop(s0, 0);
|
||||
set_sprite_prop(s1, 0);
|
||||
move_sprite(s0, base_x, base_y - 4);
|
||||
move_sprite(s1, base_x, base_y + 4);
|
||||
} else if (r->current_dir == 1) { // Su
|
||||
set_sprite_tile(s0, 3);
|
||||
set_sprite_tile(s1, 2);
|
||||
set_sprite_prop(s0, S_FLIPY);
|
||||
set_sprite_prop(s1, S_FLIPY);
|
||||
move_sprite(s0, base_x, base_y - 4);
|
||||
move_sprite(s1, base_x, base_y + 4);
|
||||
} else if (r->current_dir == 2) { // Sinistra
|
||||
set_sprite_tile(s0, 1);
|
||||
set_sprite_tile(s1, 0);
|
||||
set_sprite_prop(s0, S_FLIPX);
|
||||
set_sprite_prop(s1, S_FLIPX);
|
||||
move_sprite(s0, base_x - 4, base_y);
|
||||
move_sprite(s1, base_x + 4, base_y);
|
||||
} else if (r->current_dir == 3) { // Destra
|
||||
set_sprite_tile(s0, 0);
|
||||
set_sprite_tile(s1, 1);
|
||||
set_sprite_prop(s0, 0);
|
||||
set_sprite_prop(s1, 0);
|
||||
move_sprite(s0, base_x - 4, base_y);
|
||||
move_sprite(s1, base_x + 4, base_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
/**
|
||||
* @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);
|
||||
#define MAX_RATS 15
|
||||
|
||||
/**
|
||||
* @brief Aggiorna la posizione e la logica del ratto.
|
||||
*
|
||||
* Da chiamare ogni frame nel game loop principale.
|
||||
*/
|
||||
void update_rat(void);
|
||||
void init_rats(void);
|
||||
void update_rats(void);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user