# GMS 2 get location of point in max range (grid based)

Discussion in 'Programming' started by trentallain, Jun 29, 2019.

1. ### trentallainMember

Joined:
Aug 6, 2016
Posts:
521
Okay so I got the AI working thanks to the kind people in my other post, however I have a new issue now. What my AI is currently doing, is moving next to the closest opposing unit. What I want to do is make it move so that that the AI unit is attacking from it's furthest attack range distance, as this makes more sense.

Obviously if the AI unit's attack range is only 1, then it can keep moving up to the target, however I don't know how to do it if the range is greater than that. The unit's can not move diagonally.

Here's a diagram I made (with made up symbols): The red square is the AI unit. The blue square is it's target that it wants to attack. The pink cross is the location I want to know how to find. The dotted line is just an example path of how to get there. The green squares are the locations that are within range at the point I want to find.

How do I find the ideal location (pink cross's location) so that the AI unit is moving to it's maximum range that it can attack from? Also, the AI unit could be anywhere on the grid, not just the location in the diagram.

Not sure on how to approach this.

2. ### Turkish CoffeeMember

Joined:
Nov 25, 2016
Posts:
559
I would just use
Code:
```if (point_distance(...) =< MaxRange)
{
//execute attack
}
```
maybe thats what you want? or you want grid based checking?

Edit: you can draw the range to see it in game with "draw_circle(x,y,MaxRange,true);" in a Draw Event

3. ### trentallainMember

Joined:
Aug 6, 2016
Posts:
521
Oh oops, grid based
Edit: And I need the location to move to
Edit 2: and if it is already in range, it should move away from its target (so that the target is in range but it is as far away as it can go)

Joined:
Aug 6, 2016
Posts:
521
Bump

5. ### RefresherTowelMember

Joined:
Jul 13, 2016
Posts:
1,175
Grid based point_distance should still work fine. Instead of pixels, imagine the input to the point_direction as grid squares. Just make sure your max range is based off of the grid distance, not the pixel distance.
Code:
```if (point_distance(grid_x,grid_y,target_grid_x,target_grid_y) >= max_range) {
// movement here
}```

trentallain likes this.
6. ### trentallainMember

Joined:
Aug 6, 2016
Posts:
521
That just finds whether something is in range. How can I find a position that is the furthest square away from a target and within its own attack range?

7. ### JackTurboMember

Joined:
Oct 19, 2016
Posts:
821
Simplest approach would be to path find directly to the enemy performing a rage check after each square of movement and stop as soon as you are in range

trentallain likes this.
8. ### trentallainMember

Joined:
Aug 6, 2016
Posts:
521
Thanks, that concept is easier than what I was thinking!
Here's what I came up with to try and do this, however none of the units move.

Key words:
MP = movement points (who many squares you can move)
Range = the radius that you can attack from

Here's code:
Code:
```// If there are AI units to be controlled
if ds_priority_size(ai_unit_priority) > 0 {
// Get the AI unit that is the closest to the player
var _ai_unit = ds_priority_find_min(ai_unit_priority);
// If AI can input
switch (ai_state) {
case menu.inputting:
// Create AI mp grid
var _grid_w = ds_grid_width(global.grid);
var _grid_h = ds_grid_height(global.grid);
var _ai_mp_grid = mp_grid_create(0,0,_grid_w,_grid_h,grid.size,grid.size);
// Find closest player unit
var _ai_target = ai_get_closest(_ai_unit.x,_ai_unit.y);
// If the unit isn't trying to move to it's current cell
if _ai_unit.grid_x != _ai_target.grid_x && _ai_unit.grid_y != _ai_target.grid_y {
// Convert global.grid to an mp grid
for (var u = 0; u < _grid_w; u++) {
for (var v = 0; v < _grid_h; v++) {
// If the grid position isn't free
if global.grid[# u, v] != -1 {
// Set that position as taken in the mp grid
mp_grid_add_cell(_ai_mp_grid, u, v);
}
}
}
// Clear starting position and clear target position
mp_grid_clear_cell(_ai_mp_grid,_ai_unit.grid_x,_ai_unit.grid_y);
mp_grid_clear_cell(_ai_mp_grid,_ai_target.grid_x,_ai_target.grid_y);
// If there is a path
with (_ai_unit) {
// Tell the unit that AI is controlling it
ai_selected = id;
// Tell the unit its target
ai_target = _ai_target;
// Clear path points
path_clear_points(ai_path);
// Create the path regardless if you can fully traverse it
mp_grid_path(_ai_mp_grid, ai_path, grid_x * grid.size + 10, grid_y * grid.size + 10, _ai_target.grid_x * grid.size + 10, _ai_target.grid_y * grid.size + 10, false)
// Delete end point of the path (because this is where the target is)
path_delete_point(ai_path, path_get_number(ai_path) - 1);
// Delete points that are either exceeding MP or are blocked
var _path_end = -1;
// Find where/if the path breaks
for (var i = 0; i < path_get_number(ai_path); ++i) {
// Get path's grid coordinates at this position
var _xpos = path_get_x(ai_path, i) div grid.size;
var _ypos = path_get_y(ai_path, i) div grid.size;
// Check if target is in range yet
var _in_range = point_distance(grid_x, grid_y, ai_target.x, ai_target.y) < range;
// If the path is larger than MP, or the position is taken on the grid, or the target is now within range
if path_get_number(ai_path) > mp || global.grid[# _xpos, _ypos] != -1 || _in_range {
// Set this point as the point to end the path at
_path_end = i;
break;
}
}
// If the path has broken somewhere
if _path_end != -1 {
for (var i = _path_end; i < path_get_number(ai_path); ++i) {
// Delete all remaining points
path_delete_point(ai_path, i);
}
}
other.ai_state = menu.paused;
// Update grid coordinates
global.grid[# grid_x, grid_y] = -1;
// Get the end point of the path in grid coordinates
var _end_x = path_get_point_x(ai_path, path_get_number(ai_path) - 1) div grid.size;
var _end_y = path_get_point_y(ai_path, path_get_number(ai_path) - 1) div grid.size;
global.grid[# _end_x, _end_y] = id;
grid_x = _end_x;
grid_y = _end_y;
// Follow the path
path_start(ai_path, 3, path_action_stop, false);
}
}
// Clean up
mp_grid_destroy(_ai_mp_grid);
ds_priority_delete_min(ai_unit_priority);
break;
}
}
else {
if ai_state == menu.inputting {
// Change turns and clean up
global.turn = "ally";
global.menu_state = menu.inputting;
global.created_ai_controller = false;
ds_priority_destroy(ai_unit_priority);
instance_destroy();
}
}
```
And here's the original code that worked, but only moved the units without taking into account MP, Range, or getting as close as possible even if a target unit is surrounded already
Code:
```// If there are AI units to be controlled
if ds_priority_size(ai_unit_priority) > 0 {
// Get the AI unit that is the closest to the player
var _ai_unit = ds_priority_find_min(ai_unit_priority);
// If AI can input
switch (ai_state) {
case menu.inputting:
// Create AI mp grid
var _grid_w = ds_grid_width(global.grid);
var _grid_h = ds_grid_height(global.grid);
var _ai_mp_grid = mp_grid_create(0,0,_grid_w,_grid_h,grid.size,grid.size);
// Find closest player unit
var _ai_target = ai_get_closest(_ai_unit.x,_ai_unit.y);
// If the unit isn't trying to move to it's current cell
if _ai_unit.grid_x != _ai_target.grid_x && _ai_unit.grid_y != _ai_target.grid_y {
// Convert global.grid to an mp grid
for (var u = 0; u < _grid_w; u++) {
for (var v = 0; v < _grid_h; v++) {
// If the grid position isn't free
if global.grid[# u, v] != -1 {
// Set that position as taken in the mp grid
mp_grid_add_cell(_ai_mp_grid, u, v);
}
}
}
// Clear starting position and add target position
mp_grid_clear_cell(_ai_mp_grid,_ai_unit.grid_x,_ai_unit.grid_y);
mp_grid_clear_cell(_ai_mp_grid,_ai_target.grid_x,_ai_target.grid_y);
// If there is a path
with (_ai_unit) {
// Tell the unit that AI is controlling it
ai_selected = id;
// Tell the unit its target
ai_target = _ai_target;
// Clear path points
path_clear_points(ai_path);
// If there is a path
if mp_grid_path(_ai_mp_grid, ai_path, grid_x * grid.size + 10, grid_y * grid.size + 10, _ai_target.grid_x * grid.size + 10, _ai_target.grid_y * grid.size + 10, false) {
// Delete end point of the path (because this is where the target is)
path_delete_point(ai_path, path_get_number(ai_path) - 1);
other.ai_state = menu.paused;
// Update grid coordinates
global.grid[# grid_x, grid_y] = -1;
// Get the end point of the path in grid coordinates
var _end_x = path_get_point_x(ai_path, path_get_number(ai_path) - 1) div grid.size;
var _end_y = path_get_point_y(ai_path, path_get_number(ai_path) - 1) div grid.size;
global.grid[# _end_x, _end_y] = character;
grid_x = _end_x;
grid_y = _end_y;
// Follow the path
path_start(ai_path, 3, path_action_stop, false);
}
}
}
// Clean up
mp_grid_destroy(_ai_mp_grid);
ds_priority_delete_min(ai_unit_priority);
break;
}
}
else {
if ai_state == menu.inputting {
// Change turns and clean up
global.turn = "ally";
global.menu_state = menu.inputting;
global.created_ai_controller = false;
ds_priority_destroy(ai_unit_priority);
instance_destroy();
}
}
```

Joined:
Aug 6, 2016
Posts:
521
Bump

10. ### trentallainMember

Joined:
Aug 6, 2016
Posts:
521
I realised that I was using path_get_x instead of path_get_point_x for one part, however still can't find why nothing is moving.

Code:
```// If there are AI units to be controlled
if ds_priority_size(ai_unit_priority) > 0 {
// Get the AI unit that is the closest to the player
var _ai_unit = ds_priority_find_min(ai_unit_priority);
// If AI can input
switch (ai_state) {
case menu.inputting:
// Create AI mp grid
var _grid_w = ds_grid_width(global.grid);
var _grid_h = ds_grid_height(global.grid);
var _ai_mp_grid = mp_grid_create(0,0,_grid_w,_grid_h,grid.size,grid.size);
// Find closest player unit
var _ai_target = ai_get_closest(_ai_unit.x,_ai_unit.y);
// If the unit isn't trying to move to it's current cell
if _ai_unit.grid_x != _ai_target.grid_x && _ai_unit.grid_y != _ai_target.grid_y {
// Convert global.grid to an mp grid
for (var u = 0; u < _grid_w; u++) {
for (var v = 0; v < _grid_h; v++) {
// If the grid position isn't free
if global.grid[# u, v] != -1 {
// Set that position as taken in the mp grid
mp_grid_add_cell(_ai_mp_grid, u, v);
}
}
}
// Clear starting position and clear target position
mp_grid_clear_cell(_ai_mp_grid,_ai_unit.grid_x,_ai_unit.grid_y);
mp_grid_clear_cell(_ai_mp_grid,_ai_target.grid_x,_ai_target.grid_y);
// If there is a path
with (_ai_unit) {
// Tell the unit that AI is controlling it
ai_selected = id;
// Tell the unit its target
ai_target = _ai_target;
// Clear path points
path_clear_points(ai_path);
// Create the path regardless if you can fully traverse it
mp_grid_path(_ai_mp_grid, ai_path, grid_x * grid.size + 10, grid_y * grid.size + 10, _ai_target.grid_x * grid.size + 10, _ai_target.grid_y * grid.size + 10, false)
// Delete end point of the path (because this is where the target is)
path_delete_point(ai_path, path_get_number(ai_path) - 1);
// Delete points that are either exceeding MP or are blocked
var _path_end = -1;
// Find where/if the path breaks
for (var i = 0; i < path_get_number(ai_path); ++i) {
// Get path's grid coordinates at this position
var _xpos = path_get_point_x(ai_path, i) div grid.size;
var _ypos = path_get_point_y(ai_path, i) div grid.size;
// Check if target is in range yet
var _in_range = point_distance(_xpos, _ypos, ai_target.x, ai_target.y) < range;
// If the path is larger than MP, or the position is taken on the grid, or the target is now within range
if path_get_number(ai_path) > mp || global.grid[# _xpos, _ypos] != -1 || _in_range {
// Set this point as the point to end the path at
_path_end = i;
break;
}
}
// If the path has broken somewhere
if _path_end != -1 {
for (var i = _path_end; i < path_get_number(ai_path); ++i) {
// Delete all remaining points
path_delete_point(ai_path, i);
}
}
other.ai_state = menu.paused;
// Update grid coordinates
global.grid[# grid_x, grid_y] = -1;
// Get the end point of the path in grid coordinates
var _end_x = path_get_point_x(ai_path, path_get_number(ai_path) - 1) div grid.size;
var _end_y = path_get_point_y(ai_path, path_get_number(ai_path) - 1) div grid.size;
global.grid[# _end_x, _end_y] = id;
grid_x = _end_x;
grid_y = _end_y;
// Follow the path
path_start(ai_path, 3, path_action_stop, false);
}
}
// Clean up
mp_grid_destroy(_ai_mp_grid);
ds_priority_delete_min(ai_unit_priority);
break;
}
}
else {
if ai_state == menu.inputting {
// Change turns and clean up
global.turn = "ally";
global.menu_state = menu.inputting;
global.created_ai_controller = false;
ds_priority_destroy(ai_unit_priority);
instance_destroy();
}
}
```

Last edited: Jul 7, 2019