refactor: move resource authority into simulation
This commit is contained in:
@@ -10,129 +10,118 @@ 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 amount_remaining: float = 10.0
|
||||
@export var initial_amount: float = 10.0
|
||||
@export var yield_per_action: float = 2.0
|
||||
@export var enabled: bool = true
|
||||
@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 reserved_by: int = -1
|
||||
var state: ResourceStateRecord
|
||||
|
||||
func _ready():
|
||||
func _ready() -> void:
|
||||
if node_id.is_empty():
|
||||
push_error("ResourceNode at %s has empty node_id" % get_path())
|
||||
enabled = false
|
||||
else:
|
||||
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()])
|
||||
enabled = false
|
||||
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")
|
||||
amount_changed.connect(_update_debug_label)
|
||||
reservation_changed.connect(_update_debug_label)
|
||||
_update_debug_label()
|
||||
_try_register_with_simulation()
|
||||
_update_presentation()
|
||||
|
||||
func _exit_tree():
|
||||
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 is_depleted() -> bool:
|
||||
return amount_remaining <= 0.0
|
||||
|
||||
func can_extract() -> bool:
|
||||
return enabled and not is_depleted()
|
||||
|
||||
func is_available_for(agent_id: int) -> bool:
|
||||
return enabled and not is_depleted() and (reserved_by == -1 or reserved_by == agent_id)
|
||||
|
||||
func reserve(agent_id: int) -> bool:
|
||||
if not is_available_for(agent_id):
|
||||
func bind_state(resource_state: ResourceStateRecord) -> bool:
|
||||
if resource_state == null or resource_state.get_node_id() != node_id:
|
||||
return false
|
||||
reserved_by = agent_id
|
||||
reservation_changed.emit(node_id, agent_id)
|
||||
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 release(agent_id: int) -> void:
|
||||
if reserved_by == agent_id:
|
||||
reserved_by = -1
|
||||
reservation_changed.emit(node_id, -1)
|
||||
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 extract(requested_amount: float = yield_per_action) -> float:
|
||||
if not can_extract():
|
||||
return 0.0
|
||||
var extracted := minf(requested_amount, amount_remaining)
|
||||
amount_remaining -= extracted
|
||||
amount_changed.emit(node_id, amount_remaining)
|
||||
if is_depleted():
|
||||
if reserved_by >= 0:
|
||||
reserved_by = -1
|
||||
reservation_changed.emit(node_id, -1)
|
||||
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)
|
||||
if hide_visual_when_depleted and has_node("Visual"):
|
||||
$Visual.visible = false
|
||||
return extracted
|
||||
|
||||
func restore_persistent_state(
|
||||
saved_amount: float,
|
||||
saved_reserved_by: int,
|
||||
saved_enabled: bool
|
||||
) -> void:
|
||||
amount_remaining = maxf(saved_amount, 0.0)
|
||||
reserved_by = saved_reserved_by if amount_remaining > 0.0 else -1
|
||||
enabled = saved_enabled
|
||||
amount_changed.emit(node_id, amount_remaining)
|
||||
reservation_changed.emit(node_id, reserved_by)
|
||||
func _update_presentation() -> void:
|
||||
if has_node("Visual"):
|
||||
$Visual.visible = not (hide_visual_when_depleted and is_depleted())
|
||||
_update_debug_label()
|
||||
|
||||
func _update_debug_label(_a = null, _b = null) -> void:
|
||||
if not has_node("DebugLabel"):
|
||||
return
|
||||
var label := $DebugLabel as Label3D
|
||||
var text := "%s\n%s %.1f" % [node_id, resource_id, amount_remaining]
|
||||
if reserved_by >= 0:
|
||||
text += "\nheld:%d" % reserved_by
|
||||
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(node_id: StringName) -> ResourceNode:
|
||||
static func get_by_id(search_id: StringName) -> ResourceNode:
|
||||
for node in _all:
|
||||
if node.node_id == node_id:
|
||||
if node.node_id == search_id:
|
||||
return node
|
||||
return null
|
||||
|
||||
static func get_all() -> Array[ResourceNode]:
|
||||
return _all.duplicate()
|
||||
|
||||
static func find_available(action: StringName, agent_id: int, from_position: Vector3) -> ResourceNode:
|
||||
var best: ResourceNode
|
||||
var best_dist := INF
|
||||
for node in _all:
|
||||
if node.action_id != action:
|
||||
continue
|
||||
if not node.can_npcs_use:
|
||||
continue
|
||||
if not node.is_available_for(agent_id):
|
||||
continue
|
||||
var dist := from_position.distance_squared_to(node.interaction_point.global_position)
|
||||
if dist < best_dist:
|
||||
best_dist = dist
|
||||
best = node
|
||||
return best
|
||||
|
||||
static func find_nearest_for_player(from_position: Vector3, max_distance: float) -> ResourceNode:
|
||||
var best: ResourceNode
|
||||
var best_dist := max_distance * max_distance
|
||||
for node in _all:
|
||||
if not node.can_player_use:
|
||||
continue
|
||||
var dist := from_position.distance_squared_to(node.interaction_point.global_position)
|
||||
if dist <= best_dist:
|
||||
best_dist = dist
|
||||
best = node
|
||||
return best
|
||||
|
||||
Reference in New Issue
Block a user