feat: add structured economic events
This commit is contained in:
@@ -110,6 +110,21 @@ func _run() -> void:
|
||||
is_equal_approx(conserved_food, 9.0),
|
||||
"Exactly one food unit should leave the world when eaten"
|
||||
)
|
||||
_check_economic_event_chain(manager)
|
||||
|
||||
var restored: Node = load("res://simulation/SimulationManager.gd").new()
|
||||
restored.debug_logs = false
|
||||
root.add_child(restored)
|
||||
restored.set_process(false)
|
||||
_check(
|
||||
restored.restore_state_from_json(manager.serialize_state()),
|
||||
"Economic events should restore with the simulation"
|
||||
)
|
||||
_check(
|
||||
restored.economic_events.size() == manager.economic_events.size()
|
||||
and restored.next_event_id == manager.next_event_id,
|
||||
"Economic event history and its next ID should round-trip"
|
||||
)
|
||||
|
||||
if failures.is_empty():
|
||||
print("[TEST] Food storage loop passed")
|
||||
@@ -122,3 +137,45 @@ func _run() -> void:
|
||||
func _check(condition: bool, message: String) -> void:
|
||||
if not condition:
|
||||
failures.append(message)
|
||||
|
||||
func _check_economic_event_chain(manager: Node) -> void:
|
||||
var expected_types: Array[StringName] = [
|
||||
SimulationIds.EVENT_RESOURCE_EXTRACTED,
|
||||
SimulationIds.EVENT_STORAGE_DEPOSITED,
|
||||
SimulationIds.EVENT_STORAGE_WITHDRAWN,
|
||||
SimulationIds.EVENT_ITEM_CONSUMED
|
||||
]
|
||||
_check(
|
||||
manager.economic_events.size() == expected_types.size(),
|
||||
"Food transfer loop should emit one structured event per transfer"
|
||||
)
|
||||
if manager.economic_events.size() != expected_types.size():
|
||||
return
|
||||
for index in expected_types.size():
|
||||
var event: EconomicEventRecord = manager.economic_events[index]
|
||||
_check(
|
||||
int(event.data["event_id"]) == index
|
||||
and StringName(event.data["event_type"]) == expected_types[index]
|
||||
and StringName(event.data["item_id"])
|
||||
== SimulationIds.RESOURCE_FOOD,
|
||||
"Economic event order, identity, and item should be deterministic"
|
||||
)
|
||||
_check(
|
||||
is_equal_approx(
|
||||
float(manager.economic_events[0].data["amount"]),
|
||||
2.0
|
||||
)
|
||||
and is_equal_approx(
|
||||
float(manager.economic_events[1].data["amount"]),
|
||||
2.0
|
||||
)
|
||||
and is_equal_approx(
|
||||
float(manager.economic_events[2].data["amount"]),
|
||||
1.0
|
||||
)
|
||||
and is_equal_approx(
|
||||
float(manager.economic_events[3].data["amount"]),
|
||||
1.0
|
||||
),
|
||||
"Economic events should record exact transferred quantities"
|
||||
)
|
||||
|
||||
@@ -18,6 +18,24 @@ func _run() -> void:
|
||||
var npc: SimNPC = manager.npcs[0]
|
||||
var visual: Node3D = view.active_npc_visuals[npc.id]
|
||||
visual.set_physics_process(false)
|
||||
var carried_food: MeshInstance3D = visual.get_node("CarriedFood")
|
||||
_check(not carried_food.visible, "NPC should begin without a carried-food prop")
|
||||
npc.add_inventory(SimulationIds.RESOURCE_FOOD, 1.0)
|
||||
manager.emit_signal(
|
||||
"npc_inventory_changed",
|
||||
npc,
|
||||
SimulationIds.RESOURCE_FOOD,
|
||||
1.0
|
||||
)
|
||||
_check(carried_food.visible, "Carried food should be visible on the NPC")
|
||||
npc.remove_inventory(SimulationIds.RESOURCE_FOOD, 1.0)
|
||||
manager.emit_signal(
|
||||
"npc_inventory_changed",
|
||||
npc,
|
||||
SimulationIds.RESOURCE_FOOD,
|
||||
0.0
|
||||
)
|
||||
_check(not carried_food.visible, "Deposited or eaten food should be hidden")
|
||||
|
||||
var resource_state: ResourceStateRecord = manager.get_resource_state(
|
||||
&"berry_bush_01"
|
||||
|
||||
@@ -11,6 +11,7 @@ func _run() -> void:
|
||||
_test_legacy_npc_migration()
|
||||
_test_legacy_resource_migration()
|
||||
_test_legacy_world_storage_migration()
|
||||
_test_previous_world_event_migration()
|
||||
_test_schema_rejection()
|
||||
|
||||
if failures.is_empty():
|
||||
@@ -203,6 +204,24 @@ func _test_legacy_world_storage_migration() -> void:
|
||||
)
|
||||
manager.free()
|
||||
|
||||
func _test_previous_world_event_migration() -> void:
|
||||
var manager := _create_manager(56)
|
||||
var previous_data: Dictionary = manager.create_state_record().to_dictionary()
|
||||
previous_data["schema_version"] = (
|
||||
SimulationStateRecord.PREVIOUS_SCHEMA_VERSION
|
||||
)
|
||||
previous_data.erase("economic_events")
|
||||
previous_data["simulation"].erase("next_event_id")
|
||||
var migrated := SimulationStateRecord.from_dictionary(previous_data)
|
||||
_check(migrated != null, "World schema v2 should add an empty event stream")
|
||||
if migrated != null:
|
||||
_check(
|
||||
migrated.economic_events.is_empty()
|
||||
and int(migrated.simulation["next_event_id"]) == 0,
|
||||
"World v2 migration should initialize deterministic event identity"
|
||||
)
|
||||
manager.free()
|
||||
|
||||
func _create_manager(seed_value: int) -> Node:
|
||||
var manager: Node = load("res://simulation/SimulationManager.gd").new()
|
||||
manager.simulation_seed = seed_value
|
||||
|
||||
Reference in New Issue
Block a user