Legacy GM [SOLVED] How do I 'delay' interaction with an object when performing an Action?

Hello, I need some help with this little issue as I feel I've done everything I can.

I have a player object who is able to attack, and I have a door object, which the player can use to move rooms.

This is code in the players End Step Event that interacts with the door object, when pressing Up creates the DoorHamachi object (The object that plays an animation of the character going through and thus deac. the main player obj)
Code:
with (obj_door)
{
if (place_meeting(x,y,objPlayer1)
and objPlayer1.state != 2
and visible = 1)
{
    if (kUp)
    {
    //Deactivate Hamachi
    with (objPlayer1) event_user(0);
  
    instance_destroy(objPlayer1, 0);
 
 

    //Create a door player object
    doorHamachi = instance_create(x+8,y-16,objDoorHamachi)
This works, however whenever the player presses 'X' he attacks by playing an animation and creating an attack hitbox for his weapon. So when doing this over a door object and pressing Up, the game crashes thus giving this error.


I dont necessarily care for the crash, I'd rather have a delay between interactions like, preventing the player from going through the door whilst attacking. Maybe this is a simple fix but I'm very lost on how to achieve that between the two objects. Any help please?
 
Last edited:
If you have two objects interacting in this way, then the one that is checking back to the other needs to know it exists first.

If I'm thinking the error message through correctly: the problem is in objAxe, and it is due to trying to access an object that doesn't exist. Which is presumably the player object - that has been deleted through the door process.

What is object index 135246? You really need to include the code for objAxe, as that is where the error is coming from and without it this is a rough guess :)

1) So you may want to reverse it, and make it so that the door opening process can only happen if an instance of objAxe doesn't exist (assuming that after each attack objAxe destroys itself?)

OR

2) You could destroy objAxe in the destroy event of the player, which would stop objAxe calling an object that no longer exists. However: that wouldn't make it so that the player can't attack and open the door at the same time.

OR

3) You have this there:
Code:
and objPlayer1.state != 2
which is maybe meant to prevent door opening happening while the player is attacking? If so - it's not working, since objAxe is referring to the player after its been destroyed, and you maybe don't want door opening to be active whilst the player is attacking.

Which would mean your control method needs reordering, or it isn't in the 'state' that you think it is.

You may need to mix and match some of 1, 2, and 3 depending on which you prefer - can't open door while attacking / can open door while attacking.
 
Thank you! I'll experiment, but I'll post my hitbox/axe code for now

objAxe
Create
Code:
///Set variables
facing = 1;
attacking = false;
attack = 0;
attack_time = 34;
damage = 2;
knockback = 3;

End Step
Code:
///Hit animation
if (attacking)
{
  
    //Lock parent facing direction
    parent.facing = facing;
    //Create a hitbox at the correct time in the animation
    if (++attack == attack_time*0.5)
    {
        var xo = 15;
        var yo = -7;
        var a = instance_create(parent.x+xo*parent.facing, parent.y+yo, objHitBox);
        a.width = 8;
        a.height = 8;
        a.x_offset = xo;
        a.y_offset = yo;
        a.life = 8;
        a.parent = parent;
        a.stick_to = parent;
        a.damage = damage;
        a.knockback = knockback;
    }
    //Until it reaches the maximum time
    if (attack >= attack_time)
    {
        //At the end of the timer, stop attacking and
        //deactivate the weapon so that other weapons can be
        //be used again.
        attacking = false;
        attack = 0;
        active = false;
        parent.can_move = true;
    
    }
    else
    {
        //Set the sprite of the parent
        var spr = parent.sprAxe;
        parent.sprite_index = spr;
        parent.image_spd = 0;
        parent.image_index = (attack/attack_time)*(sprite_get_number(spr)-1);
    }
}

User Defined 0
Code:
///Press Event
if (!attacking)
{
    attacking = true;
    attack = 0;
    facing = parent.facing;
    parent.can_move = false;
    active = true; //Set to active so that other weapons cannot be
                   //used at the same time
}


HitBox
Create
Code:
///Set variables
width = 4;
height = 4;
life = 5; //number of steps which the hitbox lasts for
parent = noone; //the object which created it < can't collide with this!
stick_to = noone; //stick to an object?
x_offset = 0; //offset for sticking to object < takes stick_to.facing in to consideration
y_offset = 0;
collided = ds_list_create(); //a list of entities already collided with
damage = 0;
knockback = 0;
x1 = 0;
y1 = 0;
x2 = 0;
y2 = 0;

Destroy
Code:
///Clean up
ds_list_destroy(collided);

End Step
Code:
///Stick to parent object
with (stick_to)
{
    other.x = x+(other.x_offset*facing);
    other.y = y+other.y_offset;
}

Code:
///Hit entities - this is where I believe the error to originate

if(!variable_instance_exists(stick_to, "facing")) exit;

///Get a list of entities in collision with the hitbox
x1 = x-width;
y1 = y-height;
x2 = x+width;
y2 = y+height;
var list = collision_rectangle_multiple(x1, y1, x2, y2, objEntity, false, true);
var size = ds_list_size(list);
///Loop through all the collided entities
for (var i = 0; i < size; i++)
{
    var obj = list[|i];
    ///Check if the object is not our parent
    ///and if we haven't already collided with it (in our collided list)
    if (obj != parent)
    if (ds_list_find_index(collided, obj) == -1)
    {
        if (obj.can_damage)
        {
            ///Do damage!
            obj.take_damage += damage;
            obj.damager = parent;
            ///Do knockback!
            var my_x = x-(x_offset*stick_to.facing);
            var my_y = y-y_offset;
            var dir = point_direction(my_x, my_y, mid_x(obj), mid_y(obj));
            with (obj)
            {
                add_speed(other.knockback, dir);
                vsp -= min(1, other.knockback);
            }
        }
        ///Add to list so that we don't collide with it again
        ds_list_add(collided, obj);
    }
}
ds_list_destroy(list);

Code:
///Destroy over time
life--;
if (life <= 0)
{
    instance_destroy();
}

Room End
Code:
///Clean up
ds_list_destroy(collided);

I had a similar problem like this where if the player died and killed an enemy at the same time it would produce an error like so, that was fixed a long time ago.
 
Last edited:
I fixed the error by instead of destroying the player - instance_destroy() I went with instance_deactivate_object() when entering doors, however this creates a new problem.

When deactivating the player object by pressing the Up arrow, the player deactivates. But take the game objects into the next room, I get the same results however the player isn't deactivating.


Whats confusing me is that this isn't the same player object being carried over, each room has a level manager which creates the HUD, Camera, and the player by a spawn point.
Code:
///Create objects
instance_create(0, 0, objTransition);
var h = instance_create(0, 0, objHUD);
var c = instance_create(0, 0, objCamera);
if (instance_exists(objPlayerSpawnPoint))
{
    var p1 = instance_create(objPlayerSpawnPoint.x, objPlayerSpawnPoint.y, objPlayer1);
    h.target = p1;
    c.target = p1;
}
So by removing this when transferring over to the next room, no player is spawned.

Again here is my code that's talking to the door object in the players End Step
Code:
with (obj_door)
{
if (place_meeting(x,y,objPlayer1)
and objPlayer1.state != 2
and visible = 1)
{
    if (kUp)
    {
    //Deactivate Hamachi
    with (objPlayer1) event_user(0);
     instance_deactivate_object(objPlayer);
    instance_deactivate_object(objPlayer1);
    //Create a door Hamachi
    doorHamachi = instance_create(x+8,y-16,objDoorHamachi)

Am I just stupid and missing something obvious? How could I get one thing and then end up with another when they're the same objects being created?
 
Hmmm - that's going to take some checking :) My initial thought is that you don't need to be doing an awful lot of that - but I have to think it through before suggesting an alternative.

For now: the door problem......(pseudo code, with it structured in order of least costly checks and importance)

with (obj_door)
{
is keyboard up pressed?
{
is the player not attacking?
// could easily be a variable set to true / false. Press the attack key, attack = true. Animation ends, attack = false. For animation end use 'animation end event' and check the current sprite index - if its an attack animation then set attack to false, and
// door can now be opened
{
is there a collision? // or other circumstances: objPlayer1.state != 2.....I'm unsure what this is meant to be. visible = 1 etc....why is this needed?
{
//Deactivate Hamachi
with (objPlayer1) event_user(0);
instance_destroy(objPlayer1, 0);
//Create a door player object
doorHamachi = instance_create(x+8,y-16,objDoorHamachi)
}
}
}
}

As for the rest.....to be honest, that seems like a lot of it wouldn't be necessary. Give all objects (that the player can attack) the same parent, and you wouldn't need the ds list or to be referencing objects that may, or may not, exist.

object parent create event:
Code:
was_damaged = false;
In the player objects step event:

Code:
if attack
{
 with (object parent)
{
if !was_damaged
{
// could do point_distance to player to limit this further - so that not every object checks all the conditions, if its nowhere near the player etc
// check for collision
if collision
{
//do whatever: damage taken, knockback etc. Find the child objects type, and do specific responses if required
was_damaged = true; // stops them checking any further, meaning only one hit is registered per attack
}
}
}
}
player objects end animation event:
Code:
// check what is the sprite - is it an attack animation?
if yes
{
with (object parent)
{
was_damaged = false; // resets any children of this parent that took damage previously. Allowing them to be attacked again, when the player next strikes
}
attack = false;
}
The way that's structured is easy to follow, and is controlled by the object upon which it is all dependant. I.E the player. The fewer objects you have interacting with each other, and interacting in less convoluted / inter-dependant ways, the better it is to keep track of them and control the outcome (without crashes because something, somewhere, no longer exists etc)

2 other benefits of using 'with', rather than a ds list, are:
1) 'with' is faster
2) If there are no children present of "object parent" then it won't be called at all. It doesn't matter if none exist in the game, and you still call it in a code block. The code will be skipped without any issue.
 
Last edited:

Yal

šŸ§ *penguin noises*
GMC Elder
If you have a lot of mutually exclusive actions, you should consider using state machines to separate logic instead...
  • "normal" state is for most movement
  • "attack" state is for attacks (you could have different attack states for different attacks if their movement are different - kung fu kicks makes you dash horizontally, axe attacks hit in front of you, etc)
  • "passage" state is for going through doors
  • Climbing, swimming etc are common other states that change your base movement as well
Easiest way to do a state machine is to have a switch statement:
Code:
switch(state){
  case 0://Normal
     //movement code etc
     if(keyboard_check(ord('X')){
        state = 1;//Start attack state
     }
  break;

  case 1://Attack
   //attack code here
  break;

  //and so on
}
 
Top