feat: score resource discovery by comfort and safety
This commit is contained in:
@@ -655,8 +655,9 @@ The practical next sequence is:
|
|||||||
1. Add a cinematic/debug toggle and repeatable simulation-garden demo reset.
|
1. Add a cinematic/debug toggle and repeatable simulation-garden demo reset.
|
||||||
2. Expand resource discovery with many finite `ResourceNode` instances placed
|
2. Expand resource discovery with many finite `ResourceNode` instances placed
|
||||||
in meaningful foliage, animal-camp, berry, and village-stockpile contexts.
|
in meaningful foliage, animal-camp, berry, and village-stockpile contexts.
|
||||||
Score them by reachability, distance, safety, profession, and NPC comfort
|
The first distance/safety/comfort scoring pass exists; next, add more
|
||||||
range rather than reintroducing abstract resource zones.
|
authored resources and validate their navigation/reachability rather than
|
||||||
|
reintroducing abstract resource zones.
|
||||||
3. Expand persistence only when schedules, player state, or a real save menu
|
3. Expand persistence only when schedules, player state, or a real save menu
|
||||||
creates a concrete requirement.
|
creates a concrete requirement.
|
||||||
|
|
||||||
|
|||||||
@@ -312,7 +312,9 @@ that amount, and releases an NPC reservation if the source is depleted.
|
|||||||
Future resource expansion should add many finite `ResourceNode` instances rather
|
Future resource expansion should add many finite `ResourceNode` instances rather
|
||||||
than broad resource zones: trees in foliage clusters, berry patches, animal
|
than broad resource zones: trees in foliage clusters, berry patches, animal
|
||||||
camps, and village stockpiles can be ranked by reachability, distance, safety,
|
camps, and village stockpiles can be ranked by reachability, distance, safety,
|
||||||
profession, and NPC comfort range.
|
profession, and NPC comfort range. The current resolver already combines
|
||||||
|
distance with resource `safety_risk`, `comfort_distance`, and
|
||||||
|
`discovery_priority` metadata.
|
||||||
|
|
||||||
### Jajce scaffold
|
### Jajce scaffold
|
||||||
|
|
||||||
|
|||||||
@@ -542,11 +542,13 @@ compete for the same social/workstation site.
|
|||||||
**Exit:** temporary activity markers and task-to-marker mapping are deleted,
|
**Exit:** temporary activity markers and task-to-marker mapping are deleted,
|
||||||
not merely unused.
|
not merely unused.
|
||||||
|
|
||||||
**Status: complete for the marker-removal migration.** Future work should
|
**Status: complete for the marker-removal migration.** Follow-up work now
|
||||||
extend target scoring rather than reintroducing zones: resources can be spread
|
extends target scoring rather than reintroducing zones: resources can be spread
|
||||||
as many finite `ResourceNode` instances across foliage, animal camps, berry
|
as many finite `ResourceNode` instances across foliage, animal camps, berry
|
||||||
patches, and village stockpiles, then ranked by distance, reachability, safety,
|
patches, and village stockpiles, then ranked by distance, reachability, safety,
|
||||||
profession, and NPC comfort range.
|
profession, and NPC comfort range. The first scoring pass stores
|
||||||
|
`safety_risk`, `comfort_distance`, and `discovery_priority` on resource state
|
||||||
|
and uses them when selecting NPC resource targets.
|
||||||
|
|
||||||
## Test scenarios
|
## Test scenarios
|
||||||
|
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ duplicate IDs, and preserve deterministic ordering across save/restore. See
|
|||||||
|
|
||||||
`SimulationManager` owns `ResourceStateRecord` instances independently of the
|
`SimulationManager` owns `ResourceStateRecord` instances independently of the
|
||||||
scene tree. Amount, reservation, enabled state, action/resource identity,
|
scene tree. Amount, reservation, enabled state, action/resource identity,
|
||||||
yield, and usage permissions remain available and serializable while no
|
yield, usage permissions, and discovery metadata remain available and
|
||||||
ResourceNode is loaded.
|
serializable while no ResourceNode is loaded.
|
||||||
|
|
||||||
ResourceNode binds to a record by stable ID and provides presentation,
|
ResourceNode binds to a record by stable ID and provides presentation,
|
||||||
interaction transforms, and active-world discoverability. Unloading a node does
|
interaction transforms, and active-world discoverability. Unloading a node does
|
||||||
@@ -83,9 +83,10 @@ not delete its record. Loading another node with the same stable ID rebinds to
|
|||||||
the existing record and cannot overwrite its amount or reservation with scene
|
the existing record and cannot overwrite its amount or reservation with scene
|
||||||
defaults.
|
defaults.
|
||||||
|
|
||||||
ResourceStateRecord v2 adds the definition facts needed while unloaded. Nested
|
ResourceStateRecord v3 adds `safety_risk`, `comfort_distance`, and
|
||||||
v1 resource records are migrated explicitly and hydrate their missing
|
`discovery_priority` so unloaded resources keep the same target-selection
|
||||||
definition from a matching presentation node when one becomes available.
|
meaning after save/restore. Nested v1 and v2 resource records are migrated
|
||||||
|
explicitly.
|
||||||
|
|
||||||
## Local quicksave boundary
|
## Local quicksave boundary
|
||||||
|
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ func _resolve_resource(
|
|||||||
active_world_adapter: Node
|
active_world_adapter: Node
|
||||||
) -> Dictionary:
|
) -> Dictionary:
|
||||||
var best: Dictionary = {}
|
var best: Dictionary = {}
|
||||||
var best_distance := INF
|
var best_score := INF
|
||||||
for candidate in active_world_adapter.get_resource_candidates(definition.resource_action_id):
|
for candidate in active_world_adapter.get_resource_candidates(definition.resource_action_id):
|
||||||
var node_id := StringName(candidate["target_id"])
|
var node_id := StringName(candidate["target_id"])
|
||||||
var state: ResourceStateRecord = simulation_manager.get_resource_state(node_id)
|
var state: ResourceStateRecord = simulation_manager.get_resource_state(node_id)
|
||||||
if state == null or not state.can_npc_use() or not state.is_available_for(npc.id):
|
if state == null or not state.can_npc_use() or not state.is_available_for(npc.id):
|
||||||
continue
|
continue
|
||||||
var position: Vector3 = candidate["position"]
|
var position: Vector3 = candidate["position"]
|
||||||
var distance := origin.distance_squared_to(position)
|
var score := score_resource_candidate(npc, origin, position, state)
|
||||||
if distance < best_distance:
|
if score < best_score:
|
||||||
best_distance = distance
|
best_score = score
|
||||||
best = candidate
|
best = candidate
|
||||||
if best.is_empty():
|
if best.is_empty():
|
||||||
return {}
|
return {}
|
||||||
@@ -47,3 +47,17 @@ func _resolve_resource(
|
|||||||
if not simulation_manager.reserve_resource(target_id, npc.id):
|
if not simulation_manager.reserve_resource(target_id, npc.id):
|
||||||
return {}
|
return {}
|
||||||
return best
|
return best
|
||||||
|
|
||||||
|
|
||||||
|
func score_resource_candidate(
|
||||||
|
npc: SimNPC, origin: Vector3, position: Vector3, state: ResourceStateRecord
|
||||||
|
) -> float:
|
||||||
|
var distance := origin.distance_to(position)
|
||||||
|
var comfort_overage := maxf(distance - state.get_comfort_distance(), 0.0)
|
||||||
|
var risk_weight := 20.0 + maxf(100.0 - npc.energy, 0.0) * 0.2
|
||||||
|
return (
|
||||||
|
distance
|
||||||
|
+ comfort_overage * 2.5
|
||||||
|
+ state.get_safety_risk() * risk_weight
|
||||||
|
- state.get_discovery_priority()
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ extends RefCounted
|
|||||||
signal changed(state: ResourceStateRecord)
|
signal changed(state: ResourceStateRecord)
|
||||||
signal depleted(state: ResourceStateRecord)
|
signal depleted(state: ResourceStateRecord)
|
||||||
|
|
||||||
const SCHEMA_VERSION := 2
|
const SCHEMA_VERSION := 3
|
||||||
const LEGACY_SCHEMA_VERSION := 1
|
const LEGACY_SCHEMA_VERSION := 1
|
||||||
|
const DISCOVERY_LEGACY_SCHEMA_VERSION := 2
|
||||||
|
|
||||||
var data: Dictionary
|
var data: Dictionary
|
||||||
|
|
||||||
@@ -26,7 +27,10 @@ static func create_from_node(node: ResourceNode) -> ResourceStateRecord:
|
|||||||
"reserved_by": -1,
|
"reserved_by": -1,
|
||||||
"enabled": node.initial_enabled,
|
"enabled": node.initial_enabled,
|
||||||
"can_npcs_use": node.can_npcs_use,
|
"can_npcs_use": node.can_npcs_use,
|
||||||
"can_player_use": node.can_player_use
|
"can_player_use": node.can_player_use,
|
||||||
|
"safety_risk": node.safety_risk,
|
||||||
|
"comfort_distance": node.comfort_distance,
|
||||||
|
"discovery_priority": node.discovery_priority
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,6 +39,8 @@ static func from_dictionary(record_data: Dictionary) -> ResourceStateRecord:
|
|||||||
var version := int(record_data.get("schema_version", -1))
|
var version := int(record_data.get("schema_version", -1))
|
||||||
if version == LEGACY_SCHEMA_VERSION:
|
if version == LEGACY_SCHEMA_VERSION:
|
||||||
record_data = _migrate_v1(record_data)
|
record_data = _migrate_v1(record_data)
|
||||||
|
elif version == DISCOVERY_LEGACY_SCHEMA_VERSION:
|
||||||
|
record_data = _migrate_v2(record_data)
|
||||||
elif version != SCHEMA_VERSION:
|
elif version != SCHEMA_VERSION:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
@@ -48,7 +54,10 @@ static func from_dictionary(record_data: Dictionary) -> ResourceStateRecord:
|
|||||||
"reserved_by",
|
"reserved_by",
|
||||||
"enabled",
|
"enabled",
|
||||||
"can_npcs_use",
|
"can_npcs_use",
|
||||||
"can_player_use"
|
"can_player_use",
|
||||||
|
"safety_risk",
|
||||||
|
"comfort_distance",
|
||||||
|
"discovery_priority"
|
||||||
]
|
]
|
||||||
):
|
):
|
||||||
return null
|
return null
|
||||||
@@ -70,6 +79,15 @@ static func _migrate_v1(legacy_data: Dictionary) -> Dictionary:
|
|||||||
migrated["yield_per_action"] = 0.0
|
migrated["yield_per_action"] = 0.0
|
||||||
migrated["can_npcs_use"] = false
|
migrated["can_npcs_use"] = false
|
||||||
migrated["can_player_use"] = false
|
migrated["can_player_use"] = false
|
||||||
|
return _migrate_v2(migrated)
|
||||||
|
|
||||||
|
|
||||||
|
static func _migrate_v2(legacy_data: Dictionary) -> Dictionary:
|
||||||
|
var migrated := legacy_data.duplicate(true)
|
||||||
|
migrated["schema_version"] = SCHEMA_VERSION
|
||||||
|
migrated["safety_risk"] = clampf(float(migrated.get("safety_risk", 0.0)), 0.0, 1.0)
|
||||||
|
migrated["comfort_distance"] = maxf(float(migrated.get("comfort_distance", 18.0)), 0.1)
|
||||||
|
migrated["discovery_priority"] = float(migrated.get("discovery_priority", 0.0))
|
||||||
return migrated
|
return migrated
|
||||||
|
|
||||||
|
|
||||||
@@ -84,6 +102,9 @@ func apply_definition(node: ResourceNode) -> bool:
|
|||||||
data["yield_per_action"] = node.yield_per_action
|
data["yield_per_action"] = node.yield_per_action
|
||||||
data["can_npcs_use"] = node.can_npcs_use
|
data["can_npcs_use"] = node.can_npcs_use
|
||||||
data["can_player_use"] = node.can_player_use
|
data["can_player_use"] = node.can_player_use
|
||||||
|
data["safety_risk"] = node.safety_risk
|
||||||
|
data["comfort_distance"] = node.comfort_distance
|
||||||
|
data["discovery_priority"] = node.discovery_priority
|
||||||
data["schema_version"] = SCHEMA_VERSION
|
data["schema_version"] = SCHEMA_VERSION
|
||||||
return true
|
return true
|
||||||
|
|
||||||
@@ -132,6 +153,18 @@ func can_player_use_resource() -> bool:
|
|||||||
return bool(data["can_player_use"])
|
return bool(data["can_player_use"])
|
||||||
|
|
||||||
|
|
||||||
|
func get_safety_risk() -> float:
|
||||||
|
return clampf(float(data["safety_risk"]), 0.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
func get_comfort_distance() -> float:
|
||||||
|
return maxf(float(data["comfort_distance"]), 0.1)
|
||||||
|
|
||||||
|
|
||||||
|
func get_discovery_priority() -> float:
|
||||||
|
return float(data["discovery_priority"])
|
||||||
|
|
||||||
|
|
||||||
func is_depleted() -> bool:
|
func is_depleted() -> bool:
|
||||||
return get_amount_remaining() <= 0.0
|
return get_amount_remaining() <= 0.0
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class FakeActiveWorld:
|
|||||||
func get_resource_candidates(_action_id: StringName) -> Array[Dictionary]:
|
func get_resource_candidates(_action_id: StringName) -> Array[Dictionary]:
|
||||||
return resource_candidates
|
return resource_candidates
|
||||||
|
|
||||||
func get_activity_target(_action_id: StringName) -> Dictionary:
|
func get_activity_target(_action_id: StringName, _origin: Vector3 = Vector3.ZERO) -> Dictionary:
|
||||||
return {"target_id": "", "position": activity_position}
|
return {"target_id": "", "position": activity_position}
|
||||||
|
|
||||||
|
|
||||||
@@ -69,6 +69,7 @@ func _test_selection_and_execution_are_separate() -> void:
|
|||||||
func _test_target_resolution_and_travel_are_separate() -> void:
|
func _test_target_resolution_and_travel_are_separate() -> void:
|
||||||
var adapter := FakeActiveWorld.new()
|
var adapter := FakeActiveWorld.new()
|
||||||
adapter.resource_candidates = [
|
adapter.resource_candidates = [
|
||||||
|
{"target_id": "boundary_risky_bush", "position": Vector3(1.0, 0.0, 0.0)},
|
||||||
{"target_id": "boundary_bush", "position": Vector3(3.0, 0.0, 2.0)}
|
{"target_id": "boundary_bush", "position": Vector3(3.0, 0.0, 2.0)}
|
||||||
]
|
]
|
||||||
root.add_child(adapter)
|
root.add_child(adapter)
|
||||||
@@ -91,20 +92,42 @@ func _test_target_resolution_and_travel_are_separate() -> void:
|
|||||||
"reserved_by": -1,
|
"reserved_by": -1,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"can_npcs_use": true,
|
"can_npcs_use": true,
|
||||||
"can_player_use": true
|
"can_player_use": true,
|
||||||
|
"safety_risk": 0.0,
|
||||||
|
"comfort_distance": 18.0,
|
||||||
|
"discovery_priority": 0.0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
manager.resource_states[resource_state.get_node_id()] = resource_state
|
manager.resource_states[resource_state.get_node_id()] = resource_state
|
||||||
|
var risky_resource_state := ResourceStateRecord.from_dictionary(
|
||||||
|
{
|
||||||
|
"schema_version": ResourceStateRecord.SCHEMA_VERSION,
|
||||||
|
"node_id": "boundary_risky_bush",
|
||||||
|
"action_id": String(SimulationIds.ACTION_GATHER_FOOD),
|
||||||
|
"resource_id": String(SimulationIds.RESOURCE_FOOD),
|
||||||
|
"amount_remaining": 5.0,
|
||||||
|
"yield_per_action": 1.0,
|
||||||
|
"reserved_by": -1,
|
||||||
|
"enabled": true,
|
||||||
|
"can_npcs_use": true,
|
||||||
|
"can_player_use": true,
|
||||||
|
"safety_risk": 1.0,
|
||||||
|
"comfort_distance": 18.0,
|
||||||
|
"discovery_priority": 0.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
manager.resource_states[risky_resource_state.get_node_id()] = risky_resource_state
|
||||||
|
|
||||||
var npc: SimNPC = manager.npcs[0]
|
var npc: SimNPC = manager.npcs[0]
|
||||||
|
npc.energy = 40.0
|
||||||
npc.set_task(SimulationIds.ACTION_GATHER_FOOD)
|
npc.set_task(SimulationIds.ACTION_GATHER_FOOD)
|
||||||
_check(
|
_check(
|
||||||
manager.resolve_npc_target(npc.id, Vector3.ZERO),
|
manager.resolve_npc_target(npc.id, Vector3.ZERO),
|
||||||
"Target resolver should resolve a resource from adapter facts"
|
"Target resolver should resolve a scored resource from adapter facts"
|
||||||
)
|
)
|
||||||
_check(
|
_check(
|
||||||
npc.target_id == &"boundary_bush" and resource_state.get_reserved_by() == npc.id,
|
npc.target_id == &"boundary_bush" and resource_state.get_reserved_by() == npc.id,
|
||||||
"Target resolver should authoritatively assign and reserve the target"
|
"Target resolver should prefer the safer comfortable resource over risky proximity"
|
||||||
)
|
)
|
||||||
_check(
|
_check(
|
||||||
travel_requests == [Vector3(3.0, 0.0, 2.0)],
|
travel_requests == [Vector3(3.0, 0.0, 2.0)],
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ func _run() -> void:
|
|||||||
"reserved_by": -1,
|
"reserved_by": -1,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"can_npcs_use": true,
|
"can_npcs_use": true,
|
||||||
"can_player_use": true
|
"can_player_use": true,
|
||||||
|
"safety_risk": 0.0,
|
||||||
|
"comfort_distance": 18.0,
|
||||||
|
"discovery_priority": 0.0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
manager.resource_states[resource.get_node_id()] = resource
|
manager.resource_states[resource.get_node_id()] = resource
|
||||||
|
|||||||
@@ -116,6 +116,16 @@ func _run() -> void:
|
|||||||
ids == ["berry_bush_01", "berry_bush_02", "tree_01", "tree_02"],
|
ids == ["berry_bush_01", "berry_bush_02", "tree_01", "tree_02"],
|
||||||
"Jajce resources should preserve all stable IDs"
|
"Jajce resources should preserve all stable IDs"
|
||||||
)
|
)
|
||||||
|
var metadata_valid := true
|
||||||
|
for resource in resources:
|
||||||
|
var node := resource as ResourceNode
|
||||||
|
metadata_valid = (
|
||||||
|
metadata_valid
|
||||||
|
and node.comfort_distance > 0.0
|
||||||
|
and node.safety_risk >= 0.0
|
||||||
|
and node.safety_risk <= 1.0
|
||||||
|
)
|
||||||
|
_check(metadata_valid, "Jajce resources should define discovery metadata")
|
||||||
_check(world.has_node("WorldObjects/StorageSites"), "JajceWorld should expose StorageSites")
|
_check(world.has_node("WorldObjects/StorageSites"), "JajceWorld should expose StorageSites")
|
||||||
var pantry := world.get_node("WorldObjects/StorageSites/VillagePantry") as StorageNode
|
var pantry := world.get_node("WorldObjects/StorageSites/VillagePantry") as StorageNode
|
||||||
_check(pantry != null, "JajceWorld should include a typed VillagePantry StorageNode")
|
_check(pantry != null, "JajceWorld should include a typed VillagePantry StorageNode")
|
||||||
|
|||||||
@@ -156,6 +156,11 @@ func _test_legacy_resource_migration() -> void:
|
|||||||
is_equal_approx(migrated.get_amount_remaining(), 6.0) and migrated.get_reserved_by() == 12,
|
is_equal_approx(migrated.get_amount_remaining(), 6.0) and migrated.get_reserved_by() == 12,
|
||||||
"Resource migration should preserve mutable authority"
|
"Resource migration should preserve mutable authority"
|
||||||
)
|
)
|
||||||
|
_check(
|
||||||
|
is_equal_approx(migrated.get_safety_risk(), 0.0)
|
||||||
|
and is_equal_approx(migrated.get_comfort_distance(), 18.0),
|
||||||
|
"Resource migration should default discovery metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _test_legacy_npc_migration() -> void:
|
func _test_legacy_npc_migration() -> void:
|
||||||
|
|||||||
@@ -225,11 +225,17 @@ scale = Vector3(1.25, 1.25, 1.25)
|
|||||||
[node name="BerryBush_01" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
[node name="BerryBush_01" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
||||||
position = Vector3(-6, 0, -8)
|
position = Vector3(-6, 0, -8)
|
||||||
node_id = &"berry_bush_01"
|
node_id = &"berry_bush_01"
|
||||||
|
safety_risk = 0.05
|
||||||
|
comfort_distance = 18.0
|
||||||
|
discovery_priority = 1.0
|
||||||
|
|
||||||
[node name="BerryBush_02" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
[node name="BerryBush_02" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
||||||
position = Vector3(-10, 0, -4)
|
position = Vector3(-10, 0, -4)
|
||||||
node_id = &"berry_bush_02"
|
node_id = &"berry_bush_02"
|
||||||
initial_amount = 8.0
|
initial_amount = 8.0
|
||||||
|
safety_risk = 0.08
|
||||||
|
comfort_distance = 20.0
|
||||||
|
discovery_priority = 0.5
|
||||||
|
|
||||||
[node name="Tree_01" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
[node name="Tree_01" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
||||||
position = Vector3(6, 0, -8)
|
position = Vector3(6, 0, -8)
|
||||||
@@ -238,6 +244,8 @@ action_id = &"gather_wood"
|
|||||||
resource_id = &"wood"
|
resource_id = &"wood"
|
||||||
initial_amount = 15.0
|
initial_amount = 15.0
|
||||||
yield_per_action = 3.0
|
yield_per_action = 3.0
|
||||||
|
safety_risk = 0.12
|
||||||
|
comfort_distance = 24.0
|
||||||
|
|
||||||
[node name="Tree_02" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
[node name="Tree_02" parent="WorldObjects/ResourceNodes" instance=ExtResource("2_resource")]
|
||||||
position = Vector3(10, 0, -4)
|
position = Vector3(10, 0, -4)
|
||||||
@@ -246,6 +254,8 @@ action_id = &"gather_wood"
|
|||||||
resource_id = &"wood"
|
resource_id = &"wood"
|
||||||
initial_amount = 12.0
|
initial_amount = 12.0
|
||||||
yield_per_action = 3.0
|
yield_per_action = 3.0
|
||||||
|
safety_risk = 0.16
|
||||||
|
comfort_distance = 26.0
|
||||||
|
|
||||||
[node name="StorageSites" type="Node3D" parent="WorldObjects"]
|
[node name="StorageSites" type="Node3D" parent="WorldObjects"]
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ static var _all: Array[ResourceNode] = []
|
|||||||
@export var initial_enabled: bool = true
|
@export var initial_enabled: bool = true
|
||||||
@export var can_npcs_use: bool = true
|
@export var can_npcs_use: bool = true
|
||||||
@export var can_player_use: bool = true
|
@export var can_player_use: bool = true
|
||||||
|
@export_range(0.0, 1.0, 0.05) var safety_risk := 0.0
|
||||||
|
@export var comfort_distance := 18.0
|
||||||
|
@export var discovery_priority := 0.0
|
||||||
@export var hide_visual_when_depleted: bool = false
|
@export var hide_visual_when_depleted: bool = false
|
||||||
|
|
||||||
@onready var interaction_point: Marker3D = $InteractionPoint
|
@onready var interaction_point: Marker3D = $InteractionPoint
|
||||||
@@ -119,6 +122,8 @@ func _update_presentation() -> void:
|
|||||||
return
|
return
|
||||||
var label := $DebugLabel as Label3D
|
var label := $DebugLabel as Label3D
|
||||||
var text := "%s\n%s %.1f" % [node_id, resource_id, get_amount_remaining()]
|
var text := "%s\n%s %.1f" % [node_id, resource_id, get_amount_remaining()]
|
||||||
|
if state != null:
|
||||||
|
text += "\nrisk:%.2f comfort:%.0f" % [state.get_safety_risk(), state.get_comfort_distance()]
|
||||||
if get_reserved_by() >= 0:
|
if get_reserved_by() >= 0:
|
||||||
text += "\nheld:%d" % get_reserved_by()
|
text += "\nheld:%d" % get_reserved_by()
|
||||||
if is_depleted():
|
if is_depleted():
|
||||||
|
|||||||
Reference in New Issue
Block a user