class_name ActiveWorldAdapter extends Node const LoadedResourceSpatialIndexScript := preload( "res://world/resource_nodes/LoadedResourceSpatialIndex.gd" ) @export var pantry_storage: StorageNode @export var woodpile_storage: StorageNode @export_range(4.0, 128.0, 1.0) var resource_cell_size := 24.0 var _resource_index := LoadedResourceSpatialIndexScript.new() func _ready() -> void: _resource_index.configure(resource_cell_size) add_to_group("active_world_adapter") rebuild_resource_index() func rebuild_resource_index() -> void: _resource_index.clear() for node in ResourceNode.get_all(): _resource_index.register_node(node, node.state) func register_resource_node(node: ResourceNode, state: ResourceStateRecord = null) -> bool: return _resource_index.register_node(node, state) func unregister_resource_node(node_id: StringName) -> void: _resource_index.unregister_node(node_id) func get_resource_candidates(action_id: StringName) -> Array[Dictionary]: return _resource_index.get_all_candidates(action_id) func get_resource_candidates_in_radius( action_id: StringName, origin: Vector3, max_distance: float, min_distance_exclusive: float = -1.0 ) -> Array[Dictionary]: return _resource_index.get_candidates_in_radius( action_id, origin, max_distance, min_distance_exclusive ) func get_resource_query_profile(action_id: StringName, origin: Vector3) -> Dictionary: return _resource_index.get_query_profile(action_id, origin) func get_resource_nodes_in_radius(origin: Vector3, max_distance: float) -> Array[ResourceNode]: return _resource_index.get_nodes_in_radius(origin, max_distance) func get_resource_index_stats() -> Dictionary: return _resource_index.get_stats() func get_animal_candidates(action_id: StringName) -> Array[Dictionary]: var candidates: Array[Dictionary] = [] for node in AnimalNode.get_all(): if not node.supports_action(action_id): continue ( candidates . append( { "target_id": String(node.animal_id), "position": node.get_interaction_position(), "display_name": node.display_name, } ) ) candidates.sort_custom( func(first: Dictionary, second: Dictionary) -> bool: return String(first["target_id"]) < String(second["target_id"]) ) return candidates func get_animal_routine_sites(species_id: StringName, animal_id: StringName) -> Array[Dictionary]: var candidates: Array[Dictionary] = [] for site in AnimalRoutineSite.get_all(): if not site.supports_animal(animal_id, species_id): continue ( candidates . append( { "site_id": String(site.site_id), "position": site.get_routine_position(), "display_name": site.display_name, } ) ) candidates.sort_custom( func(first: Dictionary, second: Dictionary) -> bool: return String(first["site_id"]) < String(second["site_id"]) ) return candidates func get_animal_nodes_in_radius(origin: Vector3, max_distance: float) -> Array[AnimalNode]: var nodes: Array[AnimalNode] = [] var maximum_distance_squared := maxf(max_distance, 0.0) * maxf(max_distance, 0.0) for node in AnimalNode.get_all(): if origin.distance_squared_to(node.get_interaction_position()) <= maximum_distance_squared: nodes.append(node) nodes.sort_custom( func(first: AnimalNode, second: AnimalNode) -> bool: var first_distance := origin.distance_squared_to(first.get_interaction_position()) var second_distance := origin.distance_squared_to(second.get_interaction_position()) if not is_equal_approx(first_distance, second_distance): return first_distance < second_distance return String(first.animal_id) < String(second.animal_id) ) return nodes func get_activity_target(action_id: StringName, origin: Vector3 = Vector3.ZERO) -> Dictionary: var best_candidate: Dictionary = {} var best_distance := INF for candidate in get_activity_candidates(action_id): var position: Vector3 = candidate["position"] var distance := origin.distance_squared_to(position) if distance < best_distance: best_distance = distance best_candidate = candidate if not best_candidate.is_empty(): return best_candidate match action_id: SimulationIds.ACTION_EAT: return get_storage_target(SimulationIds.STORAGE_VILLAGE_PANTRY) SimulationIds.ACTION_DEPOSIT_FOOD, SimulationIds.ACTION_WITHDRAW_FOOD: return get_storage_target(SimulationIds.STORAGE_VILLAGE_PANTRY) SimulationIds.ACTION_DEPOSIT_WOOD: return get_storage_target(SimulationIds.STORAGE_VILLAGE_WOODPILE) return {} func get_activity_candidates(action_id: StringName) -> Array[Dictionary]: var candidates: Array[Dictionary] = [] for candidate in ActivitySite.get_all(): var site := candidate as ActivitySite if site == null: continue if not site.supports_action(action_id): continue candidates.append( { "target_id": String(site.site_id), "position": site.get_interaction_position(), "capacity": site.capacity } ) return candidates func get_storage_target(storage_id: StringName) -> Dictionary: var storage_node: StorageNode match storage_id: SimulationIds.STORAGE_VILLAGE_PANTRY: storage_node = pantry_storage SimulationIds.STORAGE_VILLAGE_WOODPILE: storage_node = woodpile_storage if storage_node == null: return {} return { "target_id": String(storage_node.storage_id), "position": storage_node.get_interaction_position() }