class_name FG3Contract2ExecutionService extends RefCounted const BattleService = preload("res://fg3/contract2_battle_service.gd") const MarketGenerator = preload("res://fg3/contract2_market_generator.gd") const State = preload("res://fg3/run_state.gd") const MAX_CREDIT := 54 const FIRST_CLEAR_RENOWN := 20 const CLEAR_ID := "CLEAR-RG02-C02" const BOSS_ID := "BO-R2-02" const BOSS_RECOVERY_COST := 26 const BOSS_PLAN_IDS := ["interrupt_pulse_family", "durable_links_stagger"] var state := State.new() var valid := false var error_code := "invalid_state" func _init(snapshot_value: Dictionary) -> void: valid = state.restore_snapshot(snapshot_value) if valid: error_code = "" static func xp_to_next(level: int) -> int: return 20 + 10 * level static func death_credit(original_list_price: int, coin_price_paid: int) -> int: return mini(original_list_price, 10 + floori(float(coin_price_paid) / 2.0)) func commit_route( operation_id: String, expected_revision: int, offer_id: String, expected_board_hash: String ) -> Dictionary: var intent := { "expected_revision": expected_revision, "offer_id": offer_id, "expected_board_hash": expected_board_hash, } var prior := _prior_operation(operation_id, "commit_contract2_route", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "contract2_market") if not guard.is_empty(): return _reject(guard) if expected_board_hash != String(state.current_run.market_board.get("board_hash", "")): return _reject("board_hash_mismatch") if state.current_run.deployed_recruit_ids.is_empty(): return _reject("deployment_required") if state.current_run.deployed_recruit_ids.size() > 10: return _reject("deployment_over_capacity") var offer := _battle_offer(offer_id) if offer.is_empty() or offer_id not in state.current_run.available_battle_offer_ids: return _reject("battle_offer_unavailable") var before := state.snapshot() var checkpoint_id := _hash({ "run_id": String(state.current_run.run_id), "contract_id": "CTR-R2-02", "operation_id": operation_id, "revision": expected_revision, "board_hash": expected_board_hash, "offer_id": offer_id, }) state.battle_checkpoints[checkpoint_id] = { "checkpoint_id": checkpoint_id, "kind": "contract2_route", "source_revision": expected_revision, "source_snapshot": before.duplicate(true), "board_hash": expected_board_hash, "offer_id": offer_id, } state.current_run.phase = "contract2_prepared" state.current_run.selected_route_id = offer_id state.current_run.pending_encounter = offer.duplicate(true) state.current_run.pending_encounter["deployed_recruit_ids"] = Array(state.current_run.deployed_recruit_ids).duplicate() state.current_run.pending_encounter["active_artifact_ids"] = Array(state.current_run.active_artifact_ids).duplicate() state.current_run.pre_battle_checkpoint_id = checkpoint_id return _commit(operation_id, "commit_contract2_route", intent, "contract2_route_prepared", { "operation_id": operation_id, "offer_id": offer_id, "route_id": String(offer.route_id), "risk": int(offer.risk), "checkpoint_id": checkpoint_id, "board_hash": expected_board_hash, }, before) func settle_route( operation_id: String, expected_revision: int, battle_snapshot: Dictionary ) -> Dictionary: var intent := { "expected_revision": expected_revision, "snapshot_hash": String(battle_snapshot.get("snapshot_hash", "")), } var prior := _prior_operation(operation_id, "settle_contract2_route", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "contract2_prepared") if not guard.is_empty(): return _reject(guard) var validation := _validate_battle_snapshot(battle_snapshot, "fg3_contract2_route") if not validation.ok: return _reject(String(validation.code)) var before := state.snapshot() var checkpoint_id := String(battle_snapshot.checkpoint_id) var checkpoint: Dictionary = state.battle_checkpoints[checkpoint_id] state.current_run = Dictionary(checkpoint.source_snapshot.current_run).duplicate(true) var outcome := String(battle_snapshot.outcome) var casualties := _apply_actor_results(battle_snapshot) var xp_award := int(battle_snapshot.xp_grant) if outcome != "victory": xp_award = floori(float(xp_award) * 0.5) var growth := _apply_growth(battle_snapshot, xp_award) _update_formation() state.current_run.last_battle_report = _route_report(battle_snapshot, xp_award) var receipt := { "operation_id": operation_id, "battle_hash": String(battle_snapshot.snapshot_hash), "round_index": int(battle_snapshot.round_index), "route_id": String(battle_snapshot.route_id), "plan_id": String(battle_snapshot.plan_id), "outcome": outcome, "cause": String(battle_snapshot.cause), "casualties": casualties, "growth": growth, "xp_award": xp_award, } if outcome == "victory": var advanced := _advance_route_victory(battle_snapshot, checkpoint_id) if not advanced.ok: state.restore_snapshot(before) return _reject(String(advanced.code)) receipt.merge(advanced.receipt, true) else: state.current_run.phase = "contract2_recovery" state.current_run.selected_route_id = "" state.current_run.pending_encounter = {} state.current_run.pre_battle_checkpoint_id = checkpoint_id state.current_run.recovery_required = true state.current_run.recovery_cost = int(battle_snapshot.recovery_coin) receipt["reward_coin"] = 0 receipt["recovery_cost"] = int(state.current_run.recovery_cost) receipt["replacement_credit"] = int(state.current_run.replacement_credit) return _commit(operation_id, "settle_contract2_route", intent, "contract2_route_settled", receipt, before) func recover_route( operation_id: String, expected_revision: int, expected_battle_hash: String, expected_cost: int ) -> Dictionary: var intent := { "expected_revision": expected_revision, "expected_battle_hash": expected_battle_hash, "expected_cost": expected_cost, } var prior := _prior_operation(operation_id, "recover_contract2_route", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "contract2_recovery") if not guard.is_empty(): return _reject(guard) if expected_battle_hash != String(state.current_run.last_battle_report.get("battle_hash", "")): return _reject("battle_hash_mismatch") if expected_cost != int(state.current_run.recovery_cost): return _reject("recovery_cost_mismatch") if int(state.current_run.coin) < expected_cost: return _reject("insufficient_coin") var before := state.snapshot() state.current_run.coin = int(state.current_run.coin) - expected_cost _heal_survivors_to_floor(0.4) state.current_run.phase = "contract2_market" state.current_run.recovery_required = false state.current_run.recovery_cost = 0 state.current_run.selected_route_id = "" state.current_run.pending_encounter = {} var checkpoint_id := String(state.current_run.pre_battle_checkpoint_id) state.current_run.pre_battle_checkpoint_id = "" state.battle_checkpoints.erase(checkpoint_id) return _commit(operation_id, "recover_contract2_route", intent, "contract2_route_recovered", { "operation_id": operation_id, "battle_hash": expected_battle_hash, "coin_cost": expected_cost, "coin": int(state.current_run.coin), "surviving_recruit_ids": Array(state.current_run.company).duplicate(), }, before) func commit_boss_plan( operation_id: String, expected_revision: int, expected_muster_hash: String, plan_id: String ) -> Dictionary: var intent := { "expected_revision": expected_revision, "expected_muster_hash": expected_muster_hash, "plan_id": plan_id, } var prior := _prior_operation(operation_id, "commit_contract2_boss_plan", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "contract2_boss_muster") if not guard.is_empty(): return _reject(guard) if expected_muster_hash != String(state.current_run.boss_muster.get("muster_hash", "")): return _reject("muster_hash_mismatch") if plan_id not in state.current_run.boss_muster.plan_ids: return _reject("boss_plan_invalid") if state.current_run.deployed_recruit_ids.is_empty(): return _reject("deployment_required") var before := state.snapshot() var checkpoint_id := _hash({ "run_id": String(state.current_run.run_id), "operation_id": operation_id, "revision": expected_revision, "muster_hash": expected_muster_hash, "plan_id": plan_id, }) state.battle_checkpoints[checkpoint_id] = { "checkpoint_id": checkpoint_id, "kind": "contract2_boss", "source_revision": expected_revision, "source_snapshot": before.duplicate(true), "muster_hash": expected_muster_hash, "plan_id": plan_id, } state.current_run.phase = "contract2_boss_prepared" state.current_run.selected_boss_plan = plan_id state.current_run.pre_battle_checkpoint_id = checkpoint_id state.current_run.pending_boss_result = {} return _commit(operation_id, "commit_contract2_boss_plan", intent, "contract2_boss_plan_prepared", { "operation_id": operation_id, "boss_id": BOSS_ID, "plan_id": plan_id, "muster_hash": expected_muster_hash, "checkpoint_id": checkpoint_id, }, before) func settle_boss( operation_id: String, expected_revision: int, battle_snapshot: Dictionary ) -> Dictionary: var intent := { "expected_revision": expected_revision, "snapshot_hash": String(battle_snapshot.get("snapshot_hash", "")), } var prior := _prior_operation(operation_id, "settle_contract2_boss", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "contract2_boss_prepared") if not guard.is_empty(): return _reject(guard) var validation := _validate_battle_snapshot(battle_snapshot, "fg3_contract2_boss") if not validation.ok: return _reject(String(validation.code)) var before := state.snapshot() var checkpoint_id := String(battle_snapshot.checkpoint_id) var checkpoint: Dictionary = state.battle_checkpoints[checkpoint_id] state.current_run = Dictionary(checkpoint.source_snapshot.current_run).duplicate(true) var outcome := String(battle_snapshot.outcome) var casualties := _apply_actor_results(battle_snapshot) var growth := _apply_growth(battle_snapshot, 0) _update_formation() var result := { "boss_id": BOSS_ID, "battle_hash": String(battle_snapshot.snapshot_hash), "muster_hash": String(battle_snapshot.muster_hash), "plan_id": String(battle_snapshot.plan_id), "outcome": outcome, "cause": String(battle_snapshot.cause), "fallen_recruit_ids": Array(battle_snapshot.fallen_recruit_ids).duplicate(), "casualties": casualties, "growth": growth, } var receipt := result.duplicate(true) receipt["operation_id"] = operation_id receipt["reward_coin"] = 0 state.current_run.selected_boss_plan = "" if outcome == "victory": result["close_id"] = _hash({ "run_id": String(state.current_run.run_id), "clear_id": CLEAR_ID, "battle_hash": String(battle_snapshot.snapshot_hash), }).substr(0, 32) result["clear_id"] = CLEAR_ID result["first_clear_renown"] = FIRST_CLEAR_RENOWN state.current_run.phase = "region_result" state.current_run.pending_boss_result = result state.current_run.pre_battle_checkpoint_id = "" state.current_run.recovery_required = false state.current_run.recovery_cost = 0 state.battle_checkpoints.erase(checkpoint_id) receipt["close_id"] = String(result.close_id) else: state.current_run.phase = "contract2_boss_recovery" state.current_run.pending_boss_result = result state.current_run.pre_battle_checkpoint_id = checkpoint_id state.current_run.recovery_required = true state.current_run.recovery_cost = BOSS_RECOVERY_COST receipt["recovery_cost"] = BOSS_RECOVERY_COST receipt["replacement_credit"] = int(state.current_run.replacement_credit) return _commit(operation_id, "settle_contract2_boss", intent, "contract2_boss_settled", receipt, before) func recover_boss( operation_id: String, expected_revision: int, expected_battle_hash: String, expected_cost: int ) -> Dictionary: var intent := { "expected_revision": expected_revision, "expected_battle_hash": expected_battle_hash, "expected_cost": expected_cost, } var prior := _prior_operation(operation_id, "recover_contract2_boss", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "contract2_boss_recovery") if not guard.is_empty(): return _reject(guard) if expected_battle_hash != String(state.current_run.pending_boss_result.get("battle_hash", "")): return _reject("battle_hash_mismatch") if expected_cost != int(state.current_run.recovery_cost): return _reject("recovery_cost_mismatch") if int(state.current_run.coin) < expected_cost: return _reject("insufficient_coin") var before := state.snapshot() state.current_run.coin = int(state.current_run.coin) - expected_cost _heal_survivors_to_floor(0.4) state.current_run.phase = "contract2_boss_muster" state.current_run.selected_boss_plan = "" state.current_run.pending_boss_result = {} state.current_run.recovery_required = false state.current_run.recovery_cost = 0 var checkpoint_id := String(state.current_run.pre_battle_checkpoint_id) state.current_run.pre_battle_checkpoint_id = "" state.battle_checkpoints.erase(checkpoint_id) return _commit(operation_id, "recover_contract2_boss", intent, "contract2_boss_recovered", { "operation_id": operation_id, "battle_hash": expected_battle_hash, "coin_cost": expected_cost, "coin": int(state.current_run.coin), "muster_hash": String(state.current_run.boss_muster.muster_hash), "surviving_recruit_ids": Array(state.current_run.company).duplicate(), }, before) func close_region( operation_id: String, expected_revision: int, expected_close_id: String ) -> Dictionary: var intent := { "expected_revision": expected_revision, "expected_close_id": expected_close_id, } var prior := _prior_operation(operation_id, "close_rg02", intent) if prior.known: return prior.result var guard := _guard(expected_revision, "region_result") if not guard.is_empty(): return _reject(guard) var pending: Dictionary = state.current_run.pending_boss_result if expected_close_id != String(pending.get("close_id", "")): return _reject("close_id_mismatch") var before := state.snapshot() var first_clear: bool = CLEAR_ID not in state.guild_profile.contract_clear_ids var renown_grant := FIRST_CLEAR_RENOWN if first_clear else 0 if first_clear: state.guild_profile.contract_clear_ids.append(CLEAR_ID) state.guild_profile.persistent_renown = mini(9999, int(state.guild_profile.persistent_renown) + renown_grant) var discovery_id := "BOSS-%s" % BOSS_ID if discovery_id not in state.guild_profile.discovery_ids: state.guild_profile.discovery_ids.append(discovery_id) var record := _company_record(pending, renown_grant) state.guild_profile.company_records.append(record) state.guild_profile.clear_receipts[CLEAR_ID] = { "clear_id": CLEAR_ID, "close_id": expected_close_id, "record_id": String(record.record_id), "renown_grant": renown_grant, } if "RG-03" not in state.guild_profile.unlocked_region_ids: state.guild_profile.unlocked_region_ids.append("RG-03") var result := { "result_kind": "fg3.rg02_closed", "run_id": String(state.current_run.run_id), "region_id": "RG-02", "clear_ids": ["CLEAR-RG02-C01", CLEAR_ID], "record_ids": [ String(state.guild_profile.clear_receipts["CLEAR-RG02-C01"].record_id), String(record.record_id), ], "persistent_renown": int(state.guild_profile.persistent_renown), "unlocked_region_id": "RG-03", "surviving_company_ids": Array(state.current_run.company).duplicate(), "boss_battle_hash": String(pending.battle_hash), } result["result_hash"] = _hash(result) state.current_run.status = "closed" state.current_run.phase = "region_closed" state.current_run.market_board = result state.current_run.available_recruit_offer_ids = [] state.current_run.available_equipment_offer_ids = [] state.current_run.available_battle_offer_ids = [] state.current_run.locked_offer_ids = [] state.current_run.next_contract_id = "" var receipt := { "operation_id": operation_id, "close_id": expected_close_id, "clear_id": CLEAR_ID, "first_clear": first_clear, "renown_grant": renown_grant, "persistent_renown": int(state.guild_profile.persistent_renown), "record": record, "result": result, "unlocked_region_ids": Array(state.guild_profile.unlocked_region_ids).duplicate(), } return _commit(operation_id, "close_rg02", intent, "rg02_closed", receipt, before) func _advance_route_victory(battle_snapshot: Dictionary, checkpoint_id: String) -> Dictionary: var reward_coin := int(battle_snapshot.reward_coin) state.current_run.coin = int(state.current_run.coin) + reward_coin state.current_run.cleared_rounds = int(state.current_run.cleared_rounds) + 1 state.current_run.route_history.append({ "round_index": int(battle_snapshot.round_index), "contract_id": "CTR-R2-02", "route_id": String(battle_snapshot.route_id), "plan_id": String(battle_snapshot.plan_id), "outcome": "victory", "battle_hash": String(battle_snapshot.snapshot_hash), "reward_coin": reward_coin, "artifact_id": String(battle_snapshot.artifact_id), }) state.current_run.selected_route_id = "" state.current_run.pending_encounter = {} state.current_run.pre_battle_checkpoint_id = "" state.current_run.recovery_required = false state.current_run.recovery_cost = 0 state.current_run.refresh_count = 0 state.battle_checkpoints.erase(checkpoint_id) var next_market := int(battle_snapshot.round_index) + 1 if next_market <= 12: var generated := MarketGenerator.new().generate(String(state.current_run.run_seed), next_market, 0) if not generated.ok: return {"ok": false, "code": "next_contract2_market_generation_failed"} var board: Dictionary = generated.board state.current_run.phase = "contract2_market" state.current_run.market_index = next_market state.current_run.round_index = next_market state.current_run.line_capacity = 10 state.current_run.market_board = board.duplicate(true) state.current_run.available_recruit_offer_ids = _offer_ids(board.recruit_slots, "slot_id") state.current_run.available_equipment_offer_ids = _offer_ids(board.equipment_offers, "offer_id") state.current_run.available_battle_offer_ids = _offer_ids(board.battle_offers, "offer_id") state.current_run.locked_offer_ids = [] else: state.current_run.phase = "contract2_boss_muster" state.current_run.market_index = 13 state.current_run.round_index = 13 state.current_run.line_capacity = 10 state.current_run.available_recruit_offer_ids = [] state.current_run.available_equipment_offer_ids = [] state.current_run.available_battle_offer_ids = [] state.current_run.locked_offer_ids = [] state.current_run.boss_muster = _boss_muster() state.current_run.selected_boss_plan = "" state.current_run.pending_boss_result = {} return {"ok": true, "code": "contract2_victory_advanced", "receipt": { "reward_coin": reward_coin, "coin": int(state.current_run.coin), "market_index": int(state.current_run.market_index), "cleared_rounds": int(state.current_run.cleared_rounds), "line_capacity": 10, "boss_muster_open": next_market == 13, }} func _boss_muster() -> Dictionary: var muster := { "muster_id": _hash({ "run_seed": String(state.current_run.run_seed), "boss_id": BOSS_ID, "contract_id": "CTR-R2-02", }).substr(0, 32), "boss_id": BOSS_ID, "contract_id": "CTR-R2-02", "market_index": 13, "line_capacity": 10, "plan_ids": BOSS_PLAN_IDS.duplicate(), "pulse_family": "healing_movement_cleanse", "linked_indices": [3, 4], "third_pulse_indices": [3, 4, 5], "failure_cause": "linked_cost_chained_through_recovery", "source_route_hash": String(state.current_run.last_battle_report.battle_hash), } muster["muster_hash"] = _hash(muster) return muster func _validate_battle_snapshot(snapshot_value: Dictionary, expected_kind: String) -> Dictionary: if String(snapshot_value.get("kind", "")) != expected_kind: return {"ok": false, "code": "battle_kind_mismatch"} for key in [ "run_id", "contract_id", "region_id", "plan_id", "terminal", "outcome", "cause", "actors", "final_order", "fallen_recruit_ids", "event_log", "checkpoint_id", "event_log_hash", "snapshot_hash", ]: if not snapshot_value.has(key): return {"ok": false, "code": "battle_field_missing:%s" % key} if not bool(snapshot_value.terminal) or String(snapshot_value.outcome) not in ["victory", "defeat"]: return {"ok": false, "code": "battle_not_terminal"} if String(snapshot_value.checkpoint_id) not in state.battle_checkpoints: return {"ok": false, "code": "checkpoint_missing"} var expected := BattleService.new(state.snapshot()).run_to_terminal(String(snapshot_value.plan_id)) if not expected.ok: return {"ok": false, "code": "battle_replay_failed"} if expected.snapshot != snapshot_value: return {"ok": false, "code": "battle_snapshot_mismatch"} return {"ok": true, "code": "ok"} func _apply_actor_results(battle_snapshot: Dictionary) -> Array: var casualties: Array = [] for recruit_id_value in battle_snapshot.final_order: var recruit_id := String(recruit_id_value) if recruit_id in battle_snapshot.fallen_recruit_ids: casualties.append(_apply_casualty(recruit_id)) elif recruit_id in state.current_run.company: state.current_run.recruit_records[recruit_id].current_hp = maxi(1, int(battle_snapshot.actors[recruit_id].hp)) return casualties func _apply_casualty(recruit_id: String) -> Dictionary: var record: Dictionary = state.current_run.recruit_records[recruit_id] var instance_id := String(state.current_run.equipped_by_recruit.get(recruit_id, "")) if not instance_id.is_empty(): state.current_run.inventory[instance_id].wearer_id = "" var credit_before := int(state.current_run.replacement_credit) var grant := death_credit(int(record.get("original_list_price", 14)), int(record.get("coin_price_paid", 0))) state.current_run.replacement_credit = mini(MAX_CREDIT, credit_before + grant) state.current_run.company.erase(recruit_id) state.current_run.deployed_recruit_ids.erase(recruit_id) state.current_run.reserve_recruit_ids.erase(recruit_id) state.current_run.recruit_records.erase(recruit_id) state.current_run.equipped_by_recruit.erase(recruit_id) state.current_run.recruits_fallen_total = int(state.current_run.recruits_fallen_total) + 1 return { "recruit_id": recruit_id, "profession_id": String(record.profession_id), "trait_id": String(record.trait_id), "level": int(record.level), "credit_grant": int(state.current_run.replacement_credit) - credit_before, "returned_equipment_instance_id": instance_id, } func _apply_growth(battle_snapshot: Dictionary, xp_award: int) -> Array: var rows: Array = [] for recruit_id_value in battle_snapshot.final_order: var recruit_id := String(recruit_id_value) if recruit_id not in state.current_run.company: continue var record: Dictionary = state.current_run.recruit_records[recruit_id] var level_before := int(record.level) var xp_before := int(record.xp) var max_hp_before := int(record.max_hp) var hp_ratio := float(record.current_hp) / float(maxi(1, max_hp_before)) record.survived_battles = int(record.survived_battles) + 1 record.xp = int(record.xp) + xp_award while int(record.level) < 10 and int(record.xp) >= xp_to_next(int(record.level)): record.xp = int(record.xp) - xp_to_next(int(record.level)) record.level = int(record.level) + 1 record.max_hp = int(record.max_hp) + 4 if int(record.level) >= 10: record.xp = mini(int(record.xp), xp_to_next(10) - 1) record.current_hp = clampi(roundi(hp_ratio * float(record.max_hp)), 1, int(record.max_hp)) state.current_run.recruit_records[recruit_id] = record rows.append({ "recruit_id": recruit_id, "level_before": level_before, "level_after": int(record.level), "xp_before": xp_before, "xp_after": int(record.xp), }) return rows func _route_report(battle_snapshot: Dictionary, xp_award: int) -> Dictionary: return { "battle_hash": String(battle_snapshot.snapshot_hash), "round_index": int(battle_snapshot.round_index), "route_id": String(battle_snapshot.route_id), "plan_id": String(battle_snapshot.plan_id), "outcome": String(battle_snapshot.outcome), "cause": String(battle_snapshot.cause), "fallen_recruit_ids": Array(battle_snapshot.fallen_recruit_ids).duplicate(), "artifact_id": String(battle_snapshot.artifact_id), "tick": int(battle_snapshot.tick), "xp_award": xp_award, } func _company_record(pending: Dictionary, renown_grant: int) -> Dictionary: var survivors: Array = [] for recruit_id in state.current_run.company: var recruit: Dictionary = state.current_run.recruit_records[recruit_id] survivors.append({ "recruit_id": String(recruit_id), "profession_id": String(recruit.profession_id), "trait_id": String(recruit.trait_id), "level": int(recruit.level), "survived_battles": int(recruit.survived_battles), }) var contract2_routes: Array = Array(state.current_run.route_history).slice(12) return { "record_id": _hash({ "run_id": String(state.current_run.run_id), "clear_id": CLEAR_ID, "battle_hash": String(pending.battle_hash), }).substr(0, 32), "run_id": String(state.current_run.run_id), "contract_id": "CTR-R2-02", "clear_id": CLEAR_ID, "boss_id": BOSS_ID, "plan_id": String(pending.plan_id), "renown_grant": renown_grant, "survivors": survivors, "fallen_total": int(state.current_run.recruits_fallen_total), "dismissed_total": int(state.current_run.recruits_dismissed_total), "active_artifact_ids": Array(state.current_run.active_artifact_ids).duplicate(), "route_ids": contract2_routes.map(func(row): return String(row.get("route_id", ""))), } func _update_formation() -> void: var deployed: Array = [] for recruit_id in state.current_run.deployed_recruit_ids: if recruit_id in state.current_run.company: deployed.append(recruit_id) state.current_run.deployed_recruit_ids = deployed var reserve: Array = [] for recruit_id in state.current_run.company: if recruit_id not in deployed: reserve.append(recruit_id) state.current_run.reserve_recruit_ids = reserve state.current_run.formation_proposal = Array(state.current_run.company).slice(0, mini(10, state.current_run.company.size())) func _heal_survivors_to_floor(ratio: float) -> void: for recruit_id in state.current_run.company: var record: Dictionary = state.current_run.recruit_records[recruit_id] record.current_hp = maxi(int(record.current_hp), ceili(ratio * float(record.max_hp))) state.current_run.recruit_records[recruit_id] = record func _battle_offer(offer_id: String) -> Dictionary: for offer in state.current_run.market_board.battle_offers: if String(offer.get("offer_id", "")) == offer_id: return Dictionary(offer).duplicate(true) return {} func _guard(expected_revision: int, phase: String) -> String: if not valid: return error_code if expected_revision != state.revision: return "stale_revision" if String(state.current_run.phase) != phase: return "%s_required" % phase return "" func _prior_operation(operation_id: String, kind: String, intent: Dictionary) -> Dictionary: if operation_id.is_empty(): return {"known": true, "result": _reject("operation_id_required")} if not valid or not state.operation_receipts.has(operation_id): return {"known": false} var stored: Dictionary = state.operation_receipts[operation_id] if String(stored.kind) != kind or String(stored.intent_hash) != _hash(intent): return {"known": true, "result": _reject("operation_conflict")} return {"known": true, "result": { "ok": true, "code": String(stored.code), "duplicate": true, "revision": int(stored.revision), "receipt": Dictionary(stored.receipt).duplicate(true), "snapshot": state.snapshot(), }} func _commit( operation_id: String, kind: String, intent: Dictionary, code: String, receipt: Dictionary, before: Dictionary ) -> Dictionary: state.revision += 1 state.operation_receipts[operation_id] = { "operation_id": operation_id, "kind": kind, "intent_hash": _hash(intent), "status": "applied", "code": code, "revision": state.revision, "receipt": receipt.duplicate(true), } if not state.invariants_hold(): state.restore_snapshot(before) return _reject("postcondition_failed") return { "ok": true, "code": code, "duplicate": false, "revision": state.revision, "receipt": receipt.duplicate(true), "snapshot": state.snapshot(), } func _reject(code: String) -> Dictionary: return { "ok": false, "code": code, "duplicate": false, "revision": state.revision if valid else -1, } func _offer_ids(offers: Array, field: String) -> Array[String]: var result: Array[String] = [] for offer in offers: result.append(String(offer.get(field, ""))) return result func _hash(value) -> String: return JSON.stringify(_canonical(value)).sha256_text() func _canonical(value): if value is float and is_equal_approx(value, round(value)): return int(round(value)) if value is Dictionary: var result := {} var keys: Array = value.keys() keys.sort_custom(func(left, right): return String(left) < String(right)) for key in keys: result[key] = _canonical(value[key]) return result if value is Array: var result: Array = [] for item in value: result.append(_canonical(item)) return result return value