Browse Source

feat: add bomb mechanic with explosion to kill rats

master
Matteo Benedetto 3 days ago
parent
commit
2407115a79
  1. 80
      make_bomb_sprites.py
  2. 109
      src/bomb.c
  3. 10
      src/bomb.h
  4. 14
      src/bomb_gfx.c
  5. 6
      src/cursor.c
  6. 6
      src/main.c
  7. 7
      src/music.c
  8. 1
      src/music.h
  9. 15
      src/rat.c
  10. 1
      src/rat.h

80
make_bomb_sprites.py

@ -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
src/bomb.c

@ -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
src/bomb.h

@ -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

14
src/bomb_gfx.c

@ -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,
};

6
src/cursor.c

@ -1,6 +1,7 @@
#include "cursor.h"
#include "maze.h"
#include <gb/gb.h>
#include "bomb.h"
const unsigned char CursorSpriteData[] = {
// Bordo quadrato 8x8 (Nero, colore 3 -> 11)
@ -44,6 +45,11 @@ void update_cursor(void) {
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;
// Effetto lampeggiante: nascondi il cursore per metà del ciclo (ogni 16 frame)

6
src/main.c

@ -20,6 +20,7 @@
#include "rat.h"
#include "music.h"
#include "cursor.h"
#include "bomb.h"
void main(void) {
uint8_t y, x;
@ -86,6 +87,8 @@ void main(void) {
// Inizializza il cursore controllato dal giocatore
init_cursor();
init_bombs();
// Inizializza il sequencer musicale
init_music();
@ -126,6 +129,9 @@ void main(void) {
// Aggiorna l'input del cursore del giocatore
update_cursor();
// Aggiorna le bombe
update_bombs();
// Attendi la sincronizzazione con il monitor
wait_vbl_done();
}

7
src/music.c

@ -422,3 +422,10 @@ void play_game_over_music(void) {
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;
}

1
src/music.h

@ -6,6 +6,7 @@ void update_music(void);
void play_sfx_moan(void);
void play_sfx_plop(void);
void play_sfx_explosion(void);
void play_game_over_music(void);

15
src/rat.c

@ -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);
}
}
}
}

1
src/rat.h

@ -9,5 +9,6 @@ extern uint8_t game_over_flag;
void init_rats(void);
void update_rats(void);
void kill_rats_at(uint8_t x, uint8_t y);
#endif

Loading…
Cancel
Save