extends Node3D class_name Building @export_group("Construction", "build") @export var build_time: float = 5.0 ## Worker-time taken for 1 citizen to finish the building, in citizen-seconds. @export var build_materials_needed: Dictionary[Item, int] = {} @export_group("Defence") @export var max_hp: int = 100 ## Amount of damage this building can sustain before being destroyed. @export_group("Stacking") @export var can_stack: bool = true ## Whether this building can be stacked on top of other buildings. @export var can_be_stacked_on: bool = true ## Whether this building will accept other buildings being stacked on it. @export var stack_position: Vector3 = Vector3(0,2,0) ## The local position that stacked buildings will be placed at. @onready var build_position: Vector3 = global_position @onready var nav_obstacle: NavObstable = $NavObstacle @onready var consumer: Consumer = $Consumer @onready var producer: Producer = $Producer @onready var collision_shape: CollisionShape3D = $CollisionShape3D signal functional_changed(functional: bool) enum BuildState { UNPLACED, BUILDING, READY, DESTROYED } const PLACEMENT_POSITION_OK: int = 0b0001 const PLACEMENT_FOUNDATION_REQUIRED: int = 0b0010 const PLACEMENT_PART_ADDED: int = 0b0100 const PLACEMENT_COMPLETED: int = 0b1000 var hp: int = max_hp var build_progress: float = 0.0: set(val): build_progress = val if build_progress >= build_time: build_state = BuildState.READY var build_percent: float = build_progress / build_time set_visual_build_progress(build_percent) var stacked_buildings: Array[Building] = [] var build_state: BuildState = BuildState.READY: set(state): if state != build_state: build_state = state functional_changed.emit(is_functional()) if build_state == BuildState.BUILDING: for item in build_materials_needed.keys(): for i in range(build_materials_needed.get(item)): var task: Task.FetchItemTask = Task.FetchItemTask.new() task.building = self task.item = item CitizenManager._instance.add_task(task) check_buildable() set_visual_build_progress(0.0) elif build_state == BuildState.READY: set_visual_build_progress(1.0) var build_materials_used: Dictionary[Item, int] = {} func _ready() -> void: if nav_obstacle != null: functional_changed.connect(func(enable:bool): nav_obstacle.enabled = enable) if consumer != null: functional_changed.connect(func(enable:bool): consumer.enabled = enable) if producer != null: functional_changed.connect(func(enable:bool): producer.enabled = enable) if collision_shape != null: functional_changed.connect(func(enable:bool): collision_shape.disabled = !enable) func _process(delta: float) -> void: if build_state == BuildState.UNPLACED: global_position = global_position.lerp(build_position, 0.4) var debug_text: String = "" debug_text += "State: %s\n" % build_state if consumer != null: var consumer_state: String if consumer.enabled: consumer_state = "OK" else: consumer_state = "Off" debug_text += "Input: %s \nStorage: %d\n" % [consumer_state, consumer.storage_total] if producer != null: var producer_state = "OK" if !producer.enabled: producer_state = "Off" elif !producer.can_produce(): producer_state = "Blocked" debug_text += "Output: %s\n" % producer_state $Label3D.text = debug_text func is_functional() -> bool: return build_state == BuildState.READY func _start_placement() -> void: build_state = BuildState.UNPLACED func _end_placement() -> void: build_state = BuildState.BUILDING func _placement_select_building(building: Building, confirmed: bool) -> int: while !building.stacked_buildings.is_empty(): building = building.stacked_buildings[0] var stack_ok: bool = can_stack and building.can_be_stacked_on var ret: int = 0 if stack_ok: build_position = building.global_position + building.stack_position ret |= PLACEMENT_POSITION_OK if confirmed and stack_ok: global_position = build_position building.stacked_buildings.append(self) ret |= PLACEMENT_COMPLETED return ret func _placement_select_position(pos: Vector3, confirmed: bool) -> int: var ret: int = 0 ret |= PLACEMENT_POSITION_OK build_position = Vector3(roundf(pos.x), ceilf(pos.y-0.01), roundf(pos.z)) if confirmed: build_state = BuildState.BUILDING ret |= PLACEMENT_COMPLETED return ret func check_buildable() -> void: if can_build(): var task: Task.BuildTask = Task.BuildTask.new() task.building = self CitizenManager._instance.add_task(task) func _add_build_material(item: Item) -> void: build_materials_used.set(item, build_materials_used.get(item, 0) + 1) check_buildable() func can_build() -> bool: for item in build_materials_needed.keys(): if build_materials_used.get(item,0) < build_materials_needed.get(item,0): return false return true func set_visual_build_progress(ratio: float) -> void: var geometries: Array[Node] = find_children("", "GeometryInstance3D", true) for node in geometries: var geometry: GeometryInstance3D = node as GeometryInstance3D var opacity: float = 0.5 + (ratio * 0.5) geometry.transparency = 1.0 - opacity