extends RefCounted ## Approved finite Mana reserve contract A06 (MP-01-r02 recommended plan). ## GameState keeps the stored amount so existing spending stays authoritative; ## this owns the processed 10-minute boundary and the last flow, so a boundary ## is consumed exactly once and replays after rollback reproduce the same flow. const VERSION := 1 const POLICY_ID := &"MP-01-r02" const INITIAL_MANA := 100 const CAPACITY := 300 const WORLD_TREE_GENERATION := 20 const INTERVAL_MINUTES := 10 const FURNACE_YIELD := 100 ## A13 mana fog: world tree and furnace yields x1.2 (floor) at fog boundaries. const FOG_WORLD_TREE_GENERATION := 24 const FOG_FURNACE_YIELD := 120 const GENERATIONS: Array[int] = [WORLD_TREE_GENERATION, FOG_WORLD_TREE_GENERATION] const LIMIT := 1000000000 const FLOW_KEYS: Array[String] = [ "from_minute", "to_minute", "before", "generation", "furnace", "demand", "consumed", "shortage", "overflow", "after", ] var _minute := -1 var _last_flow: Dictionary = {} func start_at(minute: Variant) -> bool: if _minute >= 0 or typeof(minute) != TYPE_INT or minute < 0: return false _minute = minute - minute % INTERVAL_MINUTES return true func get_minute() -> int: return _minute func get_last_flow() -> Dictionary: return _last_flow.duplicate(true) ## Processes every boundary in (last boundary, minute]. Boundaries already ## processed are no-ops, so per-minute callers and same-minute retries are safe. ## Mana may rise between boundaries (event refunds, commands); anything above ## capacity is discarded and recorded as overflow at the next boundary. The ## caller passes the world tree yield of the boundary (A13 fog: 24, else 20). func advance_to(minute: Variant, before: Variant, demand: Variant, generation: Variant = WORLD_TREE_GENERATION) -> Dictionary: if _minute < 0: return _failure(&"NOT_STARTED") if typeof(minute) != TYPE_INT or not _amount(before) \ or typeof(demand) != TYPE_INT or demand < 0 or demand > LIMIT or minute < _minute \ or typeof(generation) != TYPE_INT or generation not in GENERATIONS: return _failure(&"INVALID_FLOW") var boundary: int = minute - minute % INTERVAL_MINUTES if boundary <= _minute: return {&"ok": true, &"processed": false, &"after": before} var amount: int = before var flow := {} var count := 0 for to_minute: int in range(_minute + INTERVAL_MINUTES, boundary + 1, INTERVAL_MINUTES): flow = plan(amount, demand, 0, to_minute - INTERVAL_MINUTES, to_minute, generation) amount = flow[&"after"] count += 1 _minute = boundary _last_flow = flow return {&"ok": true, &"processed": true, &"boundaries": count, &"after": amount, &"flow": flow.duplicate(true)} ## Furnace offers are judged after natural generation and upkeep demand are ## paid: base_after = min(C, max(0, B + G - D)); accept only with the yield free ## (100, or 120 at an A13 fog boundary where G is 24). static func furnace_offer_accepted(before: int, demand: int, generation: int = WORLD_TREE_GENERATION, furnace_yield: int = FURNACE_YIELD) -> bool: var base_after := mini(CAPACITY, maxi(0, before + generation - demand)) return CAPACITY - base_after >= furnace_yield static func plan(before: int, demand: int, furnace: int, start: int, end: int, generation: int = WORLD_TREE_GENERATION) -> Dictionary: var available := before + generation + furnace var consumed := mini(available, demand) var remaining := available - consumed return {&"from_minute": start, &"to_minute": end, &"before": before, &"generation": generation, &"furnace": furnace, &"demand": demand, &"consumed": consumed, &"shortage": demand - consumed, &"overflow": maxi(remaining - CAPACITY, 0), &"after": mini(remaining, CAPACITY)} ## Legacy saves without a reserve receipt must already fit the approved capacity. static func amount_is_valid(value: Variant) -> bool: return _amount(value) and value <= CAPACITY ## A current save may hold more than capacity until the next boundary settles it. static func stored_amount_is_valid(value: Variant) -> bool: return _amount(value) func create_snapshot() -> Dictionary: return {&"version": VERSION, &"policy_id": POLICY_ID, &"capacity": CAPACITY, &"generation": WORLD_TREE_GENERATION, &"interval_minutes": INTERVAL_MINUTES, &"minute": _minute, &"last_flow": _last_flow.duplicate(true)} ## Validates fully before committing; a rejected snapshot leaves state intact. func restore_snapshot(source: Variant) -> bool: if not source is Dictionary or not _keys(source, ["version", "policy_id", "capacity", "generation", "interval_minutes", "minute", "last_flow"]): return false if typeof(source["version"]) != TYPE_INT or source["version"] != VERSION \ or typeof(source["policy_id"]) not in [TYPE_STRING, TYPE_STRING_NAME] \ or StringName(source["policy_id"]) != POLICY_ID \ or typeof(source["capacity"]) != TYPE_INT or source["capacity"] != CAPACITY \ or typeof(source["generation"]) != TYPE_INT or source["generation"] != WORLD_TREE_GENERATION \ or typeof(source["interval_minutes"]) != TYPE_INT or source["interval_minutes"] != INTERVAL_MINUTES \ or not _amount(source["minute"]) or source["minute"] % INTERVAL_MINUTES != 0 \ or not source["last_flow"] is Dictionary: return false var flow: Dictionary = source["last_flow"] if not flow.is_empty(): if not _keys(flow, FLOW_KEYS): return false for key: Variant in flow: if not _amount(flow[key]): return false if flow["to_minute"] != source["minute"] \ or flow["generation"] not in GENERATIONS \ or flow["from_minute"] + INTERVAL_MINUTES != flow["to_minute"]: return false var expected := plan(flow["before"], flow["demand"], flow["furnace"], flow["from_minute"], flow["to_minute"], flow["generation"]) if not _same_flow(expected, flow): return false _minute = source["minute"] var normalized := {} for key: Variant in flow: normalized[StringName(key)] = flow[key] _last_flow = normalized return true static func _same_flow(expected: Dictionary, actual: Dictionary) -> bool: for key: String in FLOW_KEYS: if expected[StringName(key)] != actual[key]: return false return true static func _amount(value: Variant) -> bool: return typeof(value) == TYPE_INT and value >= 0 and value <= LIMIT static func _keys(source: Dictionary, expected: Array) -> bool: if source.size() != expected.size(): return false for key: Variant in source: if typeof(key) not in [TYPE_STRING, TYPE_STRING_NAME] or String(key) not in expected: return false return true static func _failure(code: StringName) -> Dictionary: return {&"ok": false, &"code": code}