Files
gplan-2/game/presentation/views/career_overlay.gd
T

142 lines
7.4 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VERTICAL SLICE - NOT FOR PRODUCTION
# 生涯覆盖层·只读全屏页(迭代 18 卡④ 生涯界面实装批·期一 2026-10-07——任务书 v1.2 裁点全甲;
# 设计权威 design/gdd/systems/跨局循环/生涯.md v0.1 期一范围)。
# 裁决落形:P1/P2 甲=汇总段六类九项(冒险总局数/通关局数/各关通关次数/Boss 首杀/Boss 总击杀/
# 钥石余额/属性锻造/道具工坊/天赋树——逐行只读);P3 甲=登记 modal_stack 营地同档(设置层旁、
# 确认弹窗之前——登记序=树序=z 序,组合根 addChild 同位);P4 甲=零跳转(页内零「前往他处」
# 入口,仅只读展示+关闭)+局内不放生涯入口(开始覆盖层「营地与系统」组唯一入口)。
# 期二/期三内容(历史回看/图鉴/单局明细)零触碰——页内无任何期二占位件。
# 只读契约(任务书契约 3/4):数据快照由组合根 open_career 喂入(每次开层实时读 SaveManager
# 计数访问器 + MetaProgression 访问器重建——暂停层 R4 同构,快照仅单次开层生命周期,跨开层
# 零持有);本类零核心访问、零写通路——唯一信号 closed(关闭意图,组合根经模态栈单口收口)。
# 零动效(暂停层 P4 甲先例同款):直开直关,无 tween 无 AnimationPlayer。
# R1 公共件单点:全部构成件经 UiKit 工厂产出(page_shell/label/button/keystone_badge),
# 零新素材零新色值零新字体零自建样式。页壳=page_shell 暖亮底(商店同壳——主菜单页族形态)。
class_name CareerOverlay
extends Control
signal closed
var _total_label: Label # 冒险总局数(开局即计含放弃与失败)
var _cleared_label: Label # 通关局数(三关全通才计 1)
var _clears_label: Label # 各关通关次数(一行三关——display_name ×次数)
var _boss_first_label: Label # Boss 首杀(已首杀关数/总关数)
var _boss_kills_label: Label # Boss 总击杀(含重复)
var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件——暂停层同形制)
var _forge_label: Label # 属性锻造(已购等级合计)
var _workshop_label: Label # 道具工坊(已解锁件数/总件数)
var _tree_label: Label # 天赋树已购概览(聚合单条——暂停层同构)
func _init() -> void:
# 全屏页壳(T6/T7 对齐批先例——暖亮底+石纹弱质感+暗角+56px 通栏顶条+内容列)
var page: Dictionary = UiKit.page_shell(self, true)
var top: HBoxContainer = page.top_row
top.add_child(UiKit.label("生 涯",UiKit.FS_TITLE, true, UiKit.WARM))
var top_sp := Control.new()
top_sp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
top.add_child(top_sp)
var close_btn := UiKit.button("返回", 90.0) # 紧凑返回钮(设置/暂停面先例形制)
close_btn.tooltip_text = "关闭生涯页,返回主菜单(本页只读,不发生任何购买或修改)"
close_btn.pressed.connect(_emit_closed)
top.add_child(close_btn)
var box: VBoxContainer = page.box
var hint := UiKit.label("跨局生涯汇总(只读——购买与装配在营地;历史冒险回看随后续批次)",
UiKit.FS_HINT, false, UiKit.WARM_SOFT)
box.add_child(hint)
# 组一·冒险战绩(终身计数域——存档 v6:总局数开局即计、通关局数三关全通才计)
# 对比度口径:页底=暖亮档深棕(BG_BRIGHT),文字全用亮墨族 token(WARM 9.0:1 / WARM_SOFT 8.0:1 /
# CANDLE 7.2:1 / INK_L_GOLD 9.1:1 实测对 BG_BRIGHT 全过 §7.2 ≥4.5:1——深墨族〔INK_LABEL〕
# 压深棕底仅 ~1.3:1 属违例,禁用)
_section_head(box, "冒险战绩")
_total_label = _row(box, "冒险总局数")
_cleared_label = _row(box, "通关局数")
_clears_label = _row(box, "各关通关次数")
_boss_first_label = _row(box, "Boss 首杀")
_boss_kills_label = _row(box, "Boss 总击杀")
# 组二·资源与成长(钥石/三柜台解锁概览——现役只读投影)
_section_head(box, "资源与成长")
var bal_row := HBoxContainer.new()
bal_row.add_theme_constant_override("separation", 8)
box.add_child(bal_row)
var bal_name := UiKit.label("钥石余额",UiKit.FS_BODY, false, UiKit.WARM)
bal_row.add_child(bal_name)
_keystone_badge = UiKit.keystone_badge("钥石 0")
bal_row.add_child(_keystone_badge)
_forge_label = _row(box, "属性锻造")
_workshop_label = _row(box, "道具工坊")
_tree_label = _row(box, "天赋树")
_tree_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
visible = false # 显式初始化——不靠树序巧合(缺陷⑤家族纪律)
# ---- 开层(组合根唯一调用口——快照随开随建,只读同源口径同暂停层 R4)----
func open_career(data: Dictionary) -> void:
_total_label.text = "%d 局" % int(data.get("total", 0))
_cleared_label.text = "%d 局(三关全通)" % int(data.get("cleared", 0))
var parts: Array[String] = []
for row in (data.get("clears", []) as Array):
parts.append("%s ×%d" % [String(row.get("display_name", "")), int(row.get("count", 0))])
_clears_label.text = " · ".join(parts) if not parts.is_empty() else "—"
_boss_first_label.text = "%d/%d 关" % [int(data.get("boss_first_kills", 0)), int(data.get("boss_first_total", 0))]
_boss_kills_label.text = "%d 次" % int(data.get("boss_kills", 0))
_keystone_badge.set_text("钥石 %d" % int(data.get("keystone", 0)))
_forge_label.text = "已购 %d 级(三属性合计)" % int(data.get("forge_levels_total", 0))
var ws: Array = data.get("workshop", []) as Array
var unlocked := 0
for row in ws:
if bool(row.get("unlocked", false)):
unlocked += 1
_workshop_label.text = "已解锁 %d/%d 件" % [unlocked, ws.size()] if not ws.is_empty() else "—"
_tree_label.text = _tree_summary(data)
visible = true
# 天赋树聚合文案(暂停层 _rebuild_tree 同构——已购节点等级+Fork 激活侧;零已购=空态句)
func _tree_summary(data: Dictionary) -> String:
var owned: Array[String] = []
for row in (data.get("tree", []) as Array):
var lvl := int(row.get("level", 0))
if lvl > 0:
owned.append("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, int(row.get("cap", 1))])
for fg in (data.get("fork_groups", []) as Array):
var active := String(fg.get("active_side", ""))
if active == "":
continue
var side_name := active
for sd in (fg.get("sides", []) as Array):
if String(sd.get("side", "")) == active:
side_name = String(sd.get("display_name", active))
break
owned.append("%s·激活 %s" % [String(fg.get("display_name", "")), side_name])
if owned.is_empty():
return "尚未购买任何节点"
return "已购 %d 项:%s" % [owned.size(), " · ".join(owned)]
func _emit_closed() -> void:
closed.emit()
# ---- 行构件(名称列 + 数值列——暂停层行族同构;数值 Label 返回供探针/冒烟投影断言)----
func _section_head(box: VBoxContainer, text: String) -> void:
box.add_child(UiKit.label(text,UiKit.FS_BODY, true, UiKit.INK_L_GOLD))
func _row(box: VBoxContainer, name_text: String) -> Label:
var line := HBoxContainer.new()
line.add_theme_constant_override("separation", 8)
box.add_child(line)
var name_l := UiKit.label(name_text,UiKit.FS_BODY, false, UiKit.WARM)
name_l.custom_minimum_size = Vector2(150, 0)
line.add_child(name_l)
var val_l := UiKit.label("—",UiKit.FS_BODY, true, UiKit.CANDLE)
line.add_child(val_l)
return val_l