• Hey! Guest! The 39th GMC Jam will take place between November 26th, 12:00 UTC and November 30th, 12:00 UTC. Why not join in! Click here to find out more!

GML Disabling Diagonal Movement for Enemy AI on a Turn Based Grid

B

Blakester

Guest
Hello all!
Thank you for taking the time to read this.

What I'm trying to do is get the effect of an old school roguelike's turn based grid movement. Where I've hit a stumbling block, though, is that one of the NPCs that follows the player is moving one tile at a time (which is great!), but they move diagonally when I want all the moving objects to only move in 4-directions on the grid.

The way it works is that on each "turn" the player is given 3 Action Points (APs) and each tile he moves spends 1 AP. When they are out of APs, the NPC's turn starts and they are given 1 AP and this loops.

That said, I suppose moving diagonally might be fine (the way I have it worked out is that they just snap to their target location, like in old roguelikes), but they need to lose AP accordingly. If they move diagonally they shouldn't lose one AP; they should lose 2 APs.

Here's the code for the obj_Enemy:

Create Event-

Code:
//Init Variables
ap = 0; //Don't start on Object's Turn
Step Event-

Code:
///Update
//Variables
ts = 10; //Tile Size
tx = obj_Player.x; //Target X
ty = obj_Player.y; //Target Y

//Movement
if !ap == 0
{
    if place_empty(x + sign(tx-x)*ts,y + sign(ty-y)*ts)
    {
        x = x + sign(tx-x)*ts
        y = y + sign(ty-y)*ts
    }
ap -= 1;
    if (ap == 0 && global.player_ap == 0)
    {
        global.player_ap = 3;
    }
}
I've tried using mp_grid_path, but I still can't find a way to track how many APs are spent on each tile; the obj_Enemy just goes immediately on top of the obj_Player. I'm open to any suggestions on pathfinding though, I'm just not too savvy on pathfinding so I'm probably doing it wrong, to be honest. My best results so far are on the code above though.

Thank you in advance, any help would be greatly appreciated! :)
-Blake
 
K

kevins_office

Guest
Your if() place empty statement is allowing for diagonally because you are checking both x/y at once and then updating x/y at once.
Break that up so its only doing one or the other xy at a time, not both at the same time.
If you move one x and one y at the same time, that is diagonal.
 
K

kevins_office

Guest
If you want to leave it diagonal, but want to just minus the AP for it...
Code:
if (x != xprevious) ap--;
if (y != yprevious) ap--;
That way ap is subtracted for each row/column of movement.
If they only move up and down, or side to side, then only x OR y will change so only minus once.
But if they move diagonal then both x AND y changes, minus twice.

However, its possible to minus 2 in one turn/step. What if they only had 1 ap left? Now they will be negative. You will have to put some checks to see if they have enough ap to move diagonal, and deny it, or only allow one part of the move.
 
B

Blakester

Guest
Thanks a bunch!

The code worked when I tried it, but I encountered a small hiccup: the obj_Enemy will meet the obj_Player on the x-axis or the y-axis but never both, so it follows at a distance.

This is because of the "position_empty" bit and I'm trying to get the obj_Enemy to maybe target the tiles around the player rather than the one the obj_Player is on. Is there an efficient way to do this? To change the tx and ty to whichever tile adjacent to the player is closest to obj_Enemy?

I've been trying to achieve something with "distance_to_point" but it keeps getting errors and it's really sloppy.

Is there a better way to go about it?

Thank you again for the help so far though, it's definitely working I just need to iron out some of the kinks :D
 
K

kevins_office

Guest
Not sure if i'm following perfectly, but...
Don't use place empty, just have to enemy go to the players square.
Then at the end of each turn/step after the moving part of the code just check if enemy xy == player xy.

Now if you don't want the enemy to visually occupy the same square as the player, that's fine this will still work.
If xy does match, then do whatever code you want for a hit, then move the enemy one square back.
Since you are doing all of this in the same one step all of the code completes before a graphic frame is rendered, so players will never see them together in the same square.

If you didnt know, just changing the xy in a step doesn't change the sprite position on screen. You can move the xy location 100 times in the same step, and as long as you put it back to where it started before the step is over you would never see it move. It only moves to where xy is set at when the step is over.
 
B

Blakester

Guest
Oh! I see!
You're right, I didn't know about the sprite position being able to change so quickly in a step. Yeah, once I took out the "place_empty" and added a few checks it works like a charm.
Thanks again!
 
K

kevins_office

Guest
Oh! I see!
You're right, I didn't know about the sprite position being able to change so quickly in a step. Yeah, once I took out the "place_empty" and added a few checks it works like a charm.
Thanks again!
You still might be thinking about it wrong. The sprite position doesn't change so quickly in a step.
Think about it this way...
Code:
var newValue = 0;
repeat (100) {
     newValue = irandom(1000);
}
oldValue = newValue;
Now did oldValue change/move 100 times? No, oldValue was assigned whatever the last assigned value newValue had at the end of the loop.
You can change xy in a step as many times as you like, because the sprite is not updated to whatever values xy have until after the step, after all of your code has run.
 
Top