Header

~Articles This Week~

~10 Bombs~

By StarsInDust

 

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.

 

 

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.