feat: give the player a real carried inventory and deposit transactions
This commit is contained in:
@@ -25,13 +25,14 @@ height = 1.7
|
|||||||
|
|
||||||
[node name="JajceWorld" parent="." unique_id=1023795383 instance=ExtResource("11_jajce")]
|
[node name="JajceWorld" parent="." unique_id=1023795383 instance=ExtResource("11_jajce")]
|
||||||
|
|
||||||
[node name="Player" type="CharacterBody3D" parent="." unique_id=2022843760 node_paths=PackedStringArray("camera_rig", "simulation_manager", "world_view_manager", "pantry_storage", "guard_site", "study_site")]
|
[node name="Player" type="CharacterBody3D" parent="." unique_id=2022843760 node_paths=PackedStringArray("camera_rig", "simulation_manager", "world_view_manager", "pantry_storage", "woodpile_storage", "guard_site", "study_site")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
|
||||||
script = ExtResource("1_h2yge")
|
script = ExtResource("1_h2yge")
|
||||||
camera_rig = NodePath("../CameraRig")
|
camera_rig = NodePath("../CameraRig")
|
||||||
simulation_manager = NodePath("../SimulationManager")
|
simulation_manager = NodePath("../SimulationManager")
|
||||||
world_view_manager = NodePath("../WorldViewManager")
|
world_view_manager = NodePath("../WorldViewManager")
|
||||||
pantry_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
pantry_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
||||||
|
woodpile_storage = NodePath("../JajceWorld/WorldObjects/StorageSites/VillageWoodpile")
|
||||||
guard_site = NodePath("../JajceWorld/WorldObjects/ActivitySites/GuardPost")
|
guard_site = NodePath("../JajceWorld/WorldObjects/ActivitySites/GuardPost")
|
||||||
study_site = NodePath("../JajceWorld/WorldObjects/ActivitySites/StudyDesk")
|
study_site = NodePath("../JajceWorld/WorldObjects/ActivitySites/StudyDesk")
|
||||||
stomach_capacity_for_food = 10
|
stomach_capacity_for_food = 10
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ extends RefCounted
|
|||||||
const KIND_ANIMAL := &"animal"
|
const KIND_ANIMAL := &"animal"
|
||||||
const KIND_RESOURCE := &"resource"
|
const KIND_RESOURCE := &"resource"
|
||||||
const KIND_PANTRY := &"pantry"
|
const KIND_PANTRY := &"pantry"
|
||||||
|
const KIND_DEPOSIT := &"deposit"
|
||||||
const KIND_GUARD := &"guard"
|
const KIND_GUARD := &"guard"
|
||||||
const KIND_STUDY := &"study"
|
const KIND_STUDY := &"study"
|
||||||
|
|
||||||
|
|||||||
+93
-8
@@ -11,6 +11,7 @@ signal interaction_feedback(heading: String, message: String, succeeded: bool)
|
|||||||
@export var world_view_manager: Node
|
@export var world_view_manager: Node
|
||||||
|
|
||||||
@export var pantry_storage: StorageNode
|
@export var pantry_storage: StorageNode
|
||||||
|
@export var woodpile_storage: StorageNode
|
||||||
@export var guard_site: ActivitySite
|
@export var guard_site: ActivitySite
|
||||||
@export var study_site: ActivitySite
|
@export var study_site: ActivitySite
|
||||||
|
|
||||||
@@ -69,6 +70,8 @@ func try_interact() -> void:
|
|||||||
_execute_animal_interaction(context)
|
_execute_animal_interaction(context)
|
||||||
PlayerInteractionResult.KIND_RESOURCE:
|
PlayerInteractionResult.KIND_RESOURCE:
|
||||||
_execute_resource_interaction(context)
|
_execute_resource_interaction(context)
|
||||||
|
PlayerInteractionResult.KIND_DEPOSIT:
|
||||||
|
_execute_deposit_interaction(context)
|
||||||
PlayerInteractionResult.KIND_GUARD:
|
PlayerInteractionResult.KIND_GUARD:
|
||||||
simulation_manager.add_safety(3.0)
|
simulation_manager.add_safety(3.0)
|
||||||
interaction_feedback.emit(
|
interaction_feedback.emit(
|
||||||
@@ -112,6 +115,20 @@ func get_interaction_context() -> PlayerInteractionResult:
|
|||||||
"Work here · +2 knowledge",
|
"Work here · +2 knowledge",
|
||||||
study_site
|
study_site
|
||||||
)
|
)
|
||||||
|
if is_near_storage(woodpile_storage) and _get_player_carried(SimulationIds.RESOURCE_WOOD) > 0.0:
|
||||||
|
return _build_deposit_context(
|
||||||
|
SimulationIds.RESOURCE_WOOD,
|
||||||
|
SimulationIds.STORAGE_VILLAGE_WOODPILE,
|
||||||
|
woodpile_storage,
|
||||||
|
"Village woodpile"
|
||||||
|
)
|
||||||
|
if is_near_storage(pantry_storage) and _get_player_carried(SimulationIds.RESOURCE_FOOD) > 0.0:
|
||||||
|
return _build_deposit_context(
|
||||||
|
SimulationIds.RESOURCE_FOOD,
|
||||||
|
SimulationIds.STORAGE_VILLAGE_PANTRY,
|
||||||
|
pantry_storage,
|
||||||
|
"Village pantry"
|
||||||
|
)
|
||||||
if is_near_storage(pantry_storage):
|
if is_near_storage(pantry_storage):
|
||||||
return _build_pantry_context()
|
return _build_pantry_context()
|
||||||
return null
|
return null
|
||||||
@@ -290,14 +307,40 @@ func _build_resource_context(node: ResourceNode) -> PlayerInteractionResult:
|
|||||||
definition.display_name if definition != null else String(node.action_id).capitalize()
|
definition.display_name if definition != null else String(node.action_id).capitalize()
|
||||||
)
|
)
|
||||||
var display_name := String(node.node_id).replace("_", " ").capitalize()
|
var display_name := String(node.node_id).replace("_", " ").capitalize()
|
||||||
|
var carried := _get_player_carried(node.resource_id)
|
||||||
|
var detail := (
|
||||||
|
"%s · %.1f remaining · carrying %.1f" % [display_name, node.get_amount_remaining(), carried]
|
||||||
|
)
|
||||||
|
if _get_available_carry() <= 0.0:
|
||||||
|
detail = "Hands full — deposit before gathering"
|
||||||
return PlayerInteractionResult.new(
|
return PlayerInteractionResult.new(
|
||||||
PlayerInteractionResult.KIND_RESOURCE,
|
PlayerInteractionResult.KIND_RESOURCE,
|
||||||
node.action_id,
|
node.action_id,
|
||||||
node.node_id,
|
node.node_id,
|
||||||
display_name,
|
display_name,
|
||||||
prompt,
|
prompt,
|
||||||
"%s · %.1f remaining" % [display_name, node.get_amount_remaining()],
|
detail,
|
||||||
node
|
node,
|
||||||
|
"" if _get_available_carry() > 0.0 else "You cannot carry more right now."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_deposit_context(
|
||||||
|
resource_id: StringName, target_id: StringName, target_node: StorageNode, display_name: String
|
||||||
|
) -> PlayerInteractionResult:
|
||||||
|
var carried := _get_player_carried(resource_id)
|
||||||
|
return PlayerInteractionResult.new(
|
||||||
|
PlayerInteractionResult.KIND_DEPOSIT,
|
||||||
|
(
|
||||||
|
SimulationIds.ACTION_DEPOSIT_FOOD
|
||||||
|
if resource_id == SimulationIds.RESOURCE_FOOD
|
||||||
|
else SimulationIds.ACTION_DEPOSIT_WOOD
|
||||||
|
),
|
||||||
|
target_id,
|
||||||
|
display_name,
|
||||||
|
"Deposit %s" % String(resource_id).capitalize(),
|
||||||
|
"%.1f %s in your hands" % [carried, String(resource_id).to_lower()],
|
||||||
|
target_node
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -349,23 +392,51 @@ func _execute_animal_interaction(context: PlayerInteractionResult) -> void:
|
|||||||
|
|
||||||
func _execute_resource_interaction(context: PlayerInteractionResult) -> void:
|
func _execute_resource_interaction(context: PlayerInteractionResult) -> void:
|
||||||
var node := context.target_node as ResourceNode
|
var node := context.target_node as ResourceNode
|
||||||
if node == null or not simulation_manager.has_method("harvest_resource_node"):
|
if node == null or not simulation_manager.has_method("player_gather"):
|
||||||
push_error("Player: SimulationManager cannot harvest the resolved ResourceNode")
|
push_error("Player: SimulationManager cannot gather the resolved ResourceNode")
|
||||||
return
|
return
|
||||||
var extracted: float = simulation_manager.harvest_resource_node(node)
|
var extracted: float = simulation_manager.player_gather(node)
|
||||||
if extracted <= 0.0:
|
if extracted <= 0.0:
|
||||||
interaction_feedback.emit(
|
interaction_feedback.emit(
|
||||||
"%s is unavailable" % context.display_name, "Nothing could be gathered.", false
|
"%s is unavailable" % context.display_name,
|
||||||
|
(
|
||||||
|
context.blocked_reason
|
||||||
|
if not context.blocked_reason.is_empty()
|
||||||
|
else "Nothing could be gathered."
|
||||||
|
),
|
||||||
|
false
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
var destination := "pantry" if node.resource_id == SimulationIds.RESOURCE_FOOD else "woodpile"
|
var carried := _get_player_carried(node.resource_id)
|
||||||
interaction_feedback.emit(
|
interaction_feedback.emit(
|
||||||
"%s gathered" % String(node.resource_id).capitalize(),
|
"%s gathered" % String(node.resource_id).capitalize(),
|
||||||
"%.1f moved to the village %s." % [extracted, destination],
|
"%.1f added to your hands · carrying %.1f" % [extracted, carried],
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _deposit_carried(resource_id: StringName) -> float:
|
||||||
|
if not simulation_manager.has_method("player_deposit"):
|
||||||
|
push_error("Player: SimulationManager cannot deposit carried resources")
|
||||||
|
return 0.0
|
||||||
|
return simulation_manager.player_deposit(resource_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _execute_deposit_interaction(context: PlayerInteractionResult) -> void:
|
||||||
|
var resource_id := (
|
||||||
|
SimulationIds.RESOURCE_FOOD
|
||||||
|
if context.action_id == SimulationIds.ACTION_DEPOSIT_FOOD
|
||||||
|
else SimulationIds.RESOURCE_WOOD
|
||||||
|
)
|
||||||
|
var deposited: float = _deposit_carried(resource_id)
|
||||||
|
if deposited <= 0.0:
|
||||||
|
interaction_feedback.emit("Deposit blocked", "You are not carrying that here.", false)
|
||||||
|
return
|
||||||
|
interaction_feedback.emit(
|
||||||
|
"Deposited to %s" % context.display_name, "%.1f moved into the village." % deposited, true
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _execute_pantry_interaction(context: PlayerInteractionResult) -> void:
|
func _execute_pantry_interaction(context: PlayerInteractionResult) -> void:
|
||||||
if not context.is_available():
|
if not context.is_available():
|
||||||
interaction_feedback.emit("The pantry is empty", context.blocked_reason, false)
|
interaction_feedback.emit("The pantry is empty", context.blocked_reason, false)
|
||||||
@@ -424,6 +495,20 @@ func _get_pantry_food() -> float:
|
|||||||
return pantry.get_amount(SimulationIds.RESOURCE_FOOD) if pantry != null else 0.0
|
return pantry.get_amount(SimulationIds.RESOURCE_FOOD) if pantry != null else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func _get_player_carried(resource_id: StringName) -> float:
|
||||||
|
if simulation_manager == null or not simulation_manager.has_method("get_player_state"):
|
||||||
|
return 0.0
|
||||||
|
var state: PlayerStateRecord = simulation_manager.get_player_state()
|
||||||
|
return state.get_inventory_amount(resource_id) if state != null else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func _get_available_carry() -> float:
|
||||||
|
if simulation_manager == null or not simulation_manager.has_method("get_player_state"):
|
||||||
|
return 0.0
|
||||||
|
var state: PlayerStateRecord = simulation_manager.get_player_state()
|
||||||
|
return state.get_available_carry() if state != null else 0.0
|
||||||
|
|
||||||
|
|
||||||
func is_near_storage(storage_node: StorageNode) -> bool:
|
func is_near_storage(storage_node: StorageNode) -> bool:
|
||||||
if storage_node == null:
|
if storage_node == null:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ signal player_quest_expired(quest: PlayerQuestRecord)
|
|||||||
signal player_standing_changed(standing: PlayerStandingRecord)
|
signal player_standing_changed(standing: PlayerStandingRecord)
|
||||||
signal player_tier_advanced(tier: int, tier_name: String)
|
signal player_tier_advanced(tier: int, tier_name: String)
|
||||||
signal player_needs_changed(state: PlayerStateRecord)
|
signal player_needs_changed(state: PlayerStateRecord)
|
||||||
|
signal player_inventory_changed(state: PlayerStateRecord)
|
||||||
signal player_died(state: PlayerStateRecord)
|
signal player_died(state: PlayerStateRecord)
|
||||||
|
|
||||||
var village := SimVillage.new()
|
var village := SimVillage.new()
|
||||||
@@ -85,6 +86,8 @@ func _ready() -> void:
|
|||||||
event_log.event_recorded.connect(_on_economic_event_recorded)
|
event_log.event_recorded.connect(_on_economic_event_recorded)
|
||||||
event_recorder.configure(event_log, Callable(self, "get_tick_count_for_recording"))
|
event_recorder.configure(event_log, Callable(self, "get_tick_count_for_recording"))
|
||||||
event_recorder.set_npcs(npcs)
|
event_recorder.set_npcs(npcs)
|
||||||
|
player_needs.configure(economy, event_recorder)
|
||||||
|
player_needs.set_resource_states(resource_states)
|
||||||
economy.inventory_changed.connect(_on_economy_inventory_changed)
|
economy.inventory_changed.connect(_on_economy_inventory_changed)
|
||||||
economy.economic_event_requested.connect(event_recorder.record_economic)
|
economy.economic_event_requested.connect(event_recorder.record_economic)
|
||||||
economy.narrative_event_requested.connect(event_recorder.record_narrative)
|
economy.narrative_event_requested.connect(event_recorder.record_narrative)
|
||||||
@@ -578,30 +581,28 @@ func _on_economic_event_recorded(event: EconomicEventRecord) -> void:
|
|||||||
for learned_record in learned_records:
|
for learned_record in learned_records:
|
||||||
_apply_new_event_knowledge(learned_record, event)
|
_apply_new_event_knowledge(learned_record, event)
|
||||||
if StringName(event.data["event_type"]) == SimulationIds.EVENT_ANIMAL_FED:
|
if StringName(event.data["event_type"]) == SimulationIds.EVENT_ANIMAL_FED:
|
||||||
_resolve_animal_care_quest(event)
|
_finish_quest_resolution(
|
||||||
|
player_quest_system.on_animal_fed(
|
||||||
|
animal_care.get_state(StringName(event.data["destination_id"])), event, tick_count
|
||||||
|
)
|
||||||
|
)
|
||||||
_update_opportunity_from_event(event)
|
_update_opportunity_from_event(event)
|
||||||
economic_event_recorded.emit(event)
|
economic_event_recorded.emit(event)
|
||||||
|
|
||||||
|
|
||||||
func _resolve_animal_care_quest(event: EconomicEventRecord) -> void:
|
|
||||||
var animal_state := animal_care.get_state(StringName(event.data["destination_id"]))
|
|
||||||
var resolution := player_quest_system.on_animal_fed(animal_state, event, tick_count)
|
|
||||||
_finish_quest_resolution(resolution)
|
|
||||||
|
|
||||||
|
|
||||||
func _finish_quest_resolution(resolution: Dictionary) -> void:
|
func _finish_quest_resolution(resolution: Dictionary) -> void:
|
||||||
if not resolution.has("quest"):
|
if not resolution.has("quest"):
|
||||||
return
|
return
|
||||||
var quest: PlayerQuestRecord = resolution["quest"]
|
var quest: PlayerQuestRecord = resolution["quest"]
|
||||||
if resolution["completed"]:
|
if not resolution["completed"]:
|
||||||
|
player_quest_expired.emit(quest)
|
||||||
|
return
|
||||||
player_quest_completed.emit(quest)
|
player_quest_completed.emit(quest)
|
||||||
var standing := player_quest_system.standing
|
var standing := player_quest_system.standing
|
||||||
player_standing_changed.emit(standing)
|
player_standing_changed.emit(standing)
|
||||||
if standing.get_tier() > _last_player_tier:
|
if standing.get_tier() > _last_player_tier:
|
||||||
_last_player_tier = standing.get_tier()
|
_last_player_tier = standing.get_tier()
|
||||||
player_tier_advanced.emit(standing.get_tier(), standing.get_tier_name())
|
player_tier_advanced.emit(standing.get_tier(), standing.get_tier_name())
|
||||||
else:
|
|
||||||
player_quest_expired.emit(quest)
|
|
||||||
|
|
||||||
|
|
||||||
func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bool:
|
func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bool:
|
||||||
@@ -609,12 +610,11 @@ func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bo
|
|||||||
var listener := _find_npc_by_id(listener_id)
|
var listener := _find_npc_by_id(listener_id)
|
||||||
if not _can_communicate_at_shared_activity(speaker, listener):
|
if not _can_communicate_at_shared_activity(speaker, listener):
|
||||||
return false
|
return false
|
||||||
var known_event_ids := event_knowledge_system.get_communicable_event_ids(
|
for event_id in event_knowledge_system.get_communicable_event_ids(
|
||||||
speaker.id,
|
speaker.id,
|
||||||
_get_lasting_event_ids(speaker.id),
|
_get_lasting_event_ids(speaker.id),
|
||||||
opportunity_system.get_open_trigger_event_id()
|
opportunity_system.get_open_trigger_event_id()
|
||||||
)
|
):
|
||||||
for event_id in known_event_ids:
|
|
||||||
var event := event_log.get_by_id(event_id)
|
var event := event_log.get_by_id(event_id)
|
||||||
if event == null:
|
if event == null:
|
||||||
continue
|
continue
|
||||||
@@ -742,22 +742,21 @@ func get_active_player_quest() -> PlayerQuestRecord:
|
|||||||
|
|
||||||
|
|
||||||
func _advance_player_needs() -> void:
|
func _advance_player_needs() -> void:
|
||||||
if not player_needs.advance(village):
|
if player_needs.advance(village):
|
||||||
player_needs_changed.emit(player_needs.state)
|
|
||||||
return
|
|
||||||
player_died.emit(player_needs.state)
|
player_died.emit(player_needs.state)
|
||||||
player_needs.apply_death_consequence(player_quest_system.standing)
|
player_needs.apply_death_consequence(player_quest_system.standing)
|
||||||
_last_player_tier = player_quest_system.standing.get_tier()
|
_last_player_tier = player_quest_system.standing.get_tier()
|
||||||
player_standing_changed.emit(player_quest_system.standing)
|
player_standing_changed.emit(player_quest_system.standing)
|
||||||
|
return
|
||||||
|
player_needs_changed.emit(player_needs.state)
|
||||||
|
|
||||||
|
|
||||||
func respawn_player() -> bool:
|
func respawn_player() -> bool:
|
||||||
var was_dead := player_needs.state.is_dead()
|
if not player_needs.state.is_dead():
|
||||||
|
return false
|
||||||
player_needs.respawn()
|
player_needs.respawn()
|
||||||
if was_dead and not player_needs.state.is_dead():
|
|
||||||
player_needs_changed.emit(player_needs.state)
|
player_needs_changed.emit(player_needs.state)
|
||||||
return true
|
return true
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
func get_player_state() -> PlayerStateRecord:
|
func get_player_state() -> PlayerStateRecord:
|
||||||
@@ -771,6 +770,21 @@ func player_eat(amount: float) -> float:
|
|||||||
return eaten
|
return eaten
|
||||||
|
|
||||||
|
|
||||||
|
func player_gather(node: ResourceNode) -> float:
|
||||||
|
var gathered := player_needs.gather_node(node)
|
||||||
|
if gathered > 0.0:
|
||||||
|
player_inventory_changed.emit(player_needs.state)
|
||||||
|
return gathered
|
||||||
|
|
||||||
|
|
||||||
|
func player_deposit(resource_id: StringName) -> float:
|
||||||
|
var deposited := player_needs.deposit_resource(resource_id)
|
||||||
|
if deposited > 0.0:
|
||||||
|
village_changed.emit(village)
|
||||||
|
player_inventory_changed.emit(player_needs.state)
|
||||||
|
return deposited
|
||||||
|
|
||||||
|
|
||||||
func _consider_animal_care_quests() -> void:
|
func _consider_animal_care_quests() -> void:
|
||||||
var pantry: StorageStateRecord = get_pantry()
|
var pantry: StorageStateRecord = get_pantry()
|
||||||
for animal_state in animal_care.get_all_states():
|
for animal_state in animal_care.get_all_states():
|
||||||
|
|||||||
@@ -537,6 +537,8 @@ static func _is_valid_supply(
|
|||||||
var event_type := StringName(event.data["event_type"])
|
var event_type := StringName(event.data["event_type"])
|
||||||
var actor_id := int(event.data["actor_id"])
|
var actor_id := int(event.data["actor_id"])
|
||||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||||
|
if actor_id < 0:
|
||||||
|
return StringName(event.data["source_id"]) == &"player_carry"
|
||||||
return (
|
return (
|
||||||
_find_npc(actor_id, npcs) != null
|
_find_npc(actor_id, npcs) != null
|
||||||
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
||||||
|
|||||||
@@ -2,6 +2,18 @@ class_name PlayerNeedsSystem
|
|||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
var state := PlayerStateRecord.create()
|
var state := PlayerStateRecord.create()
|
||||||
|
var economy: RefCounted
|
||||||
|
var event_recorder: RefCounted
|
||||||
|
var resource_states: Dictionary = {}
|
||||||
|
|
||||||
|
|
||||||
|
func configure(economy_service: RefCounted, recorder: RefCounted) -> void:
|
||||||
|
economy = economy_service
|
||||||
|
event_recorder = recorder
|
||||||
|
|
||||||
|
|
||||||
|
func set_resource_states(states: Dictionary) -> void:
|
||||||
|
resource_states = states
|
||||||
|
|
||||||
|
|
||||||
func advance(village: SimVillage) -> bool:
|
func advance(village: SimVillage) -> bool:
|
||||||
@@ -15,8 +27,80 @@ func advance(village: SimVillage) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
func eat(amount: float) -> float:
|
func eat(amount: float) -> float:
|
||||||
var eaten := state.eat(amount)
|
return state.eat(amount)
|
||||||
return eaten
|
|
||||||
|
|
||||||
|
func gather_node(node: ResourceNode) -> float:
|
||||||
|
if node == null:
|
||||||
|
return 0.0
|
||||||
|
var resource_state := resource_states.get(node.node_id) as ResourceStateRecord
|
||||||
|
if resource_state == null or not resource_state.can_player_use_resource():
|
||||||
|
return 0.0
|
||||||
|
var available := state.get_available_carry()
|
||||||
|
if available <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
var extracted := resource_state.extract(minf(resource_state.get_yield_per_action(), available))
|
||||||
|
if extracted <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
var carried := state.add_inventory(resource_state.get_resource_id(), extracted)
|
||||||
|
if carried <= 0.0:
|
||||||
|
resource_state.data["amount_remaining"] = (
|
||||||
|
resource_state.get_amount_remaining() + extracted
|
||||||
|
)
|
||||||
|
return 0.0
|
||||||
|
var event_position := (
|
||||||
|
node.interaction_point.global_position
|
||||||
|
if node.interaction_point != null
|
||||||
|
else node.global_position
|
||||||
|
)
|
||||||
|
event_recorder.record_economic_at(
|
||||||
|
SimulationIds.EVENT_RESOURCE_EXTRACTED,
|
||||||
|
-1,
|
||||||
|
resource_state.get_node_id(),
|
||||||
|
&"player_carry",
|
||||||
|
resource_state.get_resource_id(),
|
||||||
|
carried,
|
||||||
|
event_position
|
||||||
|
)
|
||||||
|
if resource_state.get_amount_remaining() <= 0.0:
|
||||||
|
event_recorder.record_narrative_at(
|
||||||
|
SimulationIds.EVENT_RESOURCE_DEPLETED,
|
||||||
|
-1,
|
||||||
|
resource_state.get_node_id(),
|
||||||
|
"",
|
||||||
|
event_position
|
||||||
|
)
|
||||||
|
return carried
|
||||||
|
|
||||||
|
|
||||||
|
func deposit_resource(resource_id: StringName) -> float:
|
||||||
|
if resource_id.is_empty():
|
||||||
|
return 0.0
|
||||||
|
var carried := state.get_inventory_amount(resource_id)
|
||||||
|
if carried <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
if economy == null or not economy.has_method("get_storage_for_resource"):
|
||||||
|
return 0.0
|
||||||
|
var storage: StorageStateRecord = economy.get_storage_for_resource(resource_id)
|
||||||
|
if storage == null:
|
||||||
|
return 0.0
|
||||||
|
var deposited: float = economy.deposit_resource(resource_id, carried)
|
||||||
|
if deposited <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
state.remove_inventory(resource_id, deposited)
|
||||||
|
event_recorder.record_economic(
|
||||||
|
SimulationIds.EVENT_STORAGE_DEPOSITED,
|
||||||
|
-1,
|
||||||
|
&"player_carry",
|
||||||
|
storage.get_storage_id(),
|
||||||
|
resource_id,
|
||||||
|
deposited
|
||||||
|
)
|
||||||
|
return deposited
|
||||||
|
|
||||||
|
|
||||||
|
func get_carried_amount(item_id: StringName) -> float:
|
||||||
|
return state.get_inventory_amount(item_id)
|
||||||
|
|
||||||
|
|
||||||
func respawn() -> void:
|
func respawn() -> void:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const STACK_THRESHOLD := 90.0
|
|||||||
const DEATH_STANDING_PENALTY := 0.5
|
const DEATH_STANDING_PENALTY := 0.5
|
||||||
const RESET_HUNGER := 20.0
|
const RESET_HUNGER := 20.0
|
||||||
const RESET_ENERGY := 70.0
|
const RESET_ENERGY := 70.0
|
||||||
|
const DEFAULT_CARRY_CAPACITY := 4.0
|
||||||
|
|
||||||
var data: Dictionary
|
var data: Dictionary
|
||||||
|
|
||||||
@@ -28,6 +29,8 @@ static func create() -> PlayerStateRecord:
|
|||||||
"starvation_death_threshold": 600,
|
"starvation_death_threshold": 600,
|
||||||
"is_dead": false,
|
"is_dead": false,
|
||||||
"deaths": 0,
|
"deaths": 0,
|
||||||
|
"inventory": {},
|
||||||
|
"carry_capacity": DEFAULT_CARRY_CAPACITY,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -68,6 +71,28 @@ static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord:
|
|||||||
or deaths < 0
|
or deaths < 0
|
||||||
):
|
):
|
||||||
return null
|
return null
|
||||||
|
var inventory := {}
|
||||||
|
var carry_capacity := DEFAULT_CARRY_CAPACITY
|
||||||
|
if record_data.has("inventory"):
|
||||||
|
if not record_data["inventory"] is Dictionary:
|
||||||
|
return null
|
||||||
|
var normalized := {}
|
||||||
|
for raw_item_id in record_data["inventory"]:
|
||||||
|
var item_id := String(raw_item_id)
|
||||||
|
var amount := float(record_data["inventory"][raw_item_id])
|
||||||
|
if (
|
||||||
|
item_id.is_empty()
|
||||||
|
or normalized.has(item_id)
|
||||||
|
or not is_finite(amount)
|
||||||
|
or amount < 0.0
|
||||||
|
):
|
||||||
|
return null
|
||||||
|
normalized[item_id] = amount
|
||||||
|
inventory = normalized
|
||||||
|
if record_data.has("carry_capacity"):
|
||||||
|
carry_capacity = float(record_data["carry_capacity"])
|
||||||
|
if not is_finite(carry_capacity) or carry_capacity < 0.0:
|
||||||
|
return null
|
||||||
return (
|
return (
|
||||||
PlayerStateRecord
|
PlayerStateRecord
|
||||||
. new(
|
. new(
|
||||||
@@ -80,6 +105,8 @@ static func from_dictionary(record_data: Dictionary) -> PlayerStateRecord:
|
|||||||
"starvation_death_threshold": death_threshold,
|
"starvation_death_threshold": death_threshold,
|
||||||
"is_dead": bool(record_data["is_dead"]),
|
"is_dead": bool(record_data["is_dead"]),
|
||||||
"deaths": deaths,
|
"deaths": deaths,
|
||||||
|
"inventory": inventory,
|
||||||
|
"carry_capacity": carry_capacity,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -109,6 +136,47 @@ func get_deaths() -> int:
|
|||||||
return int(data["deaths"])
|
return int(data["deaths"])
|
||||||
|
|
||||||
|
|
||||||
|
func get_inventory_amount(item_id: StringName) -> float:
|
||||||
|
return float(data["inventory"].get(String(item_id), 0.0))
|
||||||
|
|
||||||
|
|
||||||
|
func get_carry_capacity() -> float:
|
||||||
|
return float(data["carry_capacity"])
|
||||||
|
|
||||||
|
|
||||||
|
func get_carried_total() -> float:
|
||||||
|
var total := 0.0
|
||||||
|
for amount in data["inventory"].values():
|
||||||
|
total += float(amount)
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
func get_available_carry() -> float:
|
||||||
|
return maxf(get_carry_capacity() - get_carried_total(), 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
func add_inventory(item_id: StringName, amount: float) -> float:
|
||||||
|
if item_id.is_empty() or not is_finite(amount) or amount <= 0.0 or is_dead():
|
||||||
|
return 0.0
|
||||||
|
var accepted := minf(amount, get_available_carry())
|
||||||
|
if accepted <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
data["inventory"][String(item_id)] = get_inventory_amount(item_id) + accepted
|
||||||
|
return accepted
|
||||||
|
|
||||||
|
|
||||||
|
func remove_inventory(item_id: StringName, requested_amount: float) -> float:
|
||||||
|
if item_id.is_empty() or not is_finite(requested_amount) or requested_amount <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
var removed := minf(requested_amount, get_inventory_amount(item_id))
|
||||||
|
if removed <= 0.0:
|
||||||
|
return 0.0
|
||||||
|
data["inventory"][String(item_id)] = get_inventory_amount(item_id) - removed
|
||||||
|
if is_equal_approx(get_inventory_amount(item_id), 0.0):
|
||||||
|
data["inventory"].erase(String(item_id))
|
||||||
|
return removed
|
||||||
|
|
||||||
|
|
||||||
func eat(amount: float) -> float:
|
func eat(amount: float) -> float:
|
||||||
if amount <= 0.0 or is_dead():
|
if amount <= 0.0 or is_dead():
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|||||||
@@ -772,6 +772,8 @@ static func _is_valid_supply_resolution(
|
|||||||
):
|
):
|
||||||
return false
|
return false
|
||||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||||
|
if actor_id < 0:
|
||||||
|
return StringName(event.data["source_id"]) == &"player_carry"
|
||||||
return (
|
return (
|
||||||
npc_ids.has(actor_id)
|
npc_ids.has(actor_id)
|
||||||
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
and StringName(event.data["source_id"]) == SimulationIds.npc_inventory_id(actor_id)
|
||||||
|
|||||||
@@ -298,17 +298,28 @@ func _run() -> void:
|
|||||||
opportunity_bush_state.set_amount_remaining(1.0)
|
opportunity_bush_state.set_amount_remaining(1.0)
|
||||||
player.global_position = opportunity_bush.interaction_point.global_position
|
player.global_position = opportunity_bush.interaction_point.global_position
|
||||||
player.call("try_interact")
|
player.call("try_interact")
|
||||||
|
var carried_after_gather: float = simulation_manager.get_player_state().get_inventory_amount(
|
||||||
|
SimulationIds.RESOURCE_FOOD
|
||||||
|
)
|
||||||
|
var pantry_position: Vector3 = (
|
||||||
|
main_scene
|
||||||
|
. get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry")
|
||||||
|
. get_interaction_position()
|
||||||
|
)
|
||||||
|
player.global_position = pantry_position
|
||||||
|
player.call("try_interact")
|
||||||
village_ui.selected_npc_index = contributor.id
|
village_ui.selected_npc_index = contributor.id
|
||||||
village_ui.call("_refresh_npc_inspector")
|
village_ui.call("_refresh_npc_inspector")
|
||||||
_check(
|
_check(
|
||||||
(
|
(
|
||||||
player_opportunity.get_status() == OpportunityStateRecord.STATUS_RESOLVED
|
is_equal_approx(carried_after_gather, 1.0)
|
||||||
|
and player_opportunity.get_status() == OpportunityStateRecord.STATUS_RESOLVED
|
||||||
and "Resolved village need" in inspector_label.text
|
and "Resolved village need" in inspector_label.text
|
||||||
and "◆ Player restocked the pantry" in inspector_label.text
|
and "◆ Player restocked the pantry" in inspector_label.text
|
||||||
and whisper_kicker.text == "NEED MET"
|
and whisper_kicker.text == "NEED MET"
|
||||||
and "The player restocked the pantry" in whisper_message.text
|
and "The player restocked the pantry" in whisper_message.text
|
||||||
),
|
),
|
||||||
"Player gathering should resolve the restored need in debug detail and transient HUD"
|
"Player gathering and depositing should resolve the restored need in debug detail and transient HUD"
|
||||||
)
|
)
|
||||||
|
|
||||||
var woodpile_state: StorageStateRecord = simulation_manager.get_woodpile()
|
var woodpile_state: StorageStateRecord = simulation_manager.get_woodpile()
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
var failures: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
call_deferred("_run")
|
||||||
|
|
||||||
|
|
||||||
|
func _run() -> void:
|
||||||
|
var manager := _create_manager()
|
||||||
|
var actor: SimNPC = manager.npcs[0]
|
||||||
|
var interested: SimNPC = manager.npcs[2]
|
||||||
|
var berry := _register_resource(
|
||||||
|
manager,
|
||||||
|
&"carry_berry_bush",
|
||||||
|
SimulationIds.ACTION_GATHER_FOOD,
|
||||||
|
SimulationIds.RESOURCE_FOOD,
|
||||||
|
3.0
|
||||||
|
)
|
||||||
|
var pantry: StorageStateRecord = manager.get_pantry()
|
||||||
|
_set_pantry_amount(manager, 1.0)
|
||||||
|
for npc in manager.npcs:
|
||||||
|
npc.position = Vector3(40.0 + npc.id * 10.0, 0.0, 0.0)
|
||||||
|
actor.position = Vector3.ZERO
|
||||||
|
interested.position = Vector3(4.0, 0.0, 0.0)
|
||||||
|
actor.hunger = 60.0
|
||||||
|
interested.hunger = 90.0
|
||||||
|
manager.economy.withdraw_to_inventory(actor, SimulationIds.RESOURCE_FOOD, 1.0)
|
||||||
|
var quest: PlayerQuestRecord = manager.get_active_player_quest()
|
||||||
|
_check(quest != null, "The carry loop needs an open player quest")
|
||||||
|
if quest == null:
|
||||||
|
berry.free()
|
||||||
|
manager.free()
|
||||||
|
_finish()
|
||||||
|
return
|
||||||
|
|
||||||
|
var state: PlayerStateRecord = manager.get_player_state()
|
||||||
|
var carried: float = manager.player_gather(berry)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
is_equal_approx(carried, 2.0)
|
||||||
|
and is_equal_approx(state.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 2.0)
|
||||||
|
and is_equal_approx(manager.get_pantry().get_amount(SimulationIds.RESOURCE_FOOD), 0.0)
|
||||||
|
),
|
||||||
|
"Gathering should fill the player's hands instead of the pantry directly",
|
||||||
|
)
|
||||||
|
var capacity_limited: float = manager.player_gather(berry)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
is_equal_approx(capacity_limited, 1.0)
|
||||||
|
and is_equal_approx(state.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 3.0)
|
||||||
|
),
|
||||||
|
"Remaining resource amount should cap the second gather",
|
||||||
|
)
|
||||||
|
var hands_full: float = manager.player_gather(berry)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
is_equal_approx(hands_full, 0.0)
|
||||||
|
and is_equal_approx(state.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 3.0)
|
||||||
|
),
|
||||||
|
"An empty resource should refuse further gathering",
|
||||||
|
)
|
||||||
|
|
||||||
|
var pantry_before: float = manager.get_pantry().get_amount(SimulationIds.RESOURCE_FOOD)
|
||||||
|
var deposited: float = manager.player_deposit(SimulationIds.RESOURCE_FOOD)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
is_equal_approx(deposited, 3.0)
|
||||||
|
and is_equal_approx(
|
||||||
|
manager.get_pantry().get_amount(SimulationIds.RESOURCE_FOOD), pantry_before + 3.0
|
||||||
|
)
|
||||||
|
and is_equal_approx(state.get_inventory_amount(SimulationIds.RESOURCE_FOOD), 0.0)
|
||||||
|
and manager.get_active_player_quest() == null
|
||||||
|
and (
|
||||||
|
manager.get_latest_player_quest_for_requester(interested.id).get_status()
|
||||||
|
== PlayerQuestRecord.STATUS_COMPLETED
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"Depositing carried food should resolve the quest through the real pantry transaction",
|
||||||
|
)
|
||||||
|
|
||||||
|
var saved_json: String = manager.serialize_state()
|
||||||
|
var restored := _create_manager(902)
|
||||||
|
_check(
|
||||||
|
restored.restore_state_from_json(saved_json),
|
||||||
|
"The emptied carry state should restore through the current schema"
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
restored.get_state_checksum() == manager.get_state_checksum()
|
||||||
|
and is_equal_approx(
|
||||||
|
restored.get_player_state().get_inventory_amount(SimulationIds.RESOURCE_FOOD), 0.0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"Restore should preserve the player's empty carried inventory and checksum",
|
||||||
|
)
|
||||||
|
|
||||||
|
var carry_state_saved: String = manager.serialize_state()
|
||||||
|
manager.player_gather(berry)
|
||||||
|
var mid_carry: String = manager.serialize_state()
|
||||||
|
var mid_restored := _create_manager(903)
|
||||||
|
_check(
|
||||||
|
mid_restored.restore_state_from_json(mid_carry),
|
||||||
|
"A partially carried inventory should restore through the current schema"
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
mid_restored.get_state_checksum() == manager.get_state_checksum()
|
||||||
|
and is_equal_approx(
|
||||||
|
mid_restored.get_player_state().get_inventory_amount(SimulationIds.RESOURCE_FOOD),
|
||||||
|
manager.get_player_state().get_inventory_amount(SimulationIds.RESOURCE_FOOD)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"Restore should preserve the player's carried inventory mid-transport",
|
||||||
|
)
|
||||||
|
|
||||||
|
berry.free()
|
||||||
|
manager.free()
|
||||||
|
restored.free()
|
||||||
|
mid_restored.free()
|
||||||
|
_finish()
|
||||||
|
|
||||||
|
|
||||||
|
func _register_resource(
|
||||||
|
manager: Node,
|
||||||
|
node_id: StringName,
|
||||||
|
action_id: StringName,
|
||||||
|
resource_id: StringName,
|
||||||
|
amount: float
|
||||||
|
) -> ResourceNode:
|
||||||
|
var node := ResourceNode.new()
|
||||||
|
node.name = "CarryResource"
|
||||||
|
node.node_id = node_id
|
||||||
|
node.action_id = action_id
|
||||||
|
node.resource_id = resource_id
|
||||||
|
node.initial_amount = amount
|
||||||
|
node.yield_per_action = 2.0
|
||||||
|
node.debug_label_enabled = false
|
||||||
|
var interaction_point := Marker3D.new()
|
||||||
|
interaction_point.name = "InteractionPoint"
|
||||||
|
node.add_child(interaction_point)
|
||||||
|
root.add_child(node)
|
||||||
|
_check(
|
||||||
|
manager.register_resource_node(node),
|
||||||
|
"The carry loop should bind a real finite ResourceNode"
|
||||||
|
)
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
func _create_manager(seed_value: int = 901) -> Node:
|
||||||
|
var manager: Node = load("res://simulation/SimulationManager.gd").new()
|
||||||
|
manager.simulation_seed = seed_value
|
||||||
|
manager.debug_logs = false
|
||||||
|
var home_positions: Array[Vector3] = [
|
||||||
|
Vector3(0.0, 0.0, 0.0),
|
||||||
|
Vector3(2.0, 0.0, 0.0),
|
||||||
|
Vector3(10.0, 0.0, 0.0),
|
||||||
|
Vector3(12.0, 0.0, 0.0),
|
||||||
|
Vector3(30.0, 0.0, 30.0),
|
||||||
|
Vector3(40.0, 0.0, 40.0),
|
||||||
|
]
|
||||||
|
manager.home_positions = home_positions
|
||||||
|
root.add_child(manager)
|
||||||
|
manager.set_process(false)
|
||||||
|
return manager
|
||||||
|
|
||||||
|
|
||||||
|
func _set_pantry_amount(manager: Node, amount: float) -> void:
|
||||||
|
var pantry: StorageStateRecord = manager.get_pantry()
|
||||||
|
pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD))
|
||||||
|
pantry.deposit(SimulationIds.RESOURCE_FOOD, amount)
|
||||||
|
manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
||||||
|
|
||||||
|
|
||||||
|
func _check(condition: bool, message: String) -> void:
|
||||||
|
if not condition:
|
||||||
|
failures.append(message)
|
||||||
|
|
||||||
|
|
||||||
|
func _finish() -> void:
|
||||||
|
if failures.is_empty():
|
||||||
|
print("[TEST] Player carry and deposit loop passed")
|
||||||
|
quit(0)
|
||||||
|
return
|
||||||
|
for failure in failures:
|
||||||
|
push_error("[TEST] " + failure)
|
||||||
|
quit(1)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bwyg38d3ieiex
|
||||||
@@ -35,11 +35,17 @@ func _run() -> void:
|
|||||||
|
|
||||||
_check(
|
_check(
|
||||||
is_equal_approx(bush_state.get_amount_remaining(), 0.0),
|
is_equal_approx(bush_state.get_amount_remaining(), 0.0),
|
||||||
"Player should deplete the nearby bush"
|
"Player should deplete the nearby bush into their hands"
|
||||||
|
)
|
||||||
|
var carried_after_gather: float = simulation_manager.get_player_state().get_inventory_amount(
|
||||||
|
SimulationIds.RESOURCE_FOOD
|
||||||
)
|
)
|
||||||
_check(
|
_check(
|
||||||
is_equal_approx(simulation_manager.village.food, food_before + 1.0),
|
(
|
||||||
"Village should receive exactly the bush's remaining food"
|
is_equal_approx(carried_after_gather, 1.0)
|
||||||
|
and is_equal_approx(simulation_manager.village.food, food_before)
|
||||||
|
),
|
||||||
|
"Gathering should fill the player's hands without bypassing the village store",
|
||||||
)
|
)
|
||||||
_check(bush_state.get_reserved_by() == -1, "Depletion should release the NPC reservation")
|
_check(bush_state.get_reserved_by() == -1, "Depletion should release the NPC reservation")
|
||||||
var player_extraction: EconomicEventRecord
|
var player_extraction: EconomicEventRecord
|
||||||
@@ -65,24 +71,41 @@ func _run() -> void:
|
|||||||
"Player depletion should preserve the resource interaction position"
|
"Player depletion should preserve the resource interaction position"
|
||||||
)
|
)
|
||||||
|
|
||||||
player.global_position = tree.interaction_point.global_position
|
|
||||||
var wood_before: float = simulation_manager.village.wood
|
|
||||||
var tree_before := tree_state.get_amount_remaining()
|
|
||||||
player.try_interact()
|
|
||||||
|
|
||||||
_check(
|
|
||||||
is_equal_approx(tree_state.get_amount_remaining(), tree_before - tree.yield_per_action),
|
|
||||||
"Player should extract the tree's configured yield"
|
|
||||||
)
|
|
||||||
_check(
|
|
||||||
is_equal_approx(simulation_manager.village.wood, wood_before + tree.yield_per_action),
|
|
||||||
"Village should receive exactly the extracted wood"
|
|
||||||
)
|
|
||||||
|
|
||||||
var pantry := (
|
var pantry := (
|
||||||
main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry") as StorageNode
|
main_scene.get_node("JajceWorld/WorldObjects/StorageSites/VillagePantry") as StorageNode
|
||||||
)
|
)
|
||||||
var pantry_state: StorageStateRecord = simulation_manager.get_pantry()
|
var pantry_state: StorageStateRecord = simulation_manager.get_pantry()
|
||||||
|
player.global_position = pantry.get_interaction_position()
|
||||||
|
player.try_interact()
|
||||||
|
var food_after_deposit: float = simulation_manager.village.food
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
is_equal_approx(food_after_deposit, food_before + 1.0)
|
||||||
|
and is_equal_approx(
|
||||||
|
simulation_manager.get_player_state().get_inventory_amount(
|
||||||
|
SimulationIds.RESOURCE_FOOD
|
||||||
|
),
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"Depositing at the typed pantry should deliver the carried food to the village",
|
||||||
|
)
|
||||||
|
|
||||||
|
player.global_position = tree.interaction_point.global_position
|
||||||
|
var wood_before: float = simulation_manager.village.wood
|
||||||
|
var tree_before := tree_state.get_amount_remaining()
|
||||||
|
player.try_interact()
|
||||||
|
var carried_wood: float = simulation_manager.get_player_state().get_inventory_amount(
|
||||||
|
SimulationIds.RESOURCE_WOOD
|
||||||
|
)
|
||||||
|
_check(
|
||||||
|
(
|
||||||
|
is_equal_approx(tree_state.get_amount_remaining(), tree_before - tree.yield_per_action)
|
||||||
|
and is_equal_approx(carried_wood, tree.yield_per_action)
|
||||||
|
),
|
||||||
|
"Player should extract the tree's configured yield into their hands",
|
||||||
|
)
|
||||||
|
|
||||||
pantry_state.deposit(SimulationIds.RESOURCE_FOOD, pantry_state.get_available_capacity() - 0.5)
|
pantry_state.deposit(SimulationIds.RESOURCE_FOOD, pantry_state.get_available_capacity() - 0.5)
|
||||||
simulation_manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
simulation_manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
||||||
bush_state.set_amount_remaining(1.0)
|
bush_state.set_amount_remaining(1.0)
|
||||||
@@ -90,20 +113,40 @@ func _run() -> void:
|
|||||||
player.try_interact()
|
player.try_interact()
|
||||||
_check(
|
_check(
|
||||||
(
|
(
|
||||||
is_equal_approx(bush_state.get_amount_remaining(), 0.5)
|
is_equal_approx(bush_state.get_amount_remaining(), 0.0)
|
||||||
and is_equal_approx(pantry_state.get_available_capacity(), 0.0)
|
and is_equal_approx(
|
||||||
|
simulation_manager.get_player_state().get_inventory_amount(
|
||||||
|
SimulationIds.RESOURCE_FOOD
|
||||||
),
|
),
|
||||||
"Player harvesting should leave overflow at its source when storage fills"
|
1.0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"Player gathering should still extract into their hands regardless of storage capacity",
|
||||||
)
|
)
|
||||||
|
|
||||||
pantry_state.deposit(SimulationIds.RESOURCE_FOOD, 3.0)
|
pantry_state.withdraw(
|
||||||
|
SimulationIds.RESOURCE_FOOD, pantry_state.get_amount(SimulationIds.RESOURCE_FOOD)
|
||||||
|
)
|
||||||
|
pantry_state.deposit(SimulationIds.RESOURCE_FOOD, 5.0)
|
||||||
simulation_manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
simulation_manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
||||||
var pantry_food_before := pantry_state.get_amount(SimulationIds.RESOURCE_FOOD)
|
var pantry_food_before := pantry_state.get_amount(SimulationIds.RESOURCE_FOOD)
|
||||||
player.global_position = pantry.get_interaction_position()
|
player.global_position = pantry.get_interaction_position()
|
||||||
player.try_interact()
|
player.try_interact()
|
||||||
|
var food_after_first_deposit := pantry_state.get_amount(SimulationIds.RESOURCE_FOOD)
|
||||||
|
var food_carried_after_first: float = (
|
||||||
|
simulation_manager.get_player_state().get_inventory_amount(SimulationIds.RESOURCE_FOOD)
|
||||||
|
)
|
||||||
_check(
|
_check(
|
||||||
pantry_state.get_amount(SimulationIds.RESOURCE_FOOD) < pantry_food_before,
|
(
|
||||||
"Player should eat from the typed pantry StorageNode"
|
food_after_first_deposit > pantry_food_before
|
||||||
|
and is_equal_approx(food_carried_after_first, 0.0)
|
||||||
|
),
|
||||||
|
"Player should deposit carried food at the typed pantry",
|
||||||
|
)
|
||||||
|
player.try_interact()
|
||||||
|
_check(
|
||||||
|
pantry_state.get_amount(SimulationIds.RESOURCE_FOOD) < food_after_first_deposit,
|
||||||
|
"Player should eat from the pantry once their hands are empty",
|
||||||
)
|
)
|
||||||
var guard_site := (
|
var guard_site := (
|
||||||
main_scene.get_node("JajceWorld/WorldObjects/ActivitySites/GuardPost") as ActivitySite
|
main_scene.get_node("JajceWorld/WorldObjects/ActivitySites/GuardPost") as ActivitySite
|
||||||
|
|||||||
Reference in New Issue
Block a user