extends Node2D ## Grid with cells in it class_name Grid ## Out of bounds return value const OUT_OF_BOUNDS: int = -1 ## Amount of rows (max Y value) @export var rows: int = 1 ## Amount of columns (max X value) @export var columns: int = 1 ## Node that marks right down position of grid @export var right_border_marker: Node2D ## Computed size of one cell @onready var cell_size = (right_border_marker.global_position - global_position) / Vector2(columns,rows) ## Computed rect of grid @onready var rect: Rect2 = Rect2(global_position,(right_border_marker.global_position - global_position)) ## Array that holds units var grid: Array[Unit] = [] func _ready() -> void: grid.resize(rows*columns) ## Returns true if position is in bounds else returns false. func is_position_valid(pos: Vector2) -> bool: return rect.has_point(pos) ## Turns position into internal array index. Returns -1 if position is incorrect func to_index(pos: Vector2) -> int: if is_position_valid(pos) == false: return OUT_OF_BOUNDS var snapped_position: Vector2 = (pos - global_position)/cell_size return int(snapped_position.x) + columns * int(snapped_position.y) ## Turns index into global world position func from_index(index: int, centered: bool = false) -> Vector2: if index < 0 or index >= columns * rows: return Vector2.ZERO return Vector2(index % columns, index / columns) * cell_size + (cell_size/2.0 if centered else Vector2.ZERO) ## Snaps position through grid func snap_position(pos: Vector2, centered: bool = false) -> Vector2: return global_position + (pos - global_position).snapped(cell_size) + (cell_size / 2) if centered else Vector2.ZERO ## Returns position as grid snapped local coordinates func to_snapped(pos: Vector2) -> Vector2: if is_position_valid(pos) == false: return Vector2.ZERO var snapped_position: Vector2 = (pos - global_position)/cell_size return Vector2(floor(snapped_position.x),floor(snapped_position.y)) ## Tries to set unit at position and returns false if unit already exists or out of bounds func try_set_unit(pos: Vector2, unit: Unit) -> bool: var index: int = to_index(pos) if index == OUT_OF_BOUNDS: return false if grid[index] != null: return false grid[index] = unit return true ## Returns true if cell is occupied or out of bounds and false if cell is empty. func is_occupied(pos: Vector2) -> bool: var index: int = to_index(pos) if index == OUT_OF_BOUNDS: return true return grid[index] != null ## Returns units with given rule predicate.[br] ## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool ## that needs to return true if cell needs to be checked.[br] ## unit - source unit. func get_units(rule: Callable,unit: Unit) -> Array[Unit]: var unit_pos = to_snapped(unit.global_position) var result: Array[Unit] = [] for i in range(columns * rows): if rule.call(i%columns,i/columns,int(unit_pos.x),int(unit_pos.y)) and grid[i] != null: result.append(grid[i]) return result ## WIP[br] ## Returns units with given rule predicate. Checks from source unit with rays and stops only when ray failed.[br] ## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool ## that needs to return true if cell needs to be checked.[br] ## unit - source unit.[br] ## rays - amount of rays to check from unit. func get_units_stoppable(rule: Callable, unit: Unit, rays: int = 8) -> Array[Unit]: return [] ## Returns empty cells with given rule predicate.[br] ## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool ## that needs to return true if cell needs to be checked.[br] ## unit - source unit. func get_empty(rule: Callable, unit: Unit) -> Array[int]: var unit_pos = to_snapped(unit.global_position) var result: Array[int] = [] for i: int in range(columns * rows): if rule.call(i%columns,i/columns,int(unit_pos.x),int(unit_pos.y)) and grid[i] == null: result.append(i) return result ## WIP[br] ## Returns empty with given rule predicate. Checks from source unit with rays and stops only when ray failed.[br] ## rule - predicate with signature [code](x,y,source_x,source_y)[/code] -> bool ## that needs to return true if cell needs to be checked.[br] ## unit - source unit.[br] ## rays - amount of rays to check from unit. func get_empty_stoppable(rule: Callable, unit: Unit, rays: int = 8) -> Array[int]: return [] ## Casts a ray along [code]direction[/code] from [code]from[/code] to find unit. If distance is given, restricts lookup to [code]distance[/code] cells. func raycast_unit(from: Vector2,direction: Vector2,distance: float = -1) -> Unit: if not direction.is_normalized(): direction = direction.normalized() var result: Unit = null # Simple algorithm var step: float = max(abs(direction.x),abs(direction.y)) var step_v: Vector2 = Vector2(direction.x/step,direction.y/step) var check_position: Vector2 = from + step_v * cell_size while is_position_valid(check_position) and distance != 0: var index: int = to_index(check_position) if index == -1: break if grid[index] != null: result = grid[index] break check_position += step_v * cell_size return result ## Casts a ray along [code]direction[/code] from [code]from[/code] to find all units along the line. If distance is given, restricts lookup to [code]distance[/code] cells. func raycast_units(from: Vector2,direction: Vector2,distance: float = -1) -> Array[Unit]: if not direction.is_normalized(): direction = direction.normalized() var result: Array[Unit] = [] # Simple algorithm var step: float = max(abs(direction.x),abs(direction.y)) var step_v: Vector2 = Vector2(direction.x/step,direction.y/step) var check_position: Vector2 = from + step_v * cell_size while is_position_valid(check_position) and distance != 0: var index: int = to_index(check_position) if index == -1: break if grid[index] != null: result.append(grid[index]) check_position += step_v * cell_size return result ## Casts a ray along [code]direction[/code] from [code]from[/code] to find all empty cells. If distance is given, restricts lookup to [code]distance[/code] cells. func raycast_empty(from: Vector2,direction: Vector2,distance: float = -1) -> Array[int]: if not direction.is_normalized(): direction = direction.normalized() var result: Array[int] = [] # Simple algorithm var step: float = max(abs(direction.x),abs(direction.y)) var step_v: Vector2 = Vector2(direction.x/step,direction.y/step) var check_position: Vector2 = from + step_v * cell_size while is_position_valid(check_position) and distance != 0: var index: int = to_index(check_position) if index == -1: break if grid[index] == null: result.append(index) check_position += step_v * cell_size return result