~10 Bombs~
3/16/2026
At this stage of the game, our bomb throwing seems to be a bit luke warm, lets spice it up. We can add another bomb type, by adding the bomb_thrown
This is my sprite image, I just added a few motion lines
Now make your sprite and your object for this. This should be a familiar process for you now. This bomb object is different from your other items, because it will be doing some special stuff like moving across the screen, as if it is thrown by the hero. There fore instead of being created by either a script or the controller object, it will have its code directly placed inside of the bomb object itself.
The bomb thrown object will have several events associated with it’s object. In fact, we will be adding 5 different types of Events, in total to this element.
- Create Event → Lines about move_speed, direction, alarm
- Step Event → Lines about movement and wall collision
- Alarm 0 Event → Lines about explosion timer
- Draw Event → Lines about draw_self (optional)
- Outside Room Event → instance_destroy()
So, in the obj_bomb_thrown, write this code.
//~~~~~~~~~~~~~~~~~~~~OBJECT BOMB THROWN ~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~ CREATE EVENT ~~~~~~~~~~~~~~~~~~~~~~~~~~
// Set movement speed and direction
move_speed = 8; // How fast the bomb flies
direction = 0; // Will be set when created (0=right, 90=up, 180=left, 270=down)
image_angle = direction; // Rotate sprite to match direction
// Optional: Add a timer before explosion
alarm[0] = 240; // Explodes after 240 steps (4 seconds at 60fps)
// Optional: Visual trail effect
image_alpha = 1;
// Debug
show_debug_message("Bomb created at (" + string(x) + ", " + string(y) + ")");
// ~~~~~~~~~~~~~~~~~~~~~~~~~~ STEP EVENT ~~~~~~~~~~~~~~~~~~~~~~~~~~
// Move the bomb
x += lengthdir_x(move_speed, direction);
y += lengthdir_y(move_speed, direction);
// Check collision with walls
if (place_meeting(x, y, obj_wall_plain) ||
place_meeting(x, y, obj_wall_crackedA) ||
place_meeting(x, y, obj_wall_crackedB)) {
// Hit a wall - explode!
show_debug_message("Bomb hit wall at (" + string(x) + ", " + string(y) + ") - DESTROYING");
// Create explosion effect here (optional)
// instance_create_layer(x, y, "Instances", obj_explosion);
// Destroy the bomb
instance_destroy();
}
// Optional: Check collision with enemies (when you add them)
// var enemy = instance_place(x, y, obj_enemy);
// if (enemy != noone) {
// instance_destroy(enemy);
// instance_destroy();
// }
// ~~~~~~~~~ ALARM 0 EVENT (Optional timer-based explosion) ~~~~~~~~~~~
// Bomb explodes after timer runs out
show_debug_message("Bomb exploded by TIMER at (" + string(x) + ", " + string(y) + ") - DESTROYING");
// Optional: Damage nearby objects
// var explosion_radius = 64;
// var nearby_enemies = ds_list_create();
// collision_circle_list(x, y, explosion_radius, obj_enemy, false, true, nearby_enemies, false);
// for (var i = 0; i < ds_list_size(nearby_enemies); i++) {
// instance_destroy(nearby_enemies[| i]);
// }
// ds_list_destroy(nearby_enemies);
// Create explosion effect (optional)
// instance_create_layer(x, y, "Instances", obj_explosion);
instance_destroy();
// ~~~~~~~~~~ DRAW EVENT (Optional - add visual flair) ~~~~~~~~~~~~~~~~
// Draw the bomb sprite with optional effects
draw_self();
// Optional: Draw a shadow
// draw_sprite_ext(sprite_index, image_index, x + 2, y + 2, image_xscale, image_yscale, image_angle, c_black, 0.3);
// ~~~~~~~~~~~~~~ OUTSIDE ROOM EVENT (Optional) ~~~~~~~~~~~~~~~~~~
// You will need to select ‘Other’ in the event dropdown to find this outside room event
// Destroy if it leaves the room
show_debug_message("Bomb left room - DESTROYING");
instance_destroy();
//~~~~~~~~~~~~~~~~~~~~~~~~~Hero Step Event ~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~ UPDATED HERO STEP EVENT ~~~~~~~~~~~~~~~~~~~~~~~~
// Includes item collection, damage handling, and bomb throwing
// MOVEMENT INPUT (Your existing code)
var move_x = keyboard_check(vk_right) - keyboard_check(vk_left);
var move_y = keyboard_check(vk_down) - keyboard_check(vk_up);
var spd = 4;
var dx = move_x * spd;
var dy = move_y * spd;
// COLLISION CHECK & MOVEMENT (Your existing code)
if (dx != 0 || dy != 0) {
// Check full diagonal move
if (!place_meeting(x + dx, y + dy, obj_wall_plain) &&
!place_meeting(x + dx, y + dy, obj_wall_crackedA) &&
!place_meeting(x + dx, y + dy, obj_wall_crackedB)) {
x += dx;
y += dy;
} else {
// Try horizontal only
if (!place_meeting(x + dx, y, obj_wall_plain) &&
!place_meeting(x + dx, y, obj_wall_crackedA) &&
!place_meeting(x + dx, y, obj_wall_crackedB)) {
x += dx;
}
// Try vertical only
if (!place_meeting(x, y + dy, obj_wall_plain) &&
!place_meeting(x, y + dy, obj_wall_crackedA) &&
!place_meeting(x, y + dy, obj_wall_crackedB)) {
y += dy;
}
}
}
// SPRITE SWITCHING (Your existing code)
if (move_y < 0) {
sprite_index = sprite_hero_back;
} else if (move_x > 0) {
sprite_index = sprite_hero_right;
} else if (move_x < 0) {
sprite_index = sprite_hero_left;
} else {
sprite_index = sprite_hero;
}
// NEW: ITEM COLLECTION
// --- Collect Heart ---
var item_heart = instance_place(x, y, obj_heart);
if (item_heart != noone) {
// Heal 1 heart
global.hearts += 1;
if (global.hearts > global.max_hearts) {
global.hearts = global.max_hearts;
}
show_debug_message("Collected heart! Hearts: " + string(global.hearts) + "/" + string(global.max_hearts));
instance_destroy(item_heart);
// Optional: Play sound effect
// audio_play_sound(snd_collect_heart, 1, false);
}
// --- Collect Apple (Gysahi Greens style) ---
var item_apple = instance_place(x, y, obj_apple);
if (item_apple != noone) {
global.apples += 1;
show_debug_message("Collected apple! Total: " + string(global.apples));
instance_destroy(item_apple);
// Optional: Add special effect (like in Chocobo's Dungeon)
// Could heal 1 heart or give temporary speed boost
// audio_play_sound(snd_collect_apple, 1, false);
}
// --- Collect Bomb ---
var item_bomb = instance_place(x, y, obj_bomb);
if (item_bomb != noone) {
global.bombs_held += 1;
show_debug_message("Picked up bomb! Total bombs: " + string(global.bombs_held));
instance_destroy(item_bomb);
// Optional: Play sound effect
// audio_play_sound(snd_collect_bomb, 1, false);
}
// NEW: BOMB THROWING
// Press SPACE to throw a bomb
if (keyboard_check_pressed(vk_space)) {
if (global.bombs_held > 0) {
global.bombs_held -= 1;
// Determine throw direction based on sprite
var throw_direction = 270; // Default: down
if (sprite_index == sprite_hero_back) {
throw_direction = 90; // Throw up
} else if (sprite_index == sprite_hero_right) {
throw_direction = 0; // Throw right
} else if (sprite_index == sprite_hero_left) {
throw_direction = 180; // Throw left
} else {
throw_direction = 270; // Throw down
}
// Create the bomb projectile slightly ahead of hero
var offset_distance = 32; // Spawn bomb 32 pixels ahead
var bomb_x = x + lengthdir_x(offset_distance, throw_direction);
var bomb_y = y + lengthdir_y(offset_distance, throw_direction);
var bomb = instance_create_layer(bomb_x, bomb_y, "Instances", obj_bomb_thrown);
bomb.direction = throw_direction;
bomb.image_angle = throw_direction;
show_debug_message("Threw bomb! Bombs remaining: " + string(global.bombs_held));
// Optional: Play throw sound
// audio_play_sound(snd_throw_bomb, 1, false);
} else {
show_debug_message("No bombs to throw!");
}
}
// NEW: CHECK FOR HAZARDS (Example: stepping on bomb damages you)
// If you have bomb hazard objects placed in the maze
var hazard_bomb = instance_place(x, y, obj_hazard_bomb);
if (hazard_bomb != noone) {
// Take damage
global.hearts -= 1;
show_debug_message("Hit a bomb! Lost 1 heart. Hearts remaining: " + string(global.hearts));
// Destroy the hazard
instance_destroy(hazard_bomb);
// Check if hero died
if (global.hearts <= 0) {
show_debug_message("Hero died! Resetting to floor start...");
// Teleport back to start
x = global.hero_start_x;
y = global.hero_start_y;
// Reset hearts
global.hearts = 3;
show_debug_message("Respawned at: (" + string(x) + ", " + string(y) + ")");
show_debug_message("Hearts restored to: " + string(global.hearts));
}
// Optional: Play damage sound and show damage animation
// audio_play_sound(snd_take_damage, 1, false);
}
// CHECK FOR EXIT (Your existing code)
if (place_meeting(x, y, obj_door)) {
show_debug_message("Hero reached the dungeon exit!");
// Go to next floor
global.current_floor += 1;
show_debug_message("Advancing to floor " + string(global.current_floor));
//Restart room to generate new maze (prevents infinite loop)
// at this time we have this room restart uncommented out
room_restart();
// Or go to next room - or uncomment this, and comment out above
// room_goto_next();}
Here you see the room_restart() code which is added to the hero step event because it needs to check every second if the hero has hit the door and if the maze needs to be recreated. We have the other option, which we can change to when we create new rooms. Right now we just need a working maze.
Update Code to allow for two types of bombs
We actually have three, objects and 3 sprites for these bombs. We have the object we see when we see a good bomb. That is a gold bomb. We have the object that we see when we see a black bomb, and we have a flying through the air object that we see when we press the space bar. So, even though we have 2 types of bombs, we really have 3 if we want to count the flying through the air bomb as a separate object. And we certainly do want to create a separate object for that, because we could not add the code we wrote above for it, if we didn’t.
Updating Scripts for Using 3 Types of Bombs
Go to the script scr_spawn_items and open it.
Write this code in the Script File.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~ SCRIPT SPAWN ITEMS ~~~~~~~~~~~~~~~~~~~
function spawn_items_in_maze(_mazeStruct, _cellSize) {
show_debug_message("=== SPAWNING ITEMS IN MAZE ===");
var m = _mazeStruct;
var cell_size = _cellSize;
// Item spawn settings
var spawn_chance = 0.15; // 15% chance per cell
var items_spawned = 0;
// Item weights for random selection
var total_weight = 12; // heart(2) + apple(5) + bomb_good(3) + bomb_hazard(2)
// Go through each cell in the maze
for (var i = 1; i < m.w - 1; i++) {
for (var j = 1; j < m.h - 1; j++) {
// Random chance to spawn an item
if (random(1) < spawn_chance) {
// Calculate center of this cell
var item_x = i * cell_size + (cell_size / 2);
var item_y = j * cell_size + (cell_size / 2);
// Make sure not too close to hero or door
var hero = instance_find(object_hero, 0);
var door = instance_find(obj_door, 0);
var too_close = false;
var min_distance = 150;
if (hero != noone && point_distance(item_x, item_y, hero.x, hero.y) < min_distance) {
too_close = true;
}
if (door != noone && point_distance(item_x, item_y, door.x, door.y) < min_distance) {
too_close = true;
}
// Check for wall collision
if (!too_close && !position_meeting(item_x, item_y, obj_wall_plain)) {
// Randomly select which item to spawn based on weights
var roll = irandom(total_weight - 1);
if (roll < 2) {
// Spawn heart (weight 2: 0-1)
instance_create_layer(item_x, item_y, "Instances", obj_heart);
items_spawned++;
show_debug_message("Spawned HEART at cell [" + string(i) + ", " + string(j) + "]");
} else if (roll < 7) {
// Spawn apple (weight 5: 2-6)
instance_create_layer(item_x, item_y, "Instances", obj_apple);
items_spawned++;
show_debug_message("Spawned APPLE at cell [" + string(i) + ", " + string(j) + "]");
} else if (roll < 10) {
// Spawn good bomb pickup (weight 3: 7-9)
instance_create_layer(item_x, item_y, "Instances", obj_bomb);
items_spawned++;
show_debug_message("Spawned BOMB (pickup) at cell [" + string(i) + ", " + string(j) + "]");
} else {
// Spawn hazard bomb (weight 2: 10-11)
instance_create_layer(item_x, item_y, "Instances", obj_hazard_bomb);
items_spawned++;
show_debug_message("Spawned HAZARD BOMB at cell [" + string(i) + ", " + string(j) + "]");
}
}
}
}
}
show_debug_message("Total items spawned: " + string(items_spawned));
show_debug_message("=== ITEM SPAWNING COMPLETE ===");
}
// FUNCTION: Spawn Guaranteed Items (Exact Count Spawning)
// Use this if you want guaranteed item counts per floor
function spawn_guaranteed_items(_mazeStruct, _cellSize, _hearts, _apples, _bombs, _hazards) {
show_debug_message("=== SPAWNING GUARANTEED ITEMS ===");
show_debug_message("Hearts: " + string(_hearts) + ", Apples: " + string(_apples) + ", Bombs: " + string(_bombs) + ", Hazards: " + string(_hazards));
var m = _mazeStruct;
var cell_size = _cellSize;
// Create list of available spawn positions
var spawn_positions = ds_list_create();
// Collect all valid spawn positions
for (var i = 1; i < m.w - 1; i++) {
for (var j = 1; j < m.h - 1; j++) {
var pos_x = i * cell_size + (cell_size / 2);
var pos_y = j * cell_size + (cell_size / 2);
// Check if position is valid (not wall, not too close to hero/door)
var hero = instance_find(object_hero, 0);
var door = instance_find(obj_door, 0);
var too_close = false;
var min_distance = 150;
if (hero != noone && point_distance(pos_x, pos_y, hero.x, hero.y) < min_distance) {
too_close = true;
}
if (door != noone && point_distance(pos_x, pos_y, door.x, door.y) < min_distance) {
too_close = true;
}
if (!too_close && !position_meeting(pos_x, pos_y, obj_wall_plain)) {
// Add position to list
ds_list_add(spawn_positions, [pos_x, pos_y, i, j]);
}
}
}
show_debug_message("Found " + string(ds_list_size(spawn_positions)) + " valid spawn positions");
// Shuffle the list
ds_list_shuffle(spawn_positions);
var items_placed = 0;
// Spawn hearts
for (var h = 0; h < _hearts && items_placed < ds_list_size(spawn_positions); h++) {
var pos = spawn_positions[| items_placed];
instance_create_layer(pos[0], pos[1], "Instances", obj_heart);
show_debug_message("Placed HEART at cell [" + string(pos[2]) + ", " + string(pos[3]) + "]");
items_placed++;
}
// Spawn apples
for (var a = 0; a < _apples && items_placed < ds_list_size(spawn_positions); a++) {
var pos = spawn_positions[| items_placed];
instance_create_layer(pos[0], pos[1], "Instances", obj_apple);
show_debug_message("Placed APPLE at cell [" + string(pos[2]) + ", " + string(pos[3]) + "]");
items_placed++;
}
// Spawn good bomb pickups (gold bombs)
for (var b = 0; b < _bombs && items_placed < ds_list_size(spawn_positions); b++) {
var pos = spawn_positions[| items_placed];
instance_create_layer(pos[0], pos[1], "Instances", obj_bomb);
show_debug_message("Placed BOMB (pickup) at cell [" + string(pos[2]) + ", " + string(pos[3]) + "]");
items_placed++;
}
// Spawn hazard bombs (black bombs)
for (var z = 0; z < _hazards && items_placed < ds_list_size(spawn_positions); z++) {
var pos = spawn_positions[| items_placed];
instance_create_layer(pos[0], pos[1], "Instances", obj_hazard_bomb);
show_debug_message("Placed HAZARD BOMB at cell [" + string(pos[2]) + ", " + string(pos[3]) + "]");
items_placed++;
}
ds_list_destroy(spawn_positions);
show_debug_message("Total items placed: " + string(items_placed));
show_debug_message("=== GUARANTEED ITEM SPAWNING COMPLETE ===");
}
The end of the Item spawning code
//~~~~~~~~~~~~~~~~~~~~~~ The Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now we want to also make some changes for the controller object. Go to the object Controller and change this code in the Create Event
show_debug_message("=== INITIALIZING ITEM SYSTEM ===");
global.hearts = 3; // Starting lives
global.max_hearts = 5; // Maximum hearts
global.apples = 0; // Gysahi Greens counter
global.bombs_held = 0; // Bombs in inventory
global.current_floor = 1; // Current dungeon floor
global.hero_start_x = 0; // Will be saved after spawn
global.hero_start_y = 0; // Will be saved after spawn
show_debug_message("Hearts: " + string(global.hearts) + "/" + string(global.max_hearts));
show_debug_message("Floor: " + string(global.current_floor));
show_debug_message("=== ITEM SYSTEM READY ===");
// STEP 2: GENERATE MAZE (Your existing code)
var target_width = 1280;
var target_height = 704;
var cell_size = 128;
var maze_width = floor(target_width / cell_size);
var maze_height = floor(target_height / cell_size);
show_debug_message("=== CREATING MAZE FOR NEW ROOM SIZE ===");
show_debug_message("Room dimensions: " + string(target_width) + " x " + string(target_height));
show_debug_message("Creating maze: " + string(maze_width) + " x " + string(maze_height) + " cells");
show_debug_message("Cell size: " + string(cell_size) + "px");
show_debug_message("Wall size: 32px");
var time_seed = get_timer();
random_set_seed(time_seed);
show_debug_message("Random seed set to: " + string(time_seed));
show_debug_message("Testing random values: " + string(irandom(100)) + ", " + string(irandom(100)) + ", " + string(irandom(100)));
// Create and generate the maze
global.myMaze = new maze("recursive_backtrack", maze_width, maze_height);
maze_generate(global.myMaze);
// Spawn the maze walls
spawn_maze_walls(global.myMaze, cell_size, 0, 0);
// Spawn hero and door
spawn_object_in_opening(global.myMaze.heroWallSide, global.myMaze.heroPosition, cell_size, global.myMaze.w, global.myMaze.h, object_hero, "Hero");
spawn_object_in_opening(global.myMaze.doorWallSide, global.myMaze.doorPosition, cell_size, global.myMaze.w, global.myMaze.h, obj_door, "Door");
show_debug_message("=== MAZE SETUP COMPLETE ===");
// STEP 3: SAVE HERO STARTING POSITION
show_debug_message("=== SAVING HERO START POSITION ===");
var hero = instance_find(object_hero, 0);
if (hero != noone) {
global.hero_start_x = hero.x;
global.hero_start_y = hero.y;
show_debug_message("Hero start position saved: (" + string(global.hero_start_x) + ", " + string(global.hero_start_y) + ")");
} else {
show_debug_message("WARNING: Could not find hero to save position!");
}
// STEP 4: SPAWN ITEMS IN MAZE
// Balanced spawning: 1-2 of each item type
// Random amount between 1-2 for each item
var hearts_to_spawn = irandom_range(1, 2);
var apples_to_spawn = irandom_range(1, 2);
var bombs_to_spawn = irandom_range(1, 2);
var hazards_to_spawn = irandom_range(0, 2); // 0-2 hazard bombs
spawn_guaranteed_items(global.myMaze, cell_size, hearts_to_spawn, apples_to_spawn, bombs_to_spawn, hazards_to_spawn);
// Alternative methods (comment out the above and uncomment one of these):
// OPTION 1: Pure random spawning (adjust spawn_chance in function) Right now the first one is uncommented out.
spawn_items_in_maze(global.myMaze, cell_size);
// OPTION 2: Fixed exact counts (hearts, apples, good bombs, hazard bombs)
// spawn_guaranteed_items(global.myMaze, cell_size, 2, 2, 2, 1);
show_debug_message("=== DUNGEON FLOOR " + string(global.current_floor) + " READY ===");
Well, that is it for our bombs. In our next section, we will be taking a look at how we can show messages on the screen for the player to see.