139 lines
6.1 KiB
GDScript
139 lines
6.1 KiB
GDScript
class_name ABGContractChoicePageController
|
|
extends RefCounted
|
|
|
|
const GuildContractService = preload("res://entry/guild_contract_service.gd")
|
|
const ContentRegistry = preload("res://p10_0/content_registry.gd")
|
|
|
|
## UI-independent Contract Choice view-data assembly.
|
|
## Contract creation remains owned by GuildContractService; this controller only
|
|
## describes the stable choice surface and whether the Confirm boundary may be
|
|
## exposed.
|
|
|
|
|
|
func snapshot(company: Dictionary, selected_difficulty := "standard", locale := "zh_CN") -> Dictionary:
|
|
if company.is_empty():
|
|
return {"ok": false, "code": "COMPANY_REQUIRED"}
|
|
var active_contract = company.get("active_contract", {})
|
|
if not active_contract is Dictionary:
|
|
return {"ok": false, "code": "COMPANY_INVALID"}
|
|
# A returned contract is history: the guild may offer the next one.
|
|
if not Dictionary(active_contract).is_empty() and String(Dictionary(active_contract).get("status", "")) != "returned":
|
|
return {"ok": false, "code": "CONTRACT_ALREADY_ACTIVE"}
|
|
var normalized := normalize_difficulty(selected_difficulty)
|
|
var catalog := _difficulty_catalog(locale, normalized)
|
|
var recruits := _copy_array(company.get("owned_recruits", []))
|
|
var company_data := {
|
|
"company_id": String(company.get("company_id", "")),
|
|
"owned_count": recruits.size(),
|
|
"recruits": recruits,
|
|
}
|
|
var contract_row: Dictionary = _next_contract_row(locale, company)
|
|
var creation_facts := {
|
|
"contract_id": String(contract_row.get("id", GuildContractService.FIRST_CONTRACT_ID)),
|
|
"route_id": String(contract_row.get("region_id", "RG-01")).to_lower(),
|
|
"route_title": String(contract_row.get("route_title", _tr("风镜初渡", "FIRST WINDGLASS CROSSING", locale))),
|
|
"route_copy": String(contract_row.get("route_copy", _tr("穿越悬浮风镜,建立公会的第一条安全通路。", "Cross the suspended windglass and establish the Guild's first safe route.", locale))),
|
|
"starting_coin": GuildContractService.STARTING_COIN,
|
|
"line_capacity": GuildContractService.STARTING_LINE_CAPACITY,
|
|
"market_counts": {"recruit": 3, "equipment": 4, "battle": 3},
|
|
}
|
|
return {
|
|
"ok": true,
|
|
"page_id": "contract_choice",
|
|
"company": company_data,
|
|
"contract": creation_facts,
|
|
"difficulties": catalog,
|
|
"selected_difficulty": normalized,
|
|
"confirm": {
|
|
"eligible": true,
|
|
"reason": "ready",
|
|
},
|
|
"locale": String(locale),
|
|
}
|
|
|
|
|
|
func normalize_difficulty(value: String) -> String:
|
|
return value if value in GuildContractService.DIFFICULTIES else "standard"
|
|
|
|
|
|
func is_valid_difficulty(value: String) -> bool:
|
|
return value in GuildContractService.DIFFICULTIES
|
|
|
|
|
|
func confirm_state(company: Dictionary, selected_difficulty := "standard") -> Dictionary:
|
|
if company.is_empty():
|
|
return {"eligible": false, "reason": "company_required"}
|
|
var active_contract = company.get("active_contract", {})
|
|
if not active_contract is Dictionary:
|
|
return {"eligible": false, "reason": "company_invalid"}
|
|
# M79-I05: a returned contract is history, so the next one may be confirmed.
|
|
if not Dictionary(active_contract).is_empty() and String(Dictionary(active_contract).get("status", "")) != "returned":
|
|
return {"eligible": false, "reason": "contract_already_active"}
|
|
if not is_valid_difficulty(selected_difficulty):
|
|
return {"eligible": false, "reason": "invalid_difficulty"}
|
|
return {"eligible": true, "reason": "ready"}
|
|
|
|
|
|
func _difficulty_catalog(locale: String, selected: String) -> Array[Dictionary]:
|
|
var result: Array[Dictionary] = []
|
|
for difficulty in GuildContractService.DIFFICULTIES:
|
|
var id := String(difficulty)
|
|
result.append({
|
|
"id": id,
|
|
"name": _difficulty_name(id, locale),
|
|
"copy": _difficulty_copy(id, locale),
|
|
"selected": id == selected,
|
|
})
|
|
return result
|
|
|
|
|
|
func _difficulty_name(value: String, locale: String) -> String:
|
|
match value:
|
|
"wayfinder": return _tr("引路", "Wayfinder", locale)
|
|
"stormbound": return _tr("风暴", "Stormbound", locale)
|
|
"iron_oath": return _tr("铁誓", "Iron Oath", locale)
|
|
return _tr("标准", "Standard", locale)
|
|
|
|
|
|
func _difficulty_copy(value: String, locale: String) -> String:
|
|
match value:
|
|
"wayfinder": return _tr("较宽容的敌方参数;所有系统、进度与记录仍然可用。", "More forgiving enemy parameters; all systems, progression and Records remain available.", locale)
|
|
"stormbound": return _tr("更高压力与回报;目标规则和远征团所有权不变。", "Higher pressure and rewards; target rules and Company ownership remain unchanged.", locale)
|
|
"iron_oath": return _tr("最高压力;只改变敌人与回报参数,不隐藏规则。", "Highest pressure; only enemy and reward parameters change, never hidden rules.", locale)
|
|
return _tr("完整的标准平衡;不需要额外永久战力。", "The complete standard balance; no extra permanent power is required.", locale)
|
|
|
|
|
|
func _copy_array(value) -> Array:
|
|
var result: Array = []
|
|
if value is Array:
|
|
for item in value:
|
|
result.append(item.duplicate(true) if item is Dictionary or item is Array else item)
|
|
return result
|
|
|
|
|
|
func _tr(zh: String, en: String, locale: String) -> String:
|
|
return en if String(locale).to_lower().begins_with("en") else zh
|
|
|
|
func _next_contract_row(locale: String, company: Dictionary) -> Dictionary:
|
|
# The next contract follows the company's ordinal; region titles come from
|
|
# the catalogue and its zh authority.
|
|
var content: Dictionary = ContentRegistry.snapshot()
|
|
var rows: Array = Array(content.get("contracts", []))
|
|
rows.sort_custom(func(a, b): return String(a.get("id", "")) < String(b.get("id", "")))
|
|
if rows.is_empty():
|
|
return {}
|
|
var ordinal := 0
|
|
if company is Dictionary:
|
|
ordinal = int(company.get("next_contract_ordinal", 0))
|
|
var row: Dictionary = Dictionary(rows[clampi(ordinal, 0, rows.size() - 1)]).duplicate(true)
|
|
var region_id := String(row.get("region_id", "RG-01"))
|
|
row["route_title"] = ContentRegistry.localized_name(content, region_id, locale)
|
|
if String(row["route_title"]).is_empty():
|
|
row["route_title"] = region_id
|
|
var region_rows: Array = Array(content.get("region", []))
|
|
for region_row in region_rows:
|
|
if String(Dictionary(region_row).get("id", "")) == region_id:
|
|
row["route_copy"] = String(Dictionary(region_row).get("framing", ""))
|
|
break
|
|
return row
|