2024-05-13 18:16:23 -04:00
|
|
|
@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()
|
2025-10-10 03:01:08 -04:00
|
|
|
@export var grid_color: Color = Color.DARK_GREEN
|
|
|
|
|
@export var line_color: Color = Color.DARK_GREEN.darkened(0.2)
|
2024-05-13 18:16:23 -04:00
|
|
|
|
|
|
|
|
var total_width: int
|
|
|
|
|
var total_height: int
|
|
|
|
|
|
|
|
|
|
var thickness: int
|
|
|
|
|
var off: Vector2
|
|
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
enum Field {
|
|
|
|
|
Logistics,
|
|
|
|
|
Residents,
|
|
|
|
|
Commercial,
|
|
|
|
|
Foragable,
|
|
|
|
|
Forested
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var fields: Dictionary[Field, Array] = {}
|
2025-09-24 20:01:45 -04:00
|
|
|
|
2024-05-13 18:16:23 -04:00
|
|
|
@onready var selection_manager: SelectionManager = Util.find(SelectionManager)
|
|
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
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
|
|
|
|
|
|
2024-05-13 18:16:23 -04:00
|
|
|
func _enter_tree():
|
|
|
|
|
_calc_dims()
|
|
|
|
|
|
|
|
|
|
func _ready():
|
|
|
|
|
_calc_dims()
|
|
|
|
|
|
|
|
|
|
func _calc_dims():
|
|
|
|
|
total_width = cells * cell_size
|
|
|
|
|
total_height = cells * cell_size
|
|
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
thickness = 2
|
2024-05-13 18:16:23 -04:00
|
|
|
off = Vector2(total_width / -2, total_height / -2)
|
|
|
|
|
|
|
|
|
|
func _draw():
|
|
|
|
|
var r = Rect2(off, Vector2(total_width, total_height))
|
2025-10-10 03:01:08 -04:00
|
|
|
draw_rect(r, grid_color, true)
|
2024-05-13 18:16:23 -04:00
|
|
|
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)
|
2025-10-10 03:01:08 -04:00
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
2024-05-13 18:16:23 -04:00
|
|
|
for x in range(cells):
|
|
|
|
|
for y in range(cells):
|
2025-10-10 03:01:08 -04:00
|
|
|
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)
|
2024-05-13 18:16:23 -04:00
|
|
|
)
|
2025-10-10 03:01:08 -04:00
|
|
|
#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:
|
2024-05-13 18:16:23 -04:00
|
|
|
return Vector2(tile) * cell_size + off
|
|
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
func tile2world_rect(tile: Rect2) -> Rect2:
|
2024-05-13 18:16:23 -04:00
|
|
|
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:
|
2025-09-24 20:01:45 -04:00
|
|
|
if tile.get_worldbox().has_point(world_position):
|
|
|
|
|
return tile.get_worldbox()
|
|
|
|
|
return quantize(world_position, Vector2.ONE)
|
|
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
func _get_building_at(pos: Vector2i) -> Building:
|
2024-05-13 18:16:23 -04:00
|
|
|
for tile in things:
|
|
|
|
|
if tile.get_tilebox().has_point(pos):
|
|
|
|
|
return tile
|
|
|
|
|
return null
|
|
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
func _get_tile_at_world_pos(pos: Vector2) -> Building:
|
2024-05-13 18:16:23 -04:00
|
|
|
#var world_pos = world2tile_f(pos)
|
|
|
|
|
for tile in things:
|
2025-09-24 20:01:45 -04:00
|
|
|
if tile.get_worldbox().has_point(pos):
|
2024-05-13 18:16:23 -04:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2025-09-24 20:01:45 -04:00
|
|
|
var things: Array[TileTransform] = []
|
|
|
|
|
var things_dict: Dictionary = {}
|
2024-05-13 18:16:23 -04:00
|
|
|
|
2025-09-24 20:01:45 -04:00
|
|
|
func tile_pos2index(pos: Vector2i) -> int:
|
|
|
|
|
return pos.x + pos.y * self.cells
|
2024-05-13 18:16:23 -04:00
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
func index2tile_pos(idx: int) -> Vector2i:
|
2025-09-24 20:01:45 -04:00
|
|
|
return Vector2i(
|
|
|
|
|
idx % self.cells,
|
|
|
|
|
floor(idx / self.cells)
|
|
|
|
|
)
|
2024-05-13 18:16:23 -04:00
|
|
|
|
2025-10-10 03:01:08 -04:00
|
|
|
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:
|
2025-09-24 20:01:45 -04:00
|
|
|
things.append(tile)
|
2025-10-10 03:01:08 -04:00
|
|
|
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()
|
2024-05-13 18:16:23 -04:00
|
|
|
|
2025-09-24 20:01:45 -04:00
|
|
|
func remove_tile(tile: TileTransform) -> void:
|
|
|
|
|
things.erase(tile)
|
2025-10-10 03:01:08 -04:00
|
|
|
|
|
|
|
|
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
|