Settle Mana raised above capacity between boundaries as recorded overflow instead of rejecting saves or latching a fault, exclude destroyed facilities from upkeep demand, and keep the depletion drill's outage sustained. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
152 lines
6.9 KiB
GDScript
152 lines
6.9 KiB
GDScript
extends SceneTree
|
|
|
|
const Cycle = preload("res://scripts/mana/mana_reserve_cycle.gd")
|
|
var _checks := 0
|
|
var _failures := 0
|
|
var _clock: Variant
|
|
var _state: Variant
|
|
var _runtime: Variant
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
print("S2: runtime finite Mana reserve (A06)")
|
|
_reset_runtime()
|
|
_test_generation_and_demand()
|
|
_test_save_round_trip_and_migration()
|
|
_test_depletion_stops_tower()
|
|
_test_between_boundary_increase()
|
|
_test_destroyed_facility_demand()
|
|
_cleanup()
|
|
if _failures == 0:
|
|
print("PASS: %d checks" % _checks)
|
|
else:
|
|
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
|
|
quit(_failures)
|
|
|
|
|
|
func _reset_runtime() -> void:
|
|
_cleanup()
|
|
_clock = load("res://autoload/game_clock.gd").new()
|
|
_state = load("res://autoload/game_state.gd").new()
|
|
_runtime = load("res://autoload/simulation_runtime.gd").new()
|
|
_clock.reset(1, 6, 0)
|
|
_state.reset()
|
|
var report: Dictionary = _runtime.initialize(_clock, _state, 3303)
|
|
_clock.set_paused(true)
|
|
_expect(report[&"valid"], "runtime initializes")
|
|
_expect(_state.get_resource(&"mana") == Cycle.INITIAL_MANA, "new game starts with approved 100 Mana")
|
|
_expect(_runtime.mana_reserve.get_minute() == _clock.absolute_minute, "reserve starts on the opening boundary")
|
|
|
|
|
|
func _cleanup() -> void:
|
|
for node: Variant in [_runtime, _clock, _state]:
|
|
if node != null:
|
|
node.free()
|
|
_runtime = null
|
|
_clock = null
|
|
_state = null
|
|
|
|
|
|
func _test_generation_and_demand() -> void:
|
|
_clock.advance_minutes(10)
|
|
var demand: int = _runtime.mana_network_service.get_total_consumer_demand()
|
|
var flow: Dictionary = _runtime.mana_reserve.get_last_flow()
|
|
_expect(flow[&"generation"] == 20 and flow[&"demand"] == demand, "boundary uses world tree 20 and registered demand")
|
|
_expect(_state.get_resource(&"mana") == mini(Cycle.CAPACITY, 100 + 20 - demand), "one boundary settles exactly once")
|
|
_clock.advance_minutes(9)
|
|
var mid: int = _state.get_resource(&"mana")
|
|
_expect(mid == int(flow[&"after"]), "no settlement between boundaries")
|
|
_clock.advance_minutes(200)
|
|
_expect(_state.get_resource(&"mana") <= Cycle.CAPACITY, "reserve never exceeds capacity")
|
|
|
|
|
|
func _test_save_round_trip_and_migration() -> void:
|
|
var snapshot: Dictionary = _runtime.create_snapshot()
|
|
var runtime_data: Dictionary = snapshot[&"systems"][&"runtime"]
|
|
_expect(runtime_data.has(&"mana_reserve"), "new saves carry the reserve contract")
|
|
_expect(_runtime.restore_snapshot(snapshot.duplicate(true)), "current save restores")
|
|
_expect(_runtime.create_snapshot() == snapshot, "restored reserve state is identical")
|
|
|
|
var legacy: Dictionary = snapshot.duplicate(true)
|
|
legacy[&"systems"][&"runtime"].erase(&"mana_reserve")
|
|
legacy[&"game_state"][&"resources"][&"mana"] = 0
|
|
_expect(_runtime.restore_snapshot(legacy), "legacy save with explicit 0 migrates")
|
|
_expect(_state.get_resource(&"mana") == 0 and _runtime.mana_reserve.get_last_flow().is_empty(), "explicit 0 stays 0 with fresh reserve")
|
|
|
|
var over: Dictionary = snapshot.duplicate(true)
|
|
over[&"systems"][&"runtime"].erase(&"mana_reserve")
|
|
over[&"game_state"][&"resources"][&"mana"] = Cycle.CAPACITY + 1
|
|
var before: Dictionary = _runtime.create_snapshot()
|
|
_expect(not _runtime.restore_snapshot(over), "legacy amount over capacity is rejected")
|
|
_expect(_runtime.create_snapshot() == before, "rejected migration keeps live state")
|
|
|
|
var tampered: Dictionary = snapshot.duplicate(true)
|
|
tampered[&"systems"][&"runtime"][&"mana_reserve"][&"last_flow"][&"after"] += 1
|
|
_expect(not _runtime.restore_snapshot(tampered), "tampered reserve flow is rejected")
|
|
_expect(_runtime.restore_snapshot(snapshot.duplicate(true)), "original save still restores")
|
|
|
|
|
|
func _test_depletion_stops_tower() -> void:
|
|
_state.set_resource(&"mana", 0)
|
|
var demand: int = _runtime.mana_network_service.get_total_consumer_demand()
|
|
_runtime.mana_network_service.register_consumer(&"test.mana_sink", 40)
|
|
_clock.advance_minutes(10 - (_clock.absolute_minute % 10))
|
|
_expect(_state.get_resource(&"mana") == 0, "demand above generation keeps the reserve at 0")
|
|
_expect(int(_runtime.mana_reserve.get_last_flow()[&"shortage"]) == demand + 40 - 20, "shortage is recorded")
|
|
_runtime.mana_network_service.unregister_node(&"test.mana_sink")
|
|
_clock.advance_minutes(10)
|
|
_expect(_state.get_resource(&"mana") == maxi(0, 20 - demand), "world tree recovers the reserve at the next boundary")
|
|
|
|
|
|
func _test_between_boundary_increase() -> void:
|
|
_clock.advance_minutes(10 - (_clock.absolute_minute % 10) + 3)
|
|
_state.add_resource(&"mana", 400)
|
|
var high: int = _state.get_resource(&"mana")
|
|
_expect(high > Cycle.CAPACITY, "mana can rise above capacity between boundaries (event refund / command)")
|
|
var saved: Dictionary = _runtime.create_snapshot()
|
|
_expect(_runtime.restore_snapshot(saved.duplicate(true)) and _state.get_resource(&"mana") == high, "over-capacity mid-interval save restores")
|
|
_clock.advance_minutes(1)
|
|
_expect(not _runtime.get_operations_report()[&"runtime_failure_latched"], "over-capacity amount does not latch a runtime fault")
|
|
_clock.advance_minutes(10 - (_clock.absolute_minute % 10))
|
|
var flow: Dictionary = _runtime.mana_reserve.get_last_flow()
|
|
_expect(_state.get_resource(&"mana") == Cycle.CAPACITY and flow[&"before"] == high, "next boundary settles to capacity")
|
|
_expect(flow[&"overflow"] == high + 20 - int(flow[&"consumed"]) - Cycle.CAPACITY, "excess is recorded as overflow")
|
|
|
|
|
|
func _test_destroyed_facility_demand() -> void:
|
|
var build = _runtime.submit_command(
|
|
&"build_facility",
|
|
{&"definition_id": &"cafeteria", &"origin": Vector3i(-5, 0, WorldService.LAYER_FRONT), &"rotation": 0},
|
|
&"player", &"", 50, -1, &"a06.build.cafeteria"
|
|
)
|
|
_expect(build.is_success(), "cafeteria construction starts")
|
|
_clock.advance_minutes(180)
|
|
var cafeteria: FacilityInstance = null
|
|
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
|
if facility.definition_id == &"cafeteria":
|
|
cafeteria = facility
|
|
_expect(cafeteria != null, "cafeteria completes")
|
|
if cafeteria == null:
|
|
return
|
|
var with_cafeteria: int = _runtime.call("_mana_reserve_demand")
|
|
_expect(with_cafeteria == _runtime.mana_network_service.get_total_consumer_demand(), "owned facility demand counts")
|
|
_runtime.facility_service.set_operational_status(cafeteria.instance_id, {&"business_open": false}, _clock.absolute_minute)
|
|
_expect(_runtime.call("_mana_reserve_demand") == with_cafeteria, "closed facility still pays upkeep demand")
|
|
_runtime.facility_service.set_operational_status(cafeteria.instance_id, {&"suspension_reason": &"DESTROYED"}, _clock.absolute_minute)
|
|
_expect(_runtime.call("_mana_reserve_demand") == with_cafeteria - 2, "destroyed facility demand is excluded")
|
|
_clock.advance_minutes(10 - (_clock.absolute_minute % 10))
|
|
_expect(int(_runtime.mana_reserve.get_last_flow()[&"demand"]) == with_cafeteria - 2, "boundary uses demand without destroyed facility")
|
|
|
|
|
|
func _expect(condition: bool, label: String) -> void:
|
|
_checks += 1
|
|
if condition:
|
|
print(" ✓ %s" % label)
|
|
else:
|
|
_failures += 1
|
|
push_error(" ✗ %s" % label)
|