class_name ABGGuildPageController extends RefCounted const ContentRegistry = preload("res://p10_0/content_registry.gd") ## UI-independent Guild view-data assembly. ## ## The controller receives a Company snapshot and describes the Guild desk's ## state and destination boundaries. It never loads, saves or mutates ## persistence and never reaches into Godot nodes or Entry Shell globals. const ACTIVE_CONTRACT_STATUSES: Array[String] = ["market", "battle", "result", "recovery"] func snapshot(company: Dictionary, 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: return {"ok": false, "code": "CONTRACT_INVALID"} var recruits := _copy_recruits(raw_recruits) var contract: Dictionary = Dictionary(raw_contract).duplicate(true) var status := String(contract.get("status", "")) var has_contract := not contract.is_empty() var is_active := status in ACTIVE_CONTRACT_STATUSES var is_returned := status == "returned" var state := "active" if is_active else "returned" if is_returned else "empty" if not has_contract else "blocked" # M79-I06: after the final Contract the route is complete rather than open. var contract_total := Array(ContentRegistry.snapshot().get("contracts", [])).size() var route_complete := contract_total > 0 and int(company.get("next_contract_ordinal", 0)) >= contract_total and not is_active _route_complete_flag = route_complete var line_capacity := int(contract.get("line_capacity", 4)) var coin := int(contract.get("coin", 0)) var difficulty := String(contract.get("difficulty", "standard")) var company_data := { "company_id": String(company.get("company_id", "")), "owned_count": recruits.size(), "line_capacity": line_capacity, "recruits": recruits, "display_recruits": _copy_recruits(recruits.slice(0, mini(2, recruits.size()))), } var contract_data := { "present": has_contract, "active": is_active, "returned": is_returned, "state": state, "status": status, "contract_id": String(contract.get("contract_id", "")), "run_id": String(contract.get("run_id", "")), "route_id": String(contract.get("route_id", "windglass_verge")), "difficulty": difficulty, "coin": coin, "line_capacity": line_capacity, } return { "ok": true, "page_id": "guild", "company": company_data, "contract": contract_data, "route_complete": route_complete, "contract_total": contract_total, "renown": int(company.get("renown", 0)), "clear_facts": Array(company.get("clear_facts", [])).size(), "destinations": _destinations(state), "locale": String(locale), } func destination_state(snapshot_data: Dictionary, destination_id: String) -> Dictionary: var destinations = snapshot_data.get("destinations", {}) if destinations is Dictionary and destinations.has(destination_id): return Dictionary(destinations.get(destination_id, {})).duplicate(true) return {"enabled": false, "visible": false, "reason": "unknown_destination"} func _destinations(state: String) -> Dictionary: var has_active := state == "active" var has_returned := state == "returned" var _route_complete := _route_complete_flag return { "contract": { # M79-I05: with the previous contract returned the guild must offer # the next one instead of stranding the player. Once the route is # complete the board closes with an explicit completion reason. "enabled": (state == "empty" or has_returned) and not _route_complete, "visible": true, "reason": "route_complete" if _route_complete else "ready" if state == "empty" else "contract_active" if has_active else "contract_returned" if has_returned else "contract_invalid", }, "market": { "enabled": has_active, "visible": has_active, "reason": "ready" if has_active else "contract_returned" if has_returned else "contract_required", }, "equipment": { "enabled": has_active, "visible": true, "reason": "ready" if has_active else "contract_required" if not has_returned else "returned_contract", }, "party": { "enabled": has_active, "visible": true, "reason": "ready" if has_active else "contract_required" if not has_returned else "returned_contract", }, "risk": { "enabled": has_active, "visible": true, "reason": "ready" if has_active else "contract_required" if not has_returned else "returned_contract", }, } func _copy_recruits(values) -> Array[Dictionary]: var result: Array[Dictionary] = [] if not values is Array: return result for value in values: if value is Dictionary: result.append(Dictionary(value).duplicate(true)) return result var _route_complete_flag := false