Aggiungi salto rischioso: stamina < 60 = rischio caduta nel vuoto

Ultima meccanica di gameplay: se hai meno di 60 stamina puoi comunque
tentare il salto, ma rischi di cadere nell'abisso. La probabilita' di
caduta e' proporzionale a quanto stamina ti manca da 60:
- 60 stamina: 0% caduta (salto sicuro)
- 30 stamina: 50% caduta
- 0 stamina: 100% caduta

Se cadi: il giocatore salta verso la cella intermedia (vuoto) invece di
quella di atterraggio, poi cade velocemente verso il basso (animazione
fall_offset, 2x pixel per frame) e game over con la schermata di morte.

Differenza chiave: il salto sicuro atterra a distanza 2; il salto fallito
si ferma a distanza 1 (sopra l'abisso) e cade.
This commit is contained in:
2026-06-26 12:49:08 +02:00
parent 768429a7f9
commit 30111d6316
5 changed files with 50 additions and 5 deletions
+6
View File
@@ -76,6 +76,7 @@ void engine_init(void) {
// Mostra le istruzioni SOLO la prima volta che si gioca il livello 1 // Mostra le istruzioni SOLO la prima volta che si gioca il livello 1
if (level == 1 && !hint_shown_once) hint_active = 1; if (level == 1 && !hint_shown_once) hint_active = 1;
descend_offset = 0; // reset animazione discesa descend_offset = 0; // reset animazione discesa
fall_offset = 0; // reset animazione caduta
// Carica il font testuale di sistema (IBM) // Carica il font testuale di sistema (IBM)
font_init(); font_init();
@@ -262,6 +263,11 @@ void engine_update(uint8_t keys, uint8_t prev_keys) {
descend_offset++; descend_offset++;
update_player_sprite(); // aggiorna lo sprite che scende update_player_sprite(); // aggiorna lo sprite che scende
} }
// Animazione di caduta nel vuoto (sconfitta per salto fallito)
if (game_over == 1 && fall_offset > 0 && fall_offset < 32) {
fall_offset++;
update_player_sprite(); // il giocatore cade
}
// Quando scade il timer drammatico... // Quando scade il timer drammatico...
if (game_over_timer == 0) { if (game_over_timer == 0) {
if (game_over == 1) { if (game_over == 1) {
+1
View File
@@ -49,5 +49,6 @@ volatile uint8_t hint_active = 0;
volatile uint8_t hint_displayed = 0; volatile uint8_t hint_displayed = 0;
volatile uint8_t hint_shown_once = 0; volatile uint8_t hint_shown_once = 0;
volatile uint8_t descend_offset = 0; volatile uint8_t descend_offset = 0;
volatile uint8_t fall_offset = 0;
volatile uint8_t credits_active = 0; volatile uint8_t credits_active = 0;
volatile uint8_t intro_active = 0; volatile uint8_t intro_active = 0;
+5
View File
@@ -90,6 +90,11 @@ extern volatile uint8_t hint_shown_once;
// (aumenta di 1 ogni frame; a 32 il giocatore e' sparito del tutto) // (aumenta di 1 ogni frame; a 32 il giocatore e' sparito del tutto)
extern volatile uint8_t descend_offset; extern volatile uint8_t descend_offset;
// --- Animazione caduta nel vuoto ---
// fall_offset: 0 = normale, >0 = il giocatore sta cadendo nell'abisso
// (aumenta di 1 ogni frame; il giocatore scende e scompare)
extern volatile uint8_t fall_offset;
// --- Credits Screen --- // --- Credits Screen ---
// credits_active: 1 = mostra la schermata crediti (SELECT nel menu titolo) // credits_active: 1 = mostra la schermata crediti (SELECT nel menu titolo)
extern volatile uint8_t credits_active; extern volatile uint8_t credits_active;
+33 -4
View File
@@ -76,6 +76,13 @@ void update_player_movement(uint8_t keys, uint8_t prev_keys) {
player_lx = target_lx; player_lx = target_lx;
player_ly = target_ly; player_ly = target_ly;
// Se stava cadendo nel vuoto, game over con animazione
if (fall_offset > 0) {
game_over = 1; // sconfitta
game_over_timer = 30; // timer per animazione di caduta
return;
}
// Snap matematico alla destinazione per prevenire derive dovute ad arrotondamenti // Snap matematico alla destinazione per prevenire derive dovute ad arrotondamenti
int16_t final_px = (player_lx - player_ly) * 16 + 96; int16_t final_px = (player_lx - player_ly) * 16 + 96;
int16_t final_py = (player_lx + player_ly) * 8 + 16; int16_t final_py = (player_lx + player_ly) * 8 + 16;
@@ -150,16 +157,32 @@ void update_player_movement(uint8_t keys, uint8_t prev_keys) {
// a) La cella intermedia DEVE essere un muro (maze == 0) // a) La cella intermedia DEVE essere un muro (maze == 0)
// b) La cella di arrivo DEVE essere un pavimento o vittoria (maze == 1 o 2) // b) La cella di arrivo DEVE essere un pavimento o vittoria (maze == 1 o 2)
// c) Devi avere abbastanza stamina (>= 60) // c) Devi avere abbastanza stamina (>= 60)
if (maze[player_ly + move_ly][player_lx + move_lx] == 0 && (maze[land_ly][land_lx] == 1 || maze[land_ly][land_lx] == 2) && stamina >= 60) { if (maze[player_ly + move_ly][player_lx + move_lx] == 0 && (maze[land_ly][land_lx] == 1 || maze[land_ly][land_lx] == 2)) {
// Il salto costa 60 stamina. Se ne hai meno, puoi tentare comunque
// ma rischi di cadere nel vuoto. La probabilita' di caduta e'
// proporzionale a quanti punti ti mancano da 60.
// (es: 30 stamina -> 50% caduta; 0 stamina -> 100% caduta)
uint8_t fall_chance = 0;
if (stamina < 60) {
fall_chance = (uint8_t)(((60 - stamina) * 255) / 60);
}
uint8_t roll = (uint8_t)rand();
uint8_t will_fall = (roll < fall_chance);
is_moving = 1; is_moving = 1;
is_jumping = 1; // Questo attiva l'arco parabolico in render.c is_jumping = 1;
move_progress = 0; move_progress = 0;
// Consuma tutta la stamina disponibile (min 60 se possibile)
if (stamina >= 60) {
stamina -= 60; stamina -= 60;
} else {
stamina = 0;
}
start_lx = player_lx; start_lx = player_lx;
start_ly = player_ly; start_ly = player_ly;
target_lx = land_lx; target_lx = will_fall ? (player_lx + move_lx) : land_lx;
target_ly = land_ly; target_ly = will_fall ? (player_ly + move_ly) : land_ly;
start_px = (start_lx - start_ly) * 16 + 96; start_px = (start_lx - start_ly) * 16 + 96;
start_py = (start_lx + start_ly) * 8 + 16; start_py = (start_lx + start_ly) * 8 + 16;
@@ -175,6 +198,12 @@ void update_player_movement(uint8_t keys, uint8_t prev_keys) {
update_stamina_display(); update_stamina_display();
update_player_sprite(); update_player_sprite();
// Se cade nel vuoto, attiva l'animazione di caduta
if (will_fall) {
fall_offset = 1;
// Il giocatore cadra' nell'abisso: game_over dopo l'animazione
}
return; return;
} }
} }
+4
View File
@@ -300,6 +300,10 @@ void update_player_sprite(void) {
if (descend_offset > 0) { if (descend_offset > 0) {
y_offset = (uint8_t)(-(int8_t)(descend_offset >> 1)); // discesa graduale y_offset = (uint8_t)(-(int8_t)(descend_offset >> 1)); // discesa graduale
} }
// Animazione caduta nel vuoto: il giocatore cade velocemente verso il basso
if (fall_offset > 0) {
y_offset = (uint8_t)(-(int8_t)(fall_offset << 1)); // caduta rapida (2x pixel)
}
// move_metasprite e' fornito da png2asset e posiziona multipli sprite hardware insieme // move_metasprite e' fornito da png2asset e posiziona multipli sprite hardware insieme
// Il giocatore e' sempre fisso al centro dello schermo (88, 88). E' il mondo (sfondo) a muoversi. // Il giocatore e' sempre fisso al centro dello schermo (88, 88). E' il mondo (sfondo) a muoversi.