Files
aetherbound-guild/runtime/entry/company_page_controller.gd
T
2026-08-25 10:29:56 +08:00

103 lines
3.8 KiB
GDScript

class_name ABGCompanyPageController
extends RefCounted
## UI-independent view-model assembly for the unaccepted Company pages.
## Callers provide a Company snapshot; this controller never loads, saves or
## mutates persistence and never reaches into Godot Control nodes.
func roster_snapshot(company: Dictionary, requested_recruit_id := "") -> Dictionary:
var members: Array[Dictionary] = []
for recruit in company.get("owned_recruits", []):
if recruit is Dictionary:
members.append(_member_snapshot(recruit))
var selected_id := _selected_id(members, requested_recruit_id)
var selected := _member_by_id(members, selected_id)
var dismissal := _dismissal_state(company, selected_id)
return {
"ok": true,
"page_id": "roster",
"selected_recruit_id": selected_id,
"members": _copy_array(members),
"selected": selected,
"dismissal": dismissal,
}
func recruit_detail_snapshot(company: Dictionary, recruit_id: String) -> Dictionary:
var recruit := _find_recruit(company, recruit_id)
if recruit.is_empty():
return {"ok": false, "code": "RECRUIT_NOT_FOUND", "recruit_id": recruit_id}
var member := _member_snapshot(recruit)
return {
"ok": true,
"page_id": "recruit_detail",
"selected_recruit_id": recruit_id,
"recruit": member,
"dismissal": _dismissal_state(company, recruit_id),
}
func _member_snapshot(recruit: Dictionary) -> Dictionary:
return {
"recruit_id": String(recruit.get("recruit_id", "")),
"rendered_name_snapshot": Dictionary(recruit.get("rendered_name_snapshot", {})).duplicate(true),
"profession_id": String(recruit.get("profession_id", "")),
"profession_job": String(recruit.get("profession_job", "")),
"profession_name": Dictionary(recruit.get("profession_name", {})).duplicate(true),
"trait_id": String(recruit.get("trait_id", "")),
"trait_name": Dictionary(recruit.get("trait_name", {})).duplicate(true),
"level": int(recruit.get("level", 0)),
"xp": int(recruit.get("xp", 0)),
"equipped_item_id": String(recruit.get("equipped_item_id", "")),
"starting_item_id": String(recruit.get("starting_item_id", "")),
"starting_item_name": Dictionary(recruit.get("starting_item_name", {})).duplicate(true),
"battle_history": Dictionary(recruit.get("battle_history", {})).duplicate(true),
}
func _dismissal_state(company: Dictionary, recruit_id: String) -> Dictionary:
var member_count := Array(company.get("owned_recruits", [])).size()
var phase := String(Dictionary(company.get("active_contract", {})).get("status", ""))
var eligible := not recruit_id.is_empty() and member_count > 1 and phase == "market"
var reason := "eligible"
if recruit_id.is_empty():
reason = "no_selection"
elif member_count <= 1:
reason = "last_member"
elif phase != "market":
reason = "phase_locked"
return {
"eligible": eligible,
"reason": reason,
"member_count": member_count,
"phase": phase,
}
func _selected_id(members: Array[Dictionary], requested_recruit_id: String) -> String:
if not requested_recruit_id.is_empty() and not _member_by_id(members, requested_recruit_id).is_empty():
return requested_recruit_id
return String(members[0].get("recruit_id", "")) if not members.is_empty() else ""
func _find_recruit(company: Dictionary, recruit_id: String) -> Dictionary:
for recruit in company.get("owned_recruits", []):
if recruit is Dictionary and String(recruit.get("recruit_id", "")) == recruit_id:
return Dictionary(recruit).duplicate(true)
return {}
func _member_by_id(members: Array[Dictionary], recruit_id: String) -> Dictionary:
for member in members:
if String(member.get("recruit_id", "")) == recruit_id:
return Dictionary(member).duplicate(true)
return {}
func _copy_array(values: Array[Dictionary]) -> Array[Dictionary]:
var copied: Array[Dictionary] = []
for value in values:
copied.append(Dictionary(value).duplicate(true))
return copied