Files
aetherbound-guild/runtime/p9_9/runtime_controller.gd
T

556 lines
25 KiB
GDScript

class_name P99RuntimeController
extends "res://p9_7/runtime_controller.gd"
const P99Content = preload("res://content/p9_9_content.gd")
const P99ProfileState = preload("res://p9_9/profile_state.gd")
const P99CharterService = preload("res://p9_9/charter_service.gd")
const P99RunService = preload("res://p9_9/run_service.gd")
const P99RunState = preload("res://p9_9/run_state.gd")
const P99BattleService = preload("res://p9_9/battle_service.gd")
const P99SaveService = preload("res://p9_9/save_service.gd")
var charter = P99CharterService.new()
var focused_charter_id := ""
var focused_draft_id := ""
var focused_hire_id := ""
var focused_route_id := ""
var input_guard_until_frame := -1
func _bootstrap_market() -> void:
if entry_snapshot.is_empty():
last_code = "entry_snapshot_required"
_queue_render()
return
market = null
var restored := false
if String(entry_snapshot.get("state_kind", "")) == "p99_run":
market = P99RunService.new()
restored = market.restore_snapshot(entry_snapshot)
if restored:
charter.state.restore_snapshot(market.state.profile)
elif bool(entry_snapshot.get("p99_activated", false)):
restored = charter.state.restore_snapshot(entry_snapshot)
if restored and not charter.state.selected_charter_claim_id.is_empty():
market = P99RunService.new(charter.state.snapshot())
else:
charter = P99CharterService.new(entry_snapshot)
restored = charter.state.invariants_hold()
if not restored:
market = null
last_code = "p99_snapshot_invalid"
_queue_render()
return
_install_run_content()
battle = P99BattleService.new(_selected_claim_or_default())
saves = P99SaveService.new()
pending_line_ids.clear()
if market != null:
pending_line_ids.assign(market.state.deployed_recruit_ids)
last_code = "p99_charter_ready" if charter.state.selected_charter_claim_id.is_empty() else "p99_run_ready"
battle_active = false
battle_result_visible = false
focused_charter_id = ""
focused_draft_id = ""
focused_hire_id = ""
focused_route_id = ""
_save_stable()
_queue_render()
func _install_run_content() -> void:
content = P99Content.run_snapshot(_selected_claim_or_default())
var recruits: Dictionary = Dictionary(content.draft_recruits).duplicate(true)
recruits.merge(content.market_hires, true)
content["recruits"] = recruits
func tap_charter_packet(claim_id: String) -> void:
if not _consume_interaction():
return
if not charter.state.selected_charter_claim_id.is_empty():
return
if focused_charter_id != claim_id:
focused_charter_id = claim_id
last_code = "charter_preview"
_queue_render()
return
var result: Dictionary = charter.claim(_operation("charter", claim_id), claim_id, charter.state.revision)
last_code = String(result.code)
if result.ok:
market = P99RunService.new(charter.state.snapshot())
_install_run_content()
battle = P99BattleService.new(charter.state.selected_charter_claim_id)
focused_charter_id = ""
_save_stable()
_queue_render()
func tap_draft_actor(recruit_id: String) -> void:
if not _consume_interaction():
return
if market == null or market.state.run_created:
return
if focused_draft_id != recruit_id:
focused_draft_id = recruit_id
last_code = "draft_inspected"
_queue_render()
return
var selected: bool = recruit_id not in market.state.selected_provisional_ids
var result: Dictionary = market.set_provisional(_operation("draft", recruit_id), recruit_id, selected, market.state.revision)
last_code = String(result.code)
if result.ok:
_save_stable()
_queue_render()
func confirm_run_creation() -> void:
if not _consume_interaction():
return
if market == null or market.state.run_created:
return
var result: Dictionary = market.confirm_run(_operation("run", "confirm"), market.state.revision)
last_code = String(result.code)
if result.ok:
focused_draft_id = ""
pending_line_ids.clear()
_save_stable()
_queue_render()
func tap_hire_actor(recruit_id: String) -> void:
if not _consume_interaction():
return
if not _market_ready() or market.state.current_market != 1:
return
if focused_hire_id != recruit_id:
focused_hire_id = recruit_id
last_code = "hire_inspected"
_queue_render()
return
var result: Dictionary = market.hire(_operation("hire", recruit_id), recruit_id)
last_code = String(result.code)
if result.ok:
focused_hire_id = ""
pending_line_ids.assign(market.state.deployed_recruit_ids)
_save_stable()
_queue_render()
func tap_route(route_id: String) -> void:
if not _consume_interaction():
return
if not _market_ready():
return
if focused_route_id != route_id:
focused_route_id = route_id
last_code = "route_preview"
_queue_render()
return
var result: Dictionary = market.commit_route(_operation("route", route_id), route_id)
last_code = String(result.code)
if not result.ok:
_queue_render()
return
_save_stable()
battle = P99BattleService.new(market.state.selected_charter_claim_id)
start_committed_battle()
func recover_company() -> void:
if not _consume_interaction():
return
if not _market_ready():
return
var result: Dictionary = market.recover_defeat(_operation("recover", "company"))
last_code = String(result.code)
if result.ok:
_save_stable()
_queue_render()
func settle_and_return() -> void:
if not _consume_interaction():
return
if not battle_active or not battle.state.terminal:
return
var result: Dictionary = market.settle_battle(_operation("settle", battle.state.route_id), battle.state.snapshot())
last_code = String(result.code)
if not result.ok:
_queue_render()
return
battle_active = false
battle_result_visible = false
focused_route_id = ""
pending_line_ids.assign(market.state.deployed_recruit_ids)
_save_stable()
_queue_render()
func _market_ready() -> bool:
return market != null and market.state.run_created and not battle_active and market.state.phase == P99RunState.Phase.MARKET
func _consume_interaction() -> bool:
var frame := int(Engine.get_process_frames())
if frame <= input_guard_until_frame:
return false
input_guard_until_frame = frame + 2
return true
func _save_stable() -> bool:
if not persistence_enabled:
return false
if market != null:
return bool(saves.save_snapshot(market.state.snapshot()).ok)
return bool(saves.save_snapshot(charter.state.snapshot()).ok)
func _operation(kind: String, target: String) -> String:
operation_index += 1
var revision: int = market.state.revision if market != null else charter.state.revision
return "p99-ui-%s-%d-%d-%s" % [kind, revision, operation_index, target]
func _render_current() -> void:
if display_root == null:
return
_clear_display()
if size.y > size.x:
_render_rotate_notice()
elif charter.state.selected_charter_claim_id.is_empty():
_render_charter_table()
elif market == null:
_render_missing_entry()
elif battle_active:
_render_battle()
elif not market.state.run_created:
_render_run_draft()
else:
_render_market()
func _render_charter_table() -> void:
_add_background(HALL_BACKGROUND)
_add_p99_header(_tr("公会宪章 · 风晶边境", "GUILD CHARTER · WINDGLASS VERGE"), _tr("选择未来机会或威胁情报,不提供永久战斗数值", "Choose future options or threat intel. No permanent combat power."))
var seal_width := (size.x - 36.0) / 8.0
for index in range(8):
var seal_id := "RG-%02d" % (index + 1)
var eligible := index == 0
var seal := _outlined_label("◆\n%s" % seal_id, 9, GOLD_LIGHT if eligible else Color(0.48, 0.58, 0.66))
seal.name = "P99RegionSeal_%s" % seal_id
seal.position = Vector2(18 + seal_width * index, 52)
seal.size = Vector2(seal_width, 48)
seal.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(seal)
for index in range(2):
var claim_id: String = ["CH-RG01-CATALOG-01", "CH-RG01-THREAT-01"][index]
var row: Dictionary = P99Content.CHARTER_CLAIMS[claim_id]
var center_x := size.x * (0.31 if index == 0 else 0.69)
var width := minf(270.0, size.x * 0.32)
var packet := ColorRect.new()
packet.name = "P99CharterPacket_%s" % claim_id
packet.position = Vector2(center_x - width * 0.5, size.y * 0.31)
packet.size = Vector2(width, size.y * 0.41)
packet.color = Color(0.12, 0.19, 0.24, 0.94) if focused_charter_id != claim_id else Color(0.25, 0.22, 0.12, 0.96)
packet.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(packet)
var seal_mark := _outlined_label("◇" if index == 0 else "◎", 34, CYAN if index == 0 else VIOLET)
seal_mark.position = Vector2(center_x - 32, size.y * 0.32)
seal_mark.size = Vector2(64, 58)
seal_mark.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(seal_mark)
var text := _outlined_label("%s · %d %s\n%s" % [_tr(String(row.name_zh), String(row.name_en)), int(row.renown_cost), _tr("声望", "RENOWN"), _tr(String(row.effect_zh), String(row.effect_en))], 11, WHITE)
text.position = Vector2(center_x - width * 0.45, size.y * 0.46)
text.size = Vector2(width * 0.90, size.y * 0.20)
text.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
text.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
display_root.add_child(text)
var target := _actor_target("P99CharterPacketTarget_%s" % claim_id, packet.position, packet.size, tap_charter_packet.bind(claim_id))
display_root.add_child(target)
var preview := _tr("点击一份契约查看;再次点击签署。当前声望 14。", "Tap a packet to inspect; tap again to sign. Current Renown: 14.")
if not focused_charter_id.is_empty():
var focused: Dictionary = P99Content.CHARTER_CLAIMS[focused_charter_id]
preview = _tr("签署后剩余 %d 声望。只改变:%s" % [14 - int(focused.renown_cost), String(focused.effect_zh)], "Renown after signing: %d. Changes only: %s" % [14 - int(focused.renown_cost), String(focused.effect_en)])
_add_context_ribbon(preview)
func _render_run_draft() -> void:
_add_background(HALL_BACKGROUND)
_add_p99_header(_tr("第二契约 · 召集新队伍", "CONTRACT 2 · MUSTER A NEW COMPANY"), _tr("从四名站立候选中选择两名;可以在确认前更换", "Choose two of four standing Recruits. Revise before confirming."))
var actor_size := clampf(size.y * 0.30, 88.0, 122.0)
var first_center := actor_size * 0.5 + 12.0
var last_center := size.x - actor_size * 0.5 - 12.0
var spacing := (last_center - first_center) / 3.0
for index in range(market.state.draft_offer_ids.size()):
var recruit_id: String = market.state.draft_offer_ids[index]
var row: Dictionary = content.draft_recruits[recruit_id]
var profession: Dictionary = content.professions[row.profession_id]
var selected: bool = recruit_id in market.state.selected_provisional_ids
var x := first_center + spacing * index - actor_size * 0.5
var y := size.y * 0.27
_add_ground_ring(Vector2(x + actor_size * 0.5, y + actor_size - 4), actor_size * 0.72, GOLD if selected else CYAN, "P99DraftRing_%s" % recruit_id)
var actor := _add_actor(String(profession.visual_rig), Vector2(x, y), actor_size, "P99DraftActor_%s" % recruit_id, true)
actor.self_modulate = _p99_profession_color(String(row.profession_id))
var target := _actor_target("P99DraftTarget_%s" % recruit_id, Vector2(x - 4, y - 4), Vector2(actor_size + 8, actor_size + 22), tap_draft_actor.bind(recruit_id))
display_root.add_child(target)
var name := _outlined_label("%s\n%s" % [_tr(String(row.name_zh), String(row.name_en)), _tr(String(profession.name_zh), String(profession.name_en))], 9, GOLD_LIGHT if selected else WHITE)
name.position = Vector2(x - 24, y + actor_size - 2)
name.size = Vector2(actor_size + 48, 50)
name.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(name)
var selected_count: int = market.state.selected_provisional_ids.size()
var context := _tr("已选 %d/2。点击角色查看,再次点击加入或移出。" % selected_count, "SELECTED %d/2. Tap to inspect, tap again to add or remove." % selected_count)
if not focused_draft_id.is_empty():
var focused: Dictionary = content.draft_recruits[focused_draft_id]
context = "%s\n%s / %s" % [_tr(String(focused.name_zh), String(focused.name_en)), _tr(String(focused.benefit_zh), String(focused.benefit_en)), _tr(String(focused.cost_zh), String(focused.cost_en))]
_add_context_ribbon(context)
if selected_count == 2:
var confirm := _action_button("P99ConfirmRunButton", _tr("确认两人 · 进入集市", "CONFIRM TWO · ENTER MARKET"), confirm_run_creation)
confirm.position = Vector2(size.x - 246, size.y - 62)
confirm.size = Vector2(228, 50)
display_root.add_child(confirm)
func _render_market() -> void:
_add_background(WINDGLASS_BACKGROUND)
if market.state.continuation_locked:
_render_market_four()
return
_add_p99_market_hud()
_render_p99_company_line()
_render_p99_hires_or_catalog()
_render_p99_route_gates()
_render_p99_market_context()
func _render_battle() -> void:
super._render_battle()
for node_name in ["P97PauseButton", "P97SpeedButton"]:
var control := find_child(node_name, true, false) as Control
if control != null:
control.position.y = 4
control.size.y = 48
func _add_p99_market_hud() -> void:
var header := ColorRect.new()
header.name = "P99MarketHud"
header.position = Vector2.ZERO
header.size = Vector2(size.x, 52)
header.color = Color(0.015, 0.035, 0.075, 0.88)
header.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(header)
var title := _outlined_label(_tr("第二契约 · 集市 %d" % market.state.current_market, "CONTRACT 2 · MARKET %d" % market.state.current_market), 16, GOLD_LIGHT)
title.position = Vector2(16, 4)
title.size = Vector2(size.x * 0.42, 42)
display_root.add_child(title)
var values := _outlined_label(_tr("◆%d 队伍 %d/4 战线 %d/%d 已胜 %d/3" % [market.state.coin, market.state.company.size(), market.state.deployed_recruit_ids.size(), market.state.line_capacity, market.state.cleared_rounds], "◆%d COMPANY %d/4 LINE %d/%d CLEARS %d/3" % [market.state.coin, market.state.company.size(), market.state.deployed_recruit_ids.size(), market.state.line_capacity, market.state.cleared_rounds]), 12, WHITE)
values.position = Vector2(size.x * 0.42, 4)
values.size = Vector2(size.x * 0.56, 42)
values.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
values.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
display_root.add_child(values)
func _render_p99_company_line() -> void:
var actor_size := clampf(size.y * 0.22, 72.0, 96.0)
var y := size.y - actor_size - 74.0
var lane_width := size.x * 0.43
var spacing := minf(actor_size * 0.82, (lane_width - 32.0) / maxf(1.0, float(market.state.company.size())))
for index in range(market.state.company.size()):
var recruit_id: String = market.state.company[index]
var x := 18.0 + spacing * index
_add_ground_ring(Vector2(x + actor_size * 0.5, y + actor_size - 2), actor_size * 0.70, GOLD, "P99CompanyRing_%s" % recruit_id)
var actor := _add_actor(_recruit_rig(recruit_id), Vector2(x, y), actor_size, "P99CompanyActor_%s" % recruit_id, true)
actor.self_modulate = _recruit_color(recruit_id)
var hp := _outlined_label("%d HP" % int(market.state.member_condition[recruit_id]), 8, WHITE)
hp.position = Vector2(x, y + actor_size - 4)
hp.size = Vector2(actor_size, 24)
hp.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(hp)
func _render_p99_hires_or_catalog() -> void:
var left := size.x * 0.43
var width := size.x - 226.0 - left
if market.state.current_market == 1:
var offers: Array = market.state.available_recruit_offer_ids
var slot := width / maxf(1.0, float(offers.size()))
for index in range(offers.size()):
var recruit_id := String(offers[index])
var row: Dictionary = content.market_hires[recruit_id]
var profession: Dictionary = content.professions[row.profession_id]
var actor_size := clampf(slot * 0.72, 44.0, 70.0)
var x := left + slot * index + (slot - actor_size) * 0.5
var y := 76.0
var actor := _add_actor(String(profession.visual_rig), Vector2(x, y), actor_size, "P99HireActor_%s" % recruit_id, true)
actor.self_modulate = _p99_profession_color(String(row.profession_id))
var target := _actor_target("P99HireTarget_%s" % recruit_id, Vector2(x - 4, y - 4), Vector2(actor_size + 8, actor_size + 48), tap_hire_actor.bind(recruit_id))
display_root.add_child(target)
var label := _outlined_label("%s\n◆%d" % [_tr(String(profession.name_zh), String(profession.name_en)), int(row.list_price)], 8, GOLD_LIGHT if focused_hire_id == recruit_id else WHITE)
label.position = Vector2(left + slot * index, y + actor_size - 2)
label.size = Vector2(slot, 42)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(label)
else:
var equipment_ids: Array = market.state.available_equipment_offer_ids
for index in range(equipment_ids.size()):
var catalog_id := String(equipment_ids[index])
var x := left + 26.0 + float(index) * minf(106.0, width * 0.42)
var icon := TextureRect.new()
icon.name = "P99CatalogItem_%s" % catalog_id
icon.texture = _equipment_texture(catalog_id)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.position = Vector2(x, 88)
icon.size = Vector2(58, 68)
icon.self_modulate = _equipment_color(catalog_id)
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(icon)
_add_ground_ring(Vector2(x + 29, 156), 68, GOLD, "P99CatalogPedestal_%s" % catalog_id)
var name := _outlined_label(_equipment_name(catalog_id), 8, WHITE)
name.position = Vector2(x - 20, 158)
name.size = Vector2(98, 30)
name.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(name)
func _render_p99_route_gates() -> void:
var route_ids: Array = market.state.available_battle_offer_ids
var lane_x := size.x - 218.0
var gap := 6.0
var width := (202.0 - gap * 2.0) / 3.0
for index in range(route_ids.size()):
var route_id := String(route_ids[index])
var route: Dictionary = content.routes[route_id]
var x := lane_x + float(index) * (width + gap)
var gate := ColorRect.new()
gate.name = "P99RouteGate_%s" % route_id
gate.position = Vector2(x, 74)
gate.size = Vector2(width, 112)
gate.color = _risk_color(int(route.risk), focused_route_id == route_id)
gate.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(gate)
var label := _outlined_label("%s\n+%d" % [[_tr("稳", "S"), _tr("险", "P"), _tr("精", "E")][int(route.risk)], int(route.reward)], 11, WHITE)
label.position = gate.position + Vector2(0, 16)
label.size = Vector2(width, 76)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
display_root.add_child(label)
var target := _actor_target("P99RouteTarget_%s" % route_id, gate.position - Vector2(2, 2), gate.size + Vector2(4, 4), tap_route.bind(route_id))
display_root.add_child(target)
func _render_p99_market_context() -> void:
var text := _tr("先查看候选并雇佣至 4 人,系统会自动列阵。", "Inspect and hire to four; the Company forms automatically.") if market.state.company.size() < 4 else _tr("点击路线查看风险、敌人和奖励;再次点击出发。", "Tap a route to inspect risk, enemies and reward; tap again to depart.")
if market.state.recovery_required:
text = _tr("队伍战败。支付 ◆6 全员恢复,再选择一条路线。", "Company defeated. Pay ◆6 to recover, then choose a route.")
elif not focused_hire_id.is_empty():
var row: Dictionary = content.market_hires[focused_hire_id]
var profession: Dictionary = content.professions[row.profession_id]
text = "%s · %s · ◆%d\n%s / %s" % [_tr(String(row.name_zh), String(row.name_en)), _tr(String(profession.name_zh), String(profession.name_en)), int(row.list_price), _tr(String(profession.get("strength_zh", "自动职责")), String(profession.get("strength_en", "Automatic role"))), _tr(String(profession.get("limit_zh", "需要队伍配合")), String(profession.get("limit_en", "Needs team support")))]
elif not focused_route_id.is_empty():
var preview: Dictionary = market.route_preview(focused_route_id)
text = "%s · %s · +%d\n%s" % [_tr(String(content.routes[focused_route_id].name_zh), String(content.routes[focused_route_id].name_en)), _tr(["稳妥", "压力", "精英"][int(preview.risk)], ["STEADY", "PRESSURED", "ELITE"][int(preview.risk)]), int(preview.reward), _tr(String(preview.pressure_zh), String(preview.pressure_en))]
if preview.threat_intel_revealed:
text += _tr(" · 到场 %d / 目标 %s / 阵风 %d" % [int(preview.intel.arrival_tick), String(preview.intel.selected_target), int(preview.intel.gust_cadence)], " · ARRIVAL %d / TARGET %s / GUST %d" % [int(preview.intel.arrival_tick), String(preview.intel.selected_target), int(preview.intel.gust_cadence)])
_add_context_ribbon(text)
if market.state.recovery_required:
var recover := _action_button("P99RecoveryButton", _tr("恢复队伍 ◆6", "RECOVER COMPANY ◆6"), recover_company)
recover.position = Vector2(size.x - 226, size.y - 62)
recover.size = Vector2(208, 50)
display_root.add_child(recover)
func _render_market_four() -> void:
_add_background(WINDGLASS_BACKGROUND)
_render_p99_company_line()
var glow := ColorRect.new()
glow.name = "P99MarketFourGlow"
glow.position = Vector2(size.x * 0.50, 0)
glow.size = Vector2(size.x * 0.50, size.y)
glow.color = Color(0.25, 0.76, 0.84, 0.18)
glow.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(glow)
var title := _outlined_label(_tr("第二契约 · 前三轮完成", "CONTRACT 2 · FIRST THREE ROUNDS COMPLETE"), 23, GOLD_LIGHT)
title.name = "P99MarketFourTitle"
title.position = Vector2(size.x * 0.48, size.y * 0.22)
title.size = Vector2(size.x * 0.50, 74)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
display_root.add_child(title)
var body := _outlined_label(_tr("集市 4 已保存\n战线容量 5 · 队伍 4 · 路线与生命保留\n\n下一目标:接近 Gale Loom(第 4-12 轮尚未开放)", "MARKET 4 SAVED\nLINE CAPACITY 5 · COMPANY 4 · ROUTES AND HP PRESERVED\n\nNEXT: APPROACH THE GALE LOOM (ROUNDS 4-12 NOT YET OPEN)"), 13, WHITE)
body.name = "P99MarketFourBody"
body.position = Vector2(size.x * 0.51, size.y * 0.43)
body.size = Vector2(size.x * 0.45, 132)
body.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
body.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
display_root.add_child(body)
func _add_p99_header(title_text: String, subtitle_text: String) -> void:
var band := ColorRect.new()
band.name = "P99Header"
band.position = Vector2.ZERO
band.size = Vector2(size.x, 52)
band.color = Color(0.015, 0.035, 0.075, 0.88)
band.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(band)
var compact := size.x < 800.0 or text_scale > 1.0
var title_fraction := 0.58 if compact else 0.48
var subtitle_fraction := 1.0 - title_fraction
var title := _outlined_label(title_text, 13 if compact else 15, GOLD_LIGHT)
title.name = "P99HeaderTitle"
title.position = Vector2(16, 3)
title.size = Vector2(size.x * title_fraction - 24, 44)
title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
display_root.add_child(title)
var subtitle := _outlined_label(subtitle_text, 9 if compact else 10, WHITE)
subtitle.name = "P99HeaderSubtitle"
subtitle.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
subtitle.clip_text = true
subtitle.position = Vector2(size.x * title_fraction, 3)
subtitle.size = Vector2(size.x * subtitle_fraction - 16, 44)
subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
display_root.add_child(subtitle)
func _add_context_ribbon(value: String) -> void:
var ribbon := ColorRect.new()
ribbon.name = "P99ContextRibbon"
ribbon.position = Vector2(0, size.y - 70)
ribbon.size = Vector2(size.x, 70)
ribbon.color = Color(0.015, 0.035, 0.075, 0.94)
ribbon.mouse_filter = Control.MOUSE_FILTER_IGNORE
display_root.add_child(ribbon)
var detail := _outlined_label(value, 10, WHITE)
detail.name = "P99ContextText"
detail.position = Vector2(16, size.y - 66)
detail.size = Vector2(size.x - 280, 58)
detail.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
display_root.add_child(detail)
func _selected_claim_or_default() -> String:
return charter.state.selected_charter_claim_id if not charter.state.selected_charter_claim_id.is_empty() else "CH-RG01-CATALOG-01"
func _p99_profession_color(profession_id: String) -> Color:
return {"PF-B01": GOLD_LIGHT, "PF-B02": CORAL, "PF-B03": JADE, "PF-B04": CYAN, "PF-B05": Color("#78bfff"), "PF-B06": VIOLET, "PF-B07": Color("#8fe0b6"), "PF-B09": Color("#78d9d0")}.get(profession_id, WHITE)
func _equipment_color(catalog_id: String) -> Color:
if catalog_id == "EQ-041":
return Color("#7dd6c3")
return super._equipment_color(catalog_id)
func _battle_event_word(event_id: String) -> String:
var p99_words := {
"cross_line_hinder": _tr("跨线迟滞", "CROSS HINDER"),
"majority_band_delay": _tr("多数延迟", "BAND DELAY"),
"changed_target_pursuit": _tr("换目标追击", "RETARGET"),
}
return String(p99_words[event_id]) if p99_words.has(event_id) else super._battle_event_word(event_id)