extends Node const BUILDING_SCENE: PackedScene = preload("res://scenes/entities/building/building.tscn") const GRID_SIZE: float = 1.0 @onready var inventory_system: Node = get_node("../InventorySystem") var blueprints: Array = [] func _ready() -> void: blueprints = [ {"id": &"floor", "name": "Floor", "size": Vector3(1, 0.2, 1), "color": Color(0.55, 0.4, 0.25), "material": &"wood", "cost": 1}, {"id": &"wall", "name": "Wall", "size": Vector3(1, 2, 0.2), "color": Color(0.7, 0.6, 0.45), "material": &"wood", "cost": 1}, {"id": &"door", "name": "Door", "size": Vector3(1, 2, 0.15), "color": Color(0.4, 0.25, 0.15), "material": &"wood", "cost": 2}, {"id": &"roof", "name": "Roof", "size": Vector3(1, 0.2, 1), "color": Color(0.5, 0.3, 0.2), "material": &"wood", "cost": 1} ] func _building_root() -> Node3D: var n: Node = get_node_or_null("/root/World/EntityRoot/Buildings") if n == null: n = get_node_or_null("/root/Dungeon/EntityRoot/Buildings") return n func get_blueprints() -> Array: return blueprints func find_blueprint(id: StringName) -> Dictionary: for b in blueprints: if b.id == id: return b return {} func snap_position(pos: Vector3) -> Vector3: return Vector3(round(pos.x / GRID_SIZE) * GRID_SIZE, max(0.0, pos.y), round(pos.z / GRID_SIZE) * GRID_SIZE) func place(player: Node, id: StringName, pos: Vector3, rot: float) -> bool: if not (multiplayer.is_server() or multiplayer.multiplayer_peer == null): request_place.rpc_id(1, player.get_path(), id, pos, rot) return false var bp: Dictionary = find_blueprint(id) if bp.is_empty(): return false var mat: StringName = bp.material var cost: int = bp.cost if inventory_system.get_amount(player, mat) < cost: return false inventory_system.remove_item(player, mat, cost) var root := _building_root() if root == null: return false var b: StaticBody3D = BUILDING_SCENE.instantiate() b.building_id = id b.name = "B_%s_%d" % [str(id), Time.get_ticks_msec() + randi() % 1000] root.add_child(b, true) b.global_position = snap_position(pos) b.rotation.y = rot b.apply_building(id) _apply_building_visual.rpc(b.get_path(), id) EventBus.building_placed.emit(b) return true func remove(player: Node, b_path: NodePath) -> bool: if not (multiplayer.is_server() or multiplayer.multiplayer_peer == null): request_remove.rpc_id(1, player.get_path(), b_path) return false var node: Node = get_node_or_null(b_path) if node == null: return false var bp: Dictionary = find_blueprint(node.building_id) if not bp.is_empty(): inventory_system.add_item(player, bp.material, max(1, int(float(bp.cost) * 0.5))) EventBus.building_removed.emit(node) node.queue_free() return true @rpc("any_peer", "reliable") func request_place(player_path: NodePath, id: StringName, pos: Vector3, rot: float) -> void: var p: Node = get_node_or_null(player_path) if p: place(p, id, pos, rot) @rpc("any_peer", "reliable") func request_remove(player_path: NodePath, b_path: NodePath) -> void: var p: Node = get_node_or_null(player_path) if p: remove(p, b_path) @rpc("authority", "reliable", "call_local") func _apply_building_visual(b_path: NodePath, id: StringName) -> void: var node: Node = get_node_or_null(b_path) if node and node.has_method("apply_building"): node.apply_building(id)