Files
aetherbound-guild/runtime/entry/risk_board_page_controller.gd
T

169 lines
6.6 KiB
GDScript

class_name ABGRiskBoardPageController
extends RefCounted
## UI-independent Risk Board view-data assembly.
##
## FormationRiskService remains responsible for loading and persisting the
## accepted party. Risk Commitment remains the next-page authority. This
## controller only validates and copies the frozen facts the RSK-001 View
## renders, plus local selection/Review eligibility.
const REQUIRED_OPTION_COUNT := 3
func snapshot(
company: Dictionary,
formation: Dictionary,
selected_risk_id := "",
locale := "zh_CN"
) -> Dictionary:
if company.is_empty():
return {"ok": false, "code": "COMPANY_REQUIRED"}
var raw_recruits = company.get("owned_recruits", [])
if not raw_recruits is Array:
return {"ok": false, "code": "COMPANY_INVALID"}
var raw_contract = company.get("active_contract", {})
if not raw_contract is Dictionary or Dictionary(raw_contract).is_empty():
return {"ok": false, "code": "RISK_CONTRACT_REQUIRED"}
if formation.is_empty():
return {"ok": false, "code": "RISK_FORMATION_REQUIRED"}
if String(formation.get("status", "")) != "ready":
return {"ok": false, "code": "RISK_READINESS_REQUIRED"}
var raw_snapshot = formation.get("accepted_party_snapshot", {})
if not raw_snapshot is Dictionary or Dictionary(raw_snapshot).is_empty():
return {"ok": false, "code": "RISK_PARTY_SNAPSHOT_REQUIRED"}
var contract: Dictionary = Dictionary(raw_contract).duplicate(true)
var members := _members_by_id(raw_recruits)
var capacity := int(formation.get("line_capacity", contract.get("line_capacity", 0)))
var party_snapshot: Dictionary = Dictionary(raw_snapshot).duplicate(true)
var party_ids = party_snapshot.get("front_to_rear_ids", formation.get("saved_front_to_rear_ids", []))
if not party_ids is Array or party_ids.is_empty():
return {"ok": false, "code": "RISK_PARTY_ORDER_REQUIRED"}
var order_validation := _validate_order(party_ids, members, capacity)
if not order_validation.ok:
return order_validation
var raw_options = formation.get("risk_options", [])
if not raw_options is Array or raw_options.size() != REQUIRED_OPTION_COUNT:
return {"ok": false, "code": "RISK_OPTIONS_INVALID"}
var options: Array[Dictionary] = []
var option_ids: Array = []
for raw_option in raw_options:
if not raw_option is Dictionary:
return {"ok": false, "code": "RISK_OPTION_INVALID"}
var option: Dictionary = Dictionary(raw_option).duplicate(true)
var option_id := String(option.get("risk_option_id", ""))
if option_id.is_empty():
return {"ok": false, "code": "RISK_OPTION_ID_INVALID"}
if option_id in option_ids:
return {"ok": false, "code": "RISK_OPTION_DUPLICATE"}
option_ids.append(option_id)
options.append(option)
var selected := String(selected_risk_id) if String(selected_risk_id) in option_ids else ""
var copied_relations: Array = _copy_array(party_snapshot.get("relations", formation.get("relations", [])))
var copied_coverage: Dictionary = Dictionary(party_snapshot.get("coverage", formation.get("coverage", {}))).duplicate(true)
var relation_by_id := _relations_by_id(copied_relations)
var ordered_recruits: Array[Dictionary] = []
for position in range(order_validation.ids.size()):
var recruit_id := String(order_validation.ids[position])
ordered_recruits.append({
"position": position,
"recruit_id": recruit_id,
"recruit": Dictionary(members[recruit_id]).duplicate(true),
"relation": Dictionary(relation_by_id.get(recruit_id, {})).duplicate(true),
})
var copied_options: Array[Dictionary] = []
for option in options:
var copied: Dictionary = option.duplicate(true)
copied["selected"] = String(copied.get("risk_option_id", "")) == selected
copied_options.append(copied)
var contract_data := {
"contract_id": String(contract.get("contract_id", "")),
"run_id": String(contract.get("run_id", "")),
"difficulty": String(contract.get("difficulty", "standard")),
"coin": int(contract.get("coin", 0)),
"line_capacity": int(contract.get("line_capacity", capacity)),
}
var company_data := {
"company_id": String(company.get("company_id", "")),
"owned_count": members.size(),
"line_capacity": capacity,
}
return {
"ok": true,
"page_id": "risk_board",
"locale": String(locale),
"company": company_data,
"contract": contract_data,
"party": {
"snapshot_id": String(party_snapshot.get("snapshot_id", "")),
"snapshot_hash": String(party_snapshot.get("snapshot_hash", "")),
"order_hash": String(party_snapshot.get("order_hash", "")),
"front_to_rear_ids": order_validation.ids.duplicate(true),
"ordered_recruits": ordered_recruits,
"orientation": String(party_snapshot.get("orientation", formation.get("orientation", "rear_left_front_right"))),
"front_position": int(party_snapshot.get("front_position", formation.get("front_position", 0))),
"line_capacity": capacity,
"relations": copied_relations,
"coverage": copied_coverage,
"first_threat": Dictionary(formation.get("first_threat", {})).duplicate(true),
},
"options": {
"items": copied_options,
"ids": option_ids.duplicate(true),
"count": copied_options.size(),
},
"selection": {
"selected_risk_id": selected,
"state": "selected" if not selected.is_empty() else "no_selection",
"auto_selected": false,
"selection_reason": "selected" if not selected.is_empty() else "no_selection",
},
"commands": {
"review_enabled": not selected.is_empty(),
"back_enabled": true,
"settings_enabled": true,
},
}
func _validate_order(values: Array, members: Dictionary, capacity: int) -> Dictionary:
if capacity <= 0 or values.size() != capacity:
return {"ok": false, "code": "RISK_PARTY_COUNT_INVALID"}
var ids: Array = []
for value in values:
var recruit_id := String(value)
if not members.has(recruit_id):
return {"ok": false, "code": "RISK_PARTY_RECRUIT_UNKNOWN"}
if recruit_id in ids:
return {"ok": false, "code": "RISK_PARTY_RECRUIT_DUPLICATE"}
ids.append(recruit_id)
return {"ok": true, "code": "RISK_PARTY_ORDER_VALID", "ids": ids}
func _members_by_id(values) -> Dictionary:
var result := {}
for value in values:
if value is Dictionary:
var copied: Dictionary = Dictionary(value).duplicate(true)
var recruit_id := String(copied.get("recruit_id", ""))
if not recruit_id.is_empty():
result[recruit_id] = copied
return result
func _relations_by_id(values: Array) -> Dictionary:
var result := {}
for value in values:
if value is Dictionary:
result[String(value.get("recruit_id", ""))] = Dictionary(value).duplicate(true)
return result
func _copy_array(values) -> Array:
var result: Array = []
if not values is Array:
return result
for value in values:
result.append(value.duplicate(true) if value is Dictionary or value is Array else value)
return result