Grid distance formula? (SOLVED)

Hello! I’m working on a Strategy RPG project similar to Fire Emblem or Reverse Collapse: Code Name Bakery. I’ve run into a hiccup early on, namely trying to get the math right for checking valid grid targets.

A quick rundown on what the program does so far:

  1. The main script generates individual game objects with clickable collision for each tile via a factory. Information for each tile is stored in a table (self.map_state).
  2. Generates a single player unit object that stores variables within its script.
  3. When a tile with a unit is clicked, it collects the information stored in the unit’s script and ports it to the main script. The most relevant variables are the movement range and the unit’s position.
  4. The variables are plugged into the function written below. Each tile is checked to see if it’s within the given range of the unit, and if so calls another function to generate more objects depicted as blue squares that represent the movement range.
  5. It’s important to note that the kind of movement I’m looking for restricts diagonal movement, forcing the range to be expended through horizontal and vertical movement.

For a range of 1-3 the script functions great, the squares are laid out exactly as expected.

However, when I set a range of 4 or more, things start looking weird.

I’m sure it’s a problem with my math, which I’m not great with. I may also be approaching the project the wrong way entirely. Any suggestions?

I’ve uploaded the project files as well if that helps.
SRPG.zip (685.9 KB)

local function draw_squares(self, pos, range)
	--Horizontal and Vertical
	for i, v in ipairs(self.map_state) do
		if v.pos.x == pos.x and v.pos.y == pos.y + (48 * range) and not v.occupied or
		v.pos.x == pos.x and v.pos.y == pos.y - (48 * range) and not v.occupied or 
		v.pos.x == pos.x + (48 * range) and v.pos.y == pos.y and not v.occupied or
		v.pos.x == pos.x - (48 * range) and v.pos.y == pos.y and not v.occupied then
			square_factory(self, v.pos, self.light_blue)
		end
	end
	range = range - 1
	if range > 0 then
		--Diagonal
		for i, v in ipairs(self.map_state) do
			if v.pos.x == pos.x + (48 * range) and v.pos.y == pos.y + 48 and not v.occupied or
			v.pos.x == pos.x + (48 * range) and v.pos.y == pos.y - 48 and not v.occupied or
			v.pos.x == pos.x - (48 * range) and v.pos.y == pos.y + 48 and not v.occupied or
			v.pos.x == pos.x - (48 * range) and v.pos.y == pos.y - 48 and not v.occupied or
			v.pos.x == pos.x + 48 and v.pos.y == pos.y + (48 * range) and not v.occupied or
			v.pos.x == pos.x + 48 and v.pos.y == pos.y - (48 * range) and not v.occupied or
			v.pos.x == pos.x - 48 and v.pos.y == pos.y + (48 * range) and not v.occupied or
			v.pos.x == pos.x - 48 and v.pos.y == pos.y - (48 * range) and not v.occupied then
				square_factory(self, v.pos, self.light_blue)
			end
		end
		draw_squares(self, pos, range)
	end
end

I haven’t reviewed your project or code in detail, but I feel like whether a given square is in range can be expressed by adding up the differences in rows and columns. I.e. if you need to move one column and three rows, that’s a distance of four.

local range = 4
local in_range = math.abs(start_col-target_col)+math.abs(start_row-target_row) <= range
2 Likes

Worked like a charm! Thank you very much, I’ve been scratching my head over this for hours!

The code looks like this now:

local function draw_squares(self, pos, range)
	for i, v in ipairs(self.map_state) do
		local in_range = math.abs(v.pos.x-pos.x)+math.abs(v.pos.y-pos.y) <= range * 48
		if in_range and not v.occupied then
			square_factory(self, v.pos, self.light_blue)
		end
	end
end

And the end result!

2 Likes