Files
gamedev-the-steward/world/storage/StorageNode.gd
T
2026-08-12 21:58:52 +02:00

161 lines
4.4 KiB
GDScript

class_name StorageNode
extends Node3D
static var _all: Array = []
@export var storage_id: StringName = SimulationIds.STORAGE_VILLAGE_PANTRY
@export var display_name := "Village Pantry"
@export var accepted_items: Array[StringName] = []
@export var debug_label_enabled := true
@onready var interaction_point: Marker3D = $InteractionPoint
const PRESENTATION_UPDATE_INTERVAL_SECONDS := 0.25
var state: StorageStateRecord
var _last_label_text := ""
var _presentation_elapsed := 0.0
var _active_world_adapter: ActiveWorldAdapter
func _ready() -> void:
if accepted_items.is_empty():
accepted_items = [SimulationIds.RESOURCE_FOOD]
if storage_id.is_empty():
push_error("StorageNode at %s has empty storage_id" % get_path())
_update_presentation()
return
_active_world_adapter = ActiveWorldAdapter.find_for_node(self)
var existing: StorageNode = get_by_id_in_context(storage_id, _active_world_adapter)
if existing != null:
push_error(
(
"Duplicate StorageNode storage_id '%s' at %s; first registered at %s"
% [storage_id, get_path(), existing.get_path()]
)
)
_update_presentation()
return
_all.append(self)
add_to_group("storage_nodes")
set_notify_transform(true)
_try_register_with_simulation()
_notify_world_adapter()
_update_presentation()
set_process(debug_label_enabled)
func _process(delta: float) -> void:
_presentation_elapsed += delta
if _presentation_elapsed < PRESENTATION_UPDATE_INTERVAL_SECONDS:
return
_presentation_elapsed = fmod(_presentation_elapsed, PRESENTATION_UPDATE_INTERVAL_SECONDS)
_update_presentation()
func _exit_tree() -> void:
_unregister_from_world_adapter()
_all.erase(self)
state = null
func bind_state(storage_state: StorageStateRecord) -> bool:
if storage_state == null or storage_state.get_storage_id() != storage_id:
return false
state = storage_state
_update_presentation()
_notify_world_adapter()
return true
func get_interaction_position() -> Vector3:
return interaction_point.global_position if interaction_point != null else global_position
func accepts_item(item_id: StringName) -> bool:
return accepted_items.is_empty() or item_id in accepted_items
func _try_register_with_simulation() -> void:
var managers := get_tree().get_nodes_in_group("simulation_manager")
if managers.size() != 1:
return
var manager := managers[0]
if manager.has_method("register_storage_node"):
manager.register_storage_node(self)
func _notification(what: int) -> void:
if what == NOTIFICATION_TRANSFORM_CHANGED and is_node_ready():
if is_instance_valid(_active_world_adapter):
_active_world_adapter.refresh_world_target_position(self)
func _notify_world_adapter() -> void:
if not is_inside_tree():
return
if (
not is_instance_valid(_active_world_adapter)
or not _active_world_adapter.owns_target_node(self)
):
_active_world_adapter = ActiveWorldAdapter.find_for_node(self)
if _active_world_adapter != null:
_active_world_adapter.register_world_target(self)
func _bind_active_world_adapter(adapter: ActiveWorldAdapter) -> void:
if adapter != null and adapter.owns_target_node(self):
_active_world_adapter = adapter
func _unregister_from_world_adapter() -> void:
if not is_instance_valid(_active_world_adapter):
_active_world_adapter = ActiveWorldAdapter.find_for_node(self)
if is_instance_valid(_active_world_adapter):
_active_world_adapter.unregister_world_target(self)
_active_world_adapter = null
func _update_presentation() -> void:
if not has_node("DebugLabel"):
return
var label := $DebugLabel as Label3D
label.visible = debug_label_enabled
if not debug_label_enabled:
return
var text := "%s\n%s" % [display_name, storage_id]
if state == null:
text += "\nUNBOUND"
else:
for item_id in accepted_items:
text += "\n%s: %.1f" % [item_id, state.get_amount(item_id)]
if text == _last_label_text:
return
_last_label_text = text
label.text = text
func set_debug_label_enabled(is_enabled: bool) -> void:
debug_label_enabled = is_enabled
_presentation_elapsed = 0.0
set_process(is_enabled)
_update_presentation()
static func get_by_id(search_id: StringName):
for node in _all:
if node.storage_id == search_id:
return node
return null
static func get_by_id_in_context(search_id: StringName, adapter: ActiveWorldAdapter) -> StorageNode:
for node in _all:
if node.storage_id == search_id and ActiveWorldAdapter.find_for_node(node) == adapter:
return node as StorageNode
return null
static func get_all() -> Array:
return _all.duplicate()