@tool class_name Grid extends Node2D @export var cells: int: set(val): cells = val _calc_dims() @export var cell_size: int: set(val): cell_size = val _calc_dims() @export var grid_color: Color = Color.DARK_GREEN @export var line_color: Color = Color.DARK_GREEN.darkened(0.2) var total_width: int var total_height: int var thickness: int var off: Vector2 enum Field { Logistics, Residents, Commercial, Foragable, Forested } var fields: Dictionary[Field, Array] = {} @onready var selection_manager: SelectionManager = Util.find(SelectionManager) func ensure_field(field: Field): if field in fields: return fields[field] = [] fields[field].resize(cells ** 2) for idx in range(cells ** 2): fields[field][idx] = 0 func _enter_tree(): _calc_dims() func _ready(): _calc_dims() func _calc_dims(): total_width = cells * cell_size total_height = cells * cell_size thickness = 2 off = Vector2(total_width / -2, total_height / -2) func _draw(): var r = Rect2(off, Vector2(total_width, total_height)) draw_rect(r, grid_color, true) for i in range(cells + 1): draw_line(Vector2(i * cell_size, 0) + off, Vector2(i * cell_size, total_height) + off, line_color, thickness, false) for i in range(cells + 1): draw_line(Vector2(0, i * cell_size) + off, Vector2(total_width, i * cell_size) + off, line_color, thickness, false) if !debug_overlay_enabled: return if !fields.has(debug_overlay_field): return var field = fields[debug_overlay_field] var field_min = field.min() var field_max = field.max() for x in range(cells): for y in range(cells): var index = tile_pos2index(Vector2i(x, y)) if field[index] == 0: continue var true_value = field[index] if true_value <= 0: continue var t = inverse_lerp(0, field_max if true_value > 0 else abs(field_min), abs(true_value)) var grad = overlay_gradient if true_value > 0 else overlay_gradient_negative var font_grad = overlay_font_color_gradient if true_value > 0 else overlay_font_color_gradient var color = grad.sample(t) var world_rect = tile2world_rect(Rect2i(x, y, 1, 1)) draw_rect(world_rect, color, true) if !debug_overlay_display_values: continue var world_pos = self.tile2world(Vector2i(x, y)) var debug_str = str(field[index]) draw_string( ThemeDB.fallback_font, world_pos + Vector2.DOWN * cell_size / 2.0, debug_str, HORIZONTAL_ALIGNMENT_CENTER, cell_size, 10, font_grad.sample(t) ) #var tile = _get_tile_at_tile_pos(world_pos) #if tile == null: #continue #print(tile) #var t = Rect2( #tile2world(world_pos), #Vector2.ONE * cell_size #) #draw_rect(t, Color("773322")) func tile2world(tile: Vector2i) -> Vector2: return Vector2(tile) * cell_size + off func tile2world_rect(tile: Rect2) -> Rect2: return Rect2(Vector2(tile.position) * cell_size + off, tile.size * cell_size) func tile2world_size(tile_size: Vector2i) -> Vector2: return Vector2(tile_size * cell_size) func world2tile(world_pos: Vector2) -> Vector2i: return Vector2i(((world_pos - off) / cell_size).floor()) func world2tile_f(world_pos: Vector2) -> Vector2: return (world_pos - off) / cell_size func world2tile_rect(rect: Rect2) -> Rect2i: var start = world2tile(rect.position) var end = world2tile(rect.end) return Rect2i(start, end - start) func quantize(world_position: Vector2, tile_size: Vector2i) -> Rect2: var q_pos = tile2world(world2tile(world_position)) return Rect2(q_pos, tile_size * cell_size) func quantize_on_objects(world_position: Vector2) -> Rect2: var tile_pos = world2tile(world_position) var size = Vector2i.ONE for tile in things: if tile.get_worldbox().has_point(world_position): return tile.get_worldbox() return quantize(world_position, Vector2.ONE) func _get_building_at(pos: Vector2i) -> Building: for tile in things: if tile.get_tilebox().has_point(pos): return tile return null func _get_tile_at_world_pos(pos: Vector2) -> Building: #var world_pos = world2tile_f(pos) for tile in things: if tile.get_worldbox().has_point(pos): return tile return null func contains_world_pos(pos: Vector2) -> bool: var tile = world2tile(pos) if tile.x < 0 or tile.x > cells - 1: return false if tile.y < 0 or tile.y > cells - 1: return false return true func _shift_rect_half(rect: Rect2) -> Rect2: return Rect2(rect.position + rect.size / -2, rect.size) var things: Array[TileTransform] = [] var things_dict: Dictionary = {} func tile_pos2index(pos: Vector2i) -> int: return pos.x + pos.y * self.cells func index2tile_pos(idx: int) -> Vector2i: return Vector2i( idx % self.cells, floor(idx / self.cells) ) func increase_field_value(emissions_data: FieldEmission, position: Vector2i): ensure_field(emissions_data.field) var field = fields[emissions_data.field] # field[tile_pos2index(position)] = max(amt, field[tile_pos2index(position)]) for x_offset in range(- emissions_data.range - 1, emissions_data.range + 1): for y_offset in range(- emissions_data.range - 1, emissions_data.range + 1): var offset = Vector2i(x_offset, y_offset) var new_pos = position + offset if new_pos.x < 0: continue if new_pos.y < 0: continue if new_pos.x >= cells: continue if new_pos.y >= cells: continue var field_addition = emissions_data.get_field_value_for_distance(offset.length()) if field_addition == 0: continue var index = tile_pos2index(new_pos) field[index] += field_addition func decrease_field_Value(emissions_data: FieldEmission, position: Vector2i): ensure_field(emissions_data.field) var field = fields[emissions_data.field] # field[tile_pos2index(position)] = max(amt, field[tile_pos2index(position)]) for x_offset in range(- emissions_data.range - 1, emissions_data.range + 1): for y_offset in range(- emissions_data.range - 1, emissions_data.range + 1): var offset = Vector2i(x_offset, y_offset) var new_pos = position + offset if new_pos.x < 0: continue if new_pos.y < 0: continue if new_pos.x >= cells: continue if new_pos.y >= cells: continue var field_addition = emissions_data.get_field_value_for_distance(offset.length()) if field_addition == 0: continue var index = tile_pos2index(new_pos) field[index] -= field_addition var overlay_gradient: Gradient = preload("res://Gradients/OverlayBasic.tres") var overlay_gradient_negative: Gradient = preload("res://Gradients/OverlayBasicNegative.tres") var overlay_font_color_gradient: Gradient = preload("res://Gradients/OverlayBasicFontColor.tres") @export var debug_overlay_enabled: bool = true @export var debug_overlay_field: Field = Field.Logistics @export var debug_overlay_display_values: bool = false func enable_field_debug_overlay(field: Field): debug_overlay_enabled = true debug_overlay_field = field queue_redraw() func disable_debug_field_overlay(): debug_overlay_enabled = false queue_redraw() func unregister_building(tile: Building) -> void: things.erase(tile) var tilebox = tile.get_tilebox() for x_off in range(tilebox.size.x): for y_off in range(tilebox.size.y): for emission in tile.data.field_emissions: decrease_field_Value(emission, tilebox.position + Vector2i(x_off, y_off)) queue_redraw() func register_building(tile: Building) -> void: things.append(tile) var tilebox = tile.get_tilebox() for x_off in range(tilebox.size.x): for y_off in range(tilebox.size.y): for emission in tile.data.field_emissions: increase_field_value(emission, tilebox.position + Vector2i(x_off, y_off)) queue_redraw() func remove_tile(tile: TileTransform) -> void: things.erase(tile) func get_field_value_at(field: Field, tile_pos: Vector2i): if field not in fields: return 0 var value = fields[field][tile_pos2index(tile_pos)] return value