feat: add bomb mechanic with explosion to kill rats
This commit is contained in:
@@ -0,0 +1,80 @@
|
|||||||
|
# Bomb and Explosion Sprites
|
||||||
|
sprites_ascii = {
|
||||||
|
"bomb_3": [
|
||||||
|
" ## ",
|
||||||
|
" #### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" #### ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"bomb_2": [
|
||||||
|
" ",
|
||||||
|
" ## ",
|
||||||
|
" #### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" #### ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"bomb_1": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" ## ",
|
||||||
|
" #### ",
|
||||||
|
" #### ",
|
||||||
|
" ## ",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"exp_center": [
|
||||||
|
"## ##",
|
||||||
|
"# # # #",
|
||||||
|
" #### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" #### ",
|
||||||
|
"# # # #",
|
||||||
|
"## ##"
|
||||||
|
],
|
||||||
|
"exp_horiz": [
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
"########",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
"########",
|
||||||
|
" ",
|
||||||
|
" "
|
||||||
|
],
|
||||||
|
"exp_vert": [
|
||||||
|
" #### ",
|
||||||
|
" #### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" ###### ",
|
||||||
|
" #### ",
|
||||||
|
" #### "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
print("const unsigned char BombSpriteData[] = {")
|
||||||
|
for name, data in sprites_ascii.items():
|
||||||
|
print(f" // {name}")
|
||||||
|
bytes_arr = []
|
||||||
|
for row in data:
|
||||||
|
lb = 0
|
||||||
|
hb = 0
|
||||||
|
for x, char in enumerate(row):
|
||||||
|
if char == '#':
|
||||||
|
lb |= (1 << (7 - x))
|
||||||
|
hb |= (1 << (7 - x))
|
||||||
|
bytes_arr.append(lb)
|
||||||
|
bytes_arr.append(hb)
|
||||||
|
hex_str = ", ".join([f"0x{b:02X}" for b in bytes_arr])
|
||||||
|
print(f" {hex_str},")
|
||||||
|
print("};")
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
#include "bomb.h"
|
||||||
|
#include "maze.h"
|
||||||
|
#include "rat.h"
|
||||||
|
#include "music.h"
|
||||||
|
#include <gb/gb.h>
|
||||||
|
|
||||||
|
extern const unsigned char BombSpriteData[];
|
||||||
|
|
||||||
|
#define BOMB_STATE_INACTIVE 0
|
||||||
|
#define BOMB_STATE_TICKING 1
|
||||||
|
#define BOMB_STATE_EXPLODING 2
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t x;
|
||||||
|
uint8_t y;
|
||||||
|
uint16_t timer;
|
||||||
|
} Bomb;
|
||||||
|
|
||||||
|
static Bomb bomb;
|
||||||
|
|
||||||
|
void init_bombs(void) {
|
||||||
|
bomb.state = BOMB_STATE_INACTIVE;
|
||||||
|
set_sprite_data(5, 6, BombSpriteData);
|
||||||
|
for(uint8_t i = 34; i <= 38; i++) {
|
||||||
|
move_sprite(i, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop_bomb(uint8_t x, uint8_t y) {
|
||||||
|
if (bomb.state == BOMB_STATE_INACTIVE) {
|
||||||
|
bomb.state = BOMB_STATE_TICKING;
|
||||||
|
bomb.x = x;
|
||||||
|
bomb.y = y;
|
||||||
|
bomb.timer = 180; // 3 seconds
|
||||||
|
play_sfx_plop(); // Generic drop sound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hide_explosion() {
|
||||||
|
for(uint8_t i = 34; i <= 38; i++) {
|
||||||
|
move_sprite(i, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_explosion_damage(uint8_t ex, uint8_t ey) {
|
||||||
|
kill_rats_at(ex, ey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_bombs(void) {
|
||||||
|
if (bomb.state == BOMB_STATE_TICKING) {
|
||||||
|
bomb.timer--;
|
||||||
|
|
||||||
|
uint8_t px = bomb.x * 8 + 12;
|
||||||
|
uint8_t py = bomb.y * 8 + 20;
|
||||||
|
|
||||||
|
if (bomb.timer > 120) {
|
||||||
|
set_sprite_tile(38, 5); // bomb_3
|
||||||
|
} else if (bomb.timer > 60) {
|
||||||
|
set_sprite_tile(38, 6); // bomb_2
|
||||||
|
} else {
|
||||||
|
if (bomb.timer & 4) {
|
||||||
|
set_sprite_tile(38, 7); // bomb_1
|
||||||
|
} else {
|
||||||
|
set_sprite_tile(38, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move_sprite(38, px, py);
|
||||||
|
|
||||||
|
if (bomb.timer == 0) {
|
||||||
|
bomb.state = BOMB_STATE_EXPLODING;
|
||||||
|
bomb.timer = 30; // 0.5s explosion
|
||||||
|
|
||||||
|
play_sfx_explosion();
|
||||||
|
|
||||||
|
set_sprite_tile(38, 8);
|
||||||
|
move_sprite(38, px, py);
|
||||||
|
do_explosion_damage(bomb.x, bomb.y);
|
||||||
|
|
||||||
|
if (bomb.y > 0 && maze[bomb.y - 1][bomb.x] == 0) {
|
||||||
|
set_sprite_tile(34, 10);
|
||||||
|
move_sprite(34, px, py - 8);
|
||||||
|
do_explosion_damage(bomb.x, bomb.y - 1);
|
||||||
|
}
|
||||||
|
if (bomb.y < MAZE_HEIGHT - 1 && maze[bomb.y + 1][bomb.x] == 0) {
|
||||||
|
set_sprite_tile(35, 10);
|
||||||
|
move_sprite(35, px, py + 8);
|
||||||
|
do_explosion_damage(bomb.x, bomb.y + 1);
|
||||||
|
}
|
||||||
|
if (bomb.x > 0 && maze[bomb.y][bomb.x - 1] == 0) {
|
||||||
|
set_sprite_tile(36, 9);
|
||||||
|
move_sprite(36, px - 8, py);
|
||||||
|
do_explosion_damage(bomb.x - 1, bomb.y);
|
||||||
|
}
|
||||||
|
if (bomb.x < MAZE_WIDTH - 1 && maze[bomb.y][bomb.x + 1] == 0) {
|
||||||
|
set_sprite_tile(37, 9);
|
||||||
|
move_sprite(37, px + 8, py);
|
||||||
|
do_explosion_damage(bomb.x + 1, bomb.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (bomb.state == BOMB_STATE_EXPLODING) {
|
||||||
|
bomb.timer--;
|
||||||
|
if (bomb.timer == 0) {
|
||||||
|
bomb.state = BOMB_STATE_INACTIVE;
|
||||||
|
hide_explosion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef BOMB_H
|
||||||
|
#define BOMB_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void init_bombs(void);
|
||||||
|
void drop_bomb(uint8_t x, uint8_t y);
|
||||||
|
void update_bombs(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
const unsigned char BombSpriteData[] = {
|
||||||
|
// bomb_3
|
||||||
|
0x18, 0x18, 0x3C, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0x00, 0x00,
|
||||||
|
// bomb_2
|
||||||
|
0x00, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
// bomb_1
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
// exp_center
|
||||||
|
0xC3, 0xC3, 0xA5, 0xA5, 0x3C, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0xA5, 0xA5, 0xC3, 0xC3,
|
||||||
|
// exp_horiz
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7E, 0x7E, 0x7E, 0x7E, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
// exp_vert
|
||||||
|
0x3C, 0x3C, 0x3C, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0x3C, 0x3C,
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
#include "maze.h"
|
#include "maze.h"
|
||||||
#include <gb/gb.h>
|
#include <gb/gb.h>
|
||||||
|
#include "bomb.h"
|
||||||
|
|
||||||
const unsigned char CursorSpriteData[] = {
|
const unsigned char CursorSpriteData[] = {
|
||||||
// Bordo quadrato 8x8 (Nero, colore 3 -> 11)
|
// Bordo quadrato 8x8 (Nero, colore 3 -> 11)
|
||||||
@@ -44,6 +45,11 @@ void update_cursor(void) {
|
|||||||
if (cursor_x < MAZE_WIDTH - 1) cursor_x++;
|
if (cursor_x < MAZE_WIDTH - 1) cursor_x++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tasto A per sganciare la bomba
|
||||||
|
if ((keys & J_A) && !(previous_keys & J_A)) {
|
||||||
|
drop_bomb(cursor_x, cursor_y);
|
||||||
|
}
|
||||||
|
|
||||||
previous_keys = keys;
|
previous_keys = keys;
|
||||||
|
|
||||||
// Effetto lampeggiante: nascondi il cursore per metà del ciclo (ogni 16 frame)
|
// Effetto lampeggiante: nascondi il cursore per metà del ciclo (ogni 16 frame)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "rat.h"
|
#include "rat.h"
|
||||||
#include "music.h"
|
#include "music.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
#include "bomb.h"
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
uint8_t y, x;
|
uint8_t y, x;
|
||||||
@@ -86,6 +87,8 @@ void main(void) {
|
|||||||
// Inizializza il cursore controllato dal giocatore
|
// Inizializza il cursore controllato dal giocatore
|
||||||
init_cursor();
|
init_cursor();
|
||||||
|
|
||||||
|
init_bombs();
|
||||||
|
|
||||||
// Inizializza il sequencer musicale
|
// Inizializza il sequencer musicale
|
||||||
init_music();
|
init_music();
|
||||||
|
|
||||||
@@ -126,6 +129,9 @@ void main(void) {
|
|||||||
// Aggiorna l'input del cursore del giocatore
|
// Aggiorna l'input del cursore del giocatore
|
||||||
update_cursor();
|
update_cursor();
|
||||||
|
|
||||||
|
// Aggiorna le bombe
|
||||||
|
update_bombs();
|
||||||
|
|
||||||
// Attendi la sincronizzazione con il monitor
|
// Attendi la sincronizzazione con il monitor
|
||||||
wait_vbl_done();
|
wait_vbl_done();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -422,3 +422,10 @@ void play_game_over_music(void) {
|
|||||||
|
|
||||||
NR22_REG = 0x00; NR32_REG = 0x00; NR42_REG = 0x00; // Zittisce tutto
|
NR22_REG = 0x00; NR32_REG = 0x00; NR42_REG = 0x00; // Zittisce tutto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void play_sfx_explosion(void) {
|
||||||
|
// Esplosione forte e lunga sul canale 4
|
||||||
|
NR42_REG = 0xF7;
|
||||||
|
NR43_REG = 0x6E;
|
||||||
|
NR44_REG = 0xC0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ void update_music(void);
|
|||||||
|
|
||||||
void play_sfx_moan(void);
|
void play_sfx_moan(void);
|
||||||
void play_sfx_plop(void);
|
void play_sfx_plop(void);
|
||||||
|
void play_sfx_explosion(void);
|
||||||
|
|
||||||
void play_game_over_music(void);
|
void play_game_over_music(void);
|
||||||
|
|
||||||
|
|||||||
@@ -297,3 +297,18 @@ void update_rats(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kill_rats_at(uint8_t x, uint8_t y) {
|
||||||
|
for (uint8_t i = 0; i < MAX_RATS; i++) {
|
||||||
|
if (rats[i].active) {
|
||||||
|
// Uccidi se il topo è esattamente sulla cella esplosa
|
||||||
|
// Oppure se ci stava andando.
|
||||||
|
if ((rats[i].rat_x == x && rats[i].rat_y == y) ||
|
||||||
|
(rats[i].target_x == x && rats[i].target_y == y)) {
|
||||||
|
rats[i].active = 0;
|
||||||
|
move_sprite(rats[i].sprite_base_idx, 0, 0);
|
||||||
|
move_sprite(rats[i].sprite_base_idx + 1, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user