128 lines
3.9 KiB
GDScript
128 lines
3.9 KiB
GDScript
class_name ResourceNode
|
|
extends Node3D
|
|
|
|
signal amount_changed(node_id: StringName, amount_remaining: float)
|
|
signal depleted(node_id: StringName)
|
|
signal reservation_changed(node_id: StringName, agent_id: int)
|
|
|
|
static var _all: Array[ResourceNode] = []
|
|
|
|
@export var node_id: StringName
|
|
@export var action_id: StringName = &"gather_food"
|
|
@export var resource_id: StringName = &"food"
|
|
@export var initial_amount: float = 10.0
|
|
@export var yield_per_action: float = 2.0
|
|
@export var initial_enabled: bool = true
|
|
@export var can_npcs_use: bool = true
|
|
@export var can_player_use: bool = true
|
|
@export var hide_visual_when_depleted: bool = false
|
|
|
|
@onready var interaction_point: Marker3D = $InteractionPoint
|
|
|
|
var state: ResourceStateRecord
|
|
|
|
func _ready() -> void:
|
|
if node_id.is_empty():
|
|
push_error("ResourceNode at %s has empty node_id" % get_path())
|
|
initial_enabled = false
|
|
_update_presentation()
|
|
return
|
|
var existing := get_by_id(node_id)
|
|
if existing != null:
|
|
push_error(
|
|
"Duplicate ResourceNode node_id '%s' at %s; first registered at %s"
|
|
% [node_id, get_path(), existing.get_path()]
|
|
)
|
|
initial_enabled = false
|
|
_update_presentation()
|
|
return
|
|
_all.append(self)
|
|
add_to_group("resource_nodes")
|
|
_try_register_with_simulation()
|
|
_update_presentation()
|
|
|
|
func _exit_tree() -> void:
|
|
_all.erase(self)
|
|
if state != null and state.changed.is_connected(_on_state_changed):
|
|
state.changed.disconnect(_on_state_changed)
|
|
if state != null and state.depleted.is_connected(_on_state_depleted):
|
|
state.depleted.disconnect(_on_state_depleted)
|
|
state = null
|
|
|
|
func bind_state(resource_state: ResourceStateRecord) -> bool:
|
|
if resource_state == null or resource_state.get_node_id() != node_id:
|
|
return false
|
|
if state != null and state.changed.is_connected(_on_state_changed):
|
|
state.changed.disconnect(_on_state_changed)
|
|
if state != null and state.depleted.is_connected(_on_state_depleted):
|
|
state.depleted.disconnect(_on_state_depleted)
|
|
state = resource_state
|
|
if not state.changed.is_connected(_on_state_changed):
|
|
state.changed.connect(_on_state_changed)
|
|
if not state.depleted.is_connected(_on_state_depleted):
|
|
state.depleted.connect(_on_state_depleted)
|
|
_update_presentation()
|
|
return true
|
|
|
|
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_resource_node"):
|
|
manager.register_resource_node(self)
|
|
|
|
func get_amount_remaining() -> float:
|
|
return state.get_amount_remaining() if state != null else initial_amount
|
|
|
|
func get_reserved_by() -> int:
|
|
return state.get_reserved_by() if state != null else -1
|
|
|
|
func is_enabled() -> bool:
|
|
return state.is_enabled() if state != null else initial_enabled
|
|
|
|
func is_depleted() -> bool:
|
|
return state.is_depleted() if state != null else initial_amount <= 0.0
|
|
|
|
func can_extract() -> bool:
|
|
return state.can_extract() if state != null else initial_enabled and not is_depleted()
|
|
|
|
func _on_state_changed(changed_state: ResourceStateRecord) -> void:
|
|
if changed_state != state:
|
|
return
|
|
amount_changed.emit(node_id, state.get_amount_remaining())
|
|
reservation_changed.emit(node_id, state.get_reserved_by())
|
|
_update_presentation()
|
|
|
|
func _on_state_depleted(depleted_state: ResourceStateRecord) -> void:
|
|
if depleted_state == state:
|
|
depleted.emit(node_id)
|
|
|
|
func _update_presentation() -> void:
|
|
if has_node("Visual"):
|
|
$Visual.visible = not (hide_visual_when_depleted and is_depleted())
|
|
if not has_node("DebugLabel"):
|
|
return
|
|
var label := $DebugLabel as Label3D
|
|
var text := "%s\n%s %.1f" % [
|
|
node_id,
|
|
resource_id,
|
|
get_amount_remaining()
|
|
]
|
|
if get_reserved_by() >= 0:
|
|
text += "\nheld:%d" % get_reserved_by()
|
|
if is_depleted():
|
|
text += "\nDEPLETED"
|
|
if state == null:
|
|
text += "\nUNBOUND"
|
|
label.text = text
|
|
|
|
static func get_by_id(search_id: StringName) -> ResourceNode:
|
|
for node in _all:
|
|
if node.node_id == search_id:
|
|
return node
|
|
return null
|
|
|
|
static func get_all() -> Array[ResourceNode]:
|
|
return _all.duplicate()
|