231 lines
7.3 KiB
GDScript
231 lines
7.3 KiB
GDScript
extends SceneTree
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
_test_feed_weak_villager()
|
|
_test_repair_home_roof()
|
|
_finish()
|
|
|
|
|
|
func _test_feed_weak_villager() -> void:
|
|
var manager := _create_manager(951)
|
|
var weak: SimNPC = manager.npcs[0]
|
|
var supplier: SimNPC = manager.npcs[1]
|
|
_empty_pantry(manager)
|
|
for npc in manager.npcs:
|
|
npc.position = Vector3(40.0 + npc.id * 10.0, 0.0, 0.0)
|
|
weak.position = Vector3.ZERO
|
|
supplier.position = Vector3(2.0, 0.0, 0.0)
|
|
weak.hunger = 95.0
|
|
weak.is_starving = true
|
|
weak.energy = 10.0
|
|
manager.economy.withdraw_to_inventory(weak, SimulationIds.RESOURCE_FOOD, 1.0)
|
|
weak.task_state = SimNPC.TASK_STATE_IDLE
|
|
weak.task_complete = true
|
|
manager.call("_select_action_if_idle", weak, SimNPC.TASK_STATE_IDLE)
|
|
var opportunity: OpportunityStateRecord = manager.get_active_opportunity()
|
|
_check(
|
|
(
|
|
opportunity != null
|
|
and opportunity.get_opportunity_type() == SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER
|
|
and opportunity.get_interested_npc_id() == weak.id
|
|
and opportunity.get_resource_id() == SimulationIds.RESOURCE_FOOD
|
|
),
|
|
"A starving weak villager with an empty pantry should open a feed-weak-villager need"
|
|
)
|
|
if opportunity == null:
|
|
manager.free()
|
|
return
|
|
|
|
supplier.position = Vector3(2.0, 0.0, 0.0)
|
|
var supplier_relationship: RelationshipStateRecord = (
|
|
manager.relationship_system.get_relationship(supplier.id, weak.id)
|
|
)
|
|
_check(
|
|
(
|
|
supplier_relationship != null
|
|
and (
|
|
supplier_relationship.increase_trust(0.15, RelationshipStateRecord.NO_CAUSE_EVENT)
|
|
> 0.0
|
|
)
|
|
),
|
|
"Feed-weak setup should make the supplier a trusted known helper"
|
|
)
|
|
supplier.add_inventory(SimulationIds.RESOURCE_FOOD, 1.0)
|
|
var helper: OpportunityHelperResult = manager.get_active_opportunity_helper()
|
|
_check(
|
|
(
|
|
helper != null
|
|
and helper.helper_npc_id == supplier.id
|
|
and helper.action_id == SimulationIds.ACTION_DEPOSIT_FOOD
|
|
),
|
|
"A trusted informed supplier should be derived as the capable helper"
|
|
)
|
|
_check(
|
|
is_equal_approx(
|
|
manager.economy.deposit_inventory(supplier, SimulationIds.RESOURCE_FOOD), 1.0
|
|
),
|
|
"A real supplier should restock the pantry for the weak villager"
|
|
)
|
|
_check(
|
|
manager.get_active_opportunity() != null,
|
|
"Stocking the pantry alone must not close a feed-weak-villager need"
|
|
)
|
|
weak.add_inventory(SimulationIds.RESOURCE_FOOD, 1.0)
|
|
_check(manager.economy.consume_npc_food(weak), "The weak villager should eat the supplied food")
|
|
var resolved: OpportunityStateRecord = manager.get_latest_opportunity_for_npc(weak.id)
|
|
var resolution: EconomicEventRecord = manager.get_opportunity_resolution_event(resolved)
|
|
_check(
|
|
(
|
|
resolved != null
|
|
and resolved.get_status() == OpportunityStateRecord.STATUS_RESOLVED
|
|
and resolution != null
|
|
and (StringName(resolution.data["event_type"]) == SimulationIds.EVENT_ITEM_CONSUMED)
|
|
and int(resolution.data["actor_id"]) == weak.id
|
|
),
|
|
"A weak-villager need should resolve only when the interested villager actually eats"
|
|
)
|
|
|
|
var saved_json: String = manager.serialize_state()
|
|
var restored := _create_manager(952)
|
|
_check(
|
|
restored.restore_state_from_json(saved_json),
|
|
"A feed-weak-villager need should survive save and restore"
|
|
)
|
|
_check(
|
|
(
|
|
restored.get_state_checksum() == manager.get_state_checksum()
|
|
and (
|
|
restored.get_latest_opportunity_for_npc(weak.id).get_status()
|
|
== OpportunityStateRecord.STATUS_RESOLVED
|
|
)
|
|
),
|
|
"Restore should preserve the resolved feed-weak history and checksum"
|
|
)
|
|
restored.free()
|
|
|
|
_empty_pantry(manager)
|
|
weak.hunger = 95.0
|
|
weak.is_starving = true
|
|
weak.energy = 5.0
|
|
weak.task_state = SimNPC.TASK_STATE_IDLE
|
|
weak.task_complete = true
|
|
manager.call("_select_action_if_idle", weak, SimNPC.TASK_STATE_IDLE)
|
|
var reopened: OpportunityStateRecord = manager.get_active_opportunity()
|
|
_check(
|
|
(
|
|
reopened != null
|
|
and reopened.get_opportunity_type() == SimulationIds.OPPORTUNITY_FEED_WEAK_VILLAGER
|
|
),
|
|
"A renewed weak condition should open a new feed-weak-villager need"
|
|
)
|
|
var created_tick: int = reopened.get_created_tick()
|
|
var invalidated: OpportunityStateRecord = manager.opportunity_system.maintain_open_opportunity(
|
|
created_tick + manager.get_knowledge_review_interval(),
|
|
manager.get_knowledge_review_interval(),
|
|
manager.npcs,
|
|
manager.get_pantry(),
|
|
manager.get_woodpile()
|
|
)
|
|
_check(
|
|
(
|
|
invalidated != null
|
|
and invalidated.get_status() == OpportunityStateRecord.STATUS_INVALIDATED
|
|
and (
|
|
invalidated.get_invalidation_reason()
|
|
== SimulationIds.OPPORTUNITY_INVALIDATED_EVIDENCE_STALE
|
|
)
|
|
),
|
|
"An unresolved weak-villager need should close as stale after one simulated day"
|
|
)
|
|
manager.free()
|
|
|
|
|
|
func _test_repair_home_roof() -> void:
|
|
var manager := _create_manager(953)
|
|
var homeowner: SimNPC = manager.npcs[0]
|
|
var supplier: SimNPC = manager.npcs[1]
|
|
var woodpile: StorageStateRecord = manager.get_woodpile()
|
|
woodpile.withdraw(SimulationIds.RESOURCE_WOOD, woodpile.get_amount(SimulationIds.RESOURCE_WOOD))
|
|
manager.economy.sync_resource(SimulationIds.RESOURCE_WOOD)
|
|
manager.village.safety = 15.0
|
|
homeowner.position = Vector3.ZERO
|
|
var definition := SimulationDefinitions.get_action(SimulationIds.ACTION_SLEEP)
|
|
manager.call("_apply_action_completion", homeowner, SimulationIds.ACTION_SLEEP, definition)
|
|
var opportunity: OpportunityStateRecord = manager.get_active_opportunity()
|
|
_check(
|
|
(
|
|
opportunity != null
|
|
and opportunity.get_opportunity_type() == SimulationIds.OPPORTUNITY_REPAIR_HOME_ROOF
|
|
and opportunity.get_interested_npc_id() == homeowner.id
|
|
and opportunity.get_resource_id() == SimulationIds.RESOURCE_WOOD
|
|
),
|
|
"A sleepless night in a low-safety village should open a damaged-roof wood need"
|
|
)
|
|
if opportunity == null:
|
|
manager.free()
|
|
return
|
|
|
|
supplier.add_inventory(SimulationIds.RESOURCE_WOOD, 1.0)
|
|
_check(
|
|
is_equal_approx(
|
|
manager.economy.deposit_inventory(supplier, SimulationIds.RESOURCE_WOOD), 1.0
|
|
),
|
|
"A real supplier should deposit wood for the roof repair"
|
|
)
|
|
var resolved: OpportunityStateRecord = manager.get_latest_opportunity_for_npc(homeowner.id)
|
|
_check(
|
|
(
|
|
resolved != null
|
|
and resolved.get_status() == OpportunityStateRecord.STATUS_RESOLVED
|
|
and manager.get_active_opportunity() == null
|
|
),
|
|
"A real wood deposit should resolve the damaged-roof need"
|
|
)
|
|
manager.free()
|
|
|
|
|
|
func _empty_pantry(manager: Node) -> void:
|
|
var pantry: StorageStateRecord = manager.get_pantry()
|
|
pantry.withdraw(SimulationIds.RESOURCE_FOOD, pantry.get_amount(SimulationIds.RESOURCE_FOOD))
|
|
manager.economy.sync_resource(SimulationIds.RESOURCE_FOOD)
|
|
|
|
|
|
func _create_manager(seed_value: int = 951) -> 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 _finish() -> void:
|
|
if failures.is_empty():
|
|
print("[TEST] Village care opportunities passed: weak villager + damaged roof")
|
|
quit(0)
|
|
return
|
|
for failure in failures:
|
|
push_error("[TEST] " + failure)
|
|
quit(1)
|
|
|
|
|
|
func _check(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append(message)
|