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

198 lines
8.8 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
# Date: 2026-09-21(元进度实现批——营地覆盖层呈现)
# 营地覆盖层(模态):标题「营地 · 永久强化」+ 钥石余额 / 柜台一「属性锻造」(三行可购——显示名 ·
# 当前 Lv · 每级 +flat · 下级价格 · 购买按钮余额不足禁用)/ 柜台二「道具工坊」(六行——显示名 ·
# 解锁条件 · 进度 · 状态;局内效果排 Alpha 魔法道具重设计批——代拍板 3,注记一行)/ 柜台三
# 「奖励格图鉴」(占位锁定行——内容排 Alpha 地图战斗资源差异化批——代拍板 4)/「最近冒险」列表
# (recent_summaries 投影:结果/通关数/钥石合计/种子)/ 关闭按钮。
# 挂接方式:仿 ShopOverlay(dim + CenterContainer + box);开合与开始层互斥仿 TunePanel 先例
# (开始覆盖层非模态且树序在后——打开本层前由组合根隐藏开始层,规范 2.3 遮挡纪律)。
# 高内聚边界:纯视图 setter 面——账本快照经组合根喂入(MetaProgression 只读投影),购买/关闭
# 意图经信号外发——零核心写调用、零 MetaProgression 持有。视觉 = ui_kit 程序占位
#(代拍板 8——视觉终稿以表现层样张过审为前置,归 HUD 协作批)。
class_name CampOverlay
extends Control
signal buy_requested(attr: String)
signal closed
const ATTR_LABELS := {"atk": "攻击", "max_hp": "生命上限", "armor_class": "护甲"} # 档位文案轴(数据契约 attr 词表,与 ShopOverlay 同款)
var _balance_label: Label
var _forge_holder: VBoxContainer
var _workshop_holder: VBoxContainer
var _forge_rows: Array = [] # [{attr, level_l, price_l, btn}]——refresh_camp 就地重刷(零重建回流)
var _workshop_rows: Array = [] # [{item_id, progress_l, status_l}]
var _recent_list: Label # 最近冒险(逐行拼接 label——只读账面,随 open_camp 重建)
func _init() -> void:
var dim := ColorRect.new()
dim.color = Color(0, 0, 0, 0.72)
dim.set_anchors_preset(Control.PRESET_FULL_RECT)
add_child(dim)
var center := CenterContainer.new()
center.set_anchors_preset(Control.PRESET_FULL_RECT)
dim.add_child(center)
var box := VBoxContainer.new()
box.add_theme_constant_override("separation", 6)
center.add_child(box)
var title := UiKit.label("营地 · 永久强化", 24, true)
title.add_theme_color_override("font_color", UiKit.WARM)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
box.add_child(title)
_balance_label = UiKit.label("钥石 0", 18, true)
_balance_label.add_theme_color_override("font_color", UiKit.GOLD)
_balance_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
box.add_child(_balance_label)
box.add_child(HSeparator.new())
box.add_child(_counter_header("属性锻造(钥石直购 · 每级 flat 整数 · 价格几何递增)"))
_forge_holder = VBoxContainer.new()
_forge_holder.add_theme_constant_override("separation", 4)
box.add_child(_forge_holder)
box.add_child(_counter_header("道具工坊(累计通关次数解锁 · 局内效果排 Alpha 魔法道具重设计批)"))
_workshop_holder = VBoxContainer.new()
_workshop_holder.add_theme_constant_override("separation", 4)
box.add_child(_workshop_holder)
box.add_child(_counter_header("奖励格图鉴"))
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)", 13, false)
lock_l.add_theme_color_override("font_color", UiKit.WARM_FAINT)
box.add_child(lock_l)
box.add_child(HSeparator.new())
box.add_child(_counter_header("最近冒险"))
var scroll := ScrollContainer.new()
scroll.custom_minimum_size = Vector2(560, 96)
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
box.add_child(scroll)
_recent_list = UiKit.label("", 13, false)
_recent_list.add_theme_color_override("font_color", UiKit.WARM_SOFT)
_recent_list.custom_minimum_size = Vector2(544, 0)
_recent_list.autowrap_mode = TextServer.AUTOWRAP_OFF
scroll.add_child(_recent_list)
var close_btn := UiKit.button("关闭营地(回开始界面)")
close_btn.pressed.connect(func(): closed.emit())
box.add_child(close_btn)
visible = false
func _counter_header(text: String) -> Label:
var l := UiKit.label(text, 15, true)
l.add_theme_color_override("font_color", UiKit.WARM_SOFT)
return l
# ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡)----
# 打开营地(全量重建三柜台行 + 最近冒险——数据 = 组合根 _camp_data() 投影)
func open_camp(data: Dictionary) -> void:
_rebuild_forge(data)
_rebuild_workshop(data)
_rebuild_recent(data)
refresh_camp(data)
visible = true
# 状态重刷(购买后调用——余额/价格/禁用态/工坊状态就地改文案,零重建)
func refresh_camp(data: Dictionary) -> void:
var balance := int(data.get("balance", 0))
_balance_label.text = "钥石 %d" % balance
var forge_by_attr := {}
for row in data.get("forge", []):
forge_by_attr[String(row.get("attr", ""))] = row
for card in _forge_rows:
var row: Dictionary = forge_by_attr.get(String(card.attr), {})
if row.is_empty():
continue
card.level_l.text = "Lv %d" % int(row.get("level", 0))
card.price_l.text = "下级价格 %d 钥石" % int(row.get("price", 0))
card.btn.disabled = balance < int(row.get("price", 0)) # 余额不足禁用置灰
var ws_by_id := {}
for row in data.get("workshop", []):
ws_by_id[String(row.get("item_id", ""))] = row
for card in _workshop_rows:
var row: Dictionary = ws_by_id.get(String(card.item_id), {})
if row.is_empty():
continue
var th := int(row.get("threshold", 0))
var prog := int(row.get("progress", 0))
var unlocked := bool(row.get("unlocked", false))
card.progress_l.text = "累计通关 %d / %d 次" % [prog, th]
card.status_l.text = "已解锁" if unlocked else "未解锁"
card.status_l.add_theme_color_override("font_color", UiKit.GOLD if unlocked else UiKit.WARM_FAINT)
func _rebuild_forge(data: Dictionary) -> void:
_forge_rows.clear()
for child in _forge_holder.get_children():
child.queue_free()
for row in data.get("forge", []):
_forge_rows.append(_build_forge_row(_forge_holder, row))
func _build_forge_row(holder: VBoxContainer, row: Dictionary) -> Dictionary:
var card := PanelContainer.new()
card.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.BG_OVERLAY, UiKit.GOLD_DIM, 4, 1, Vector4(12, 4, 12, 4)))
var line := HBoxContainer.new()
line.add_theme_constant_override("separation", 12)
card.add_child(line)
var attr := String(row.get("attr", ""))
var name_l := UiKit.label("%s(每级 %+d flat)" % [String(row.get("display_name", "")), int(row.get("flat", 0))], 14, true)
name_l.custom_minimum_size = Vector2(210, 0)
line.add_child(name_l)
var level_l := UiKit.label("", 14, false)
level_l.custom_minimum_size = Vector2(64, 0)
line.add_child(level_l)
var price_l := UiKit.label("", 14, false)
price_l.add_theme_color_override("font_color", UiKit.WARM_SOFT)
price_l.custom_minimum_size = Vector2(150, 0)
line.add_child(price_l)
var btn := UiKit.button("锻造 +1 级")
btn.pressed.connect(func(): buy_requested.emit(attr))
line.add_child(btn)
holder.add_child(card)
return {"attr": attr, "level_l": level_l, "price_l": price_l, "btn": btn}
func _rebuild_workshop(data: Dictionary) -> void:
_workshop_rows.clear()
for child in _workshop_holder.get_children():
child.queue_free()
for row in data.get("workshop", []):
var card := PanelContainer.new()
card.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.BG_OVERLAY, UiKit.WARM_FAINT, 4, 1, Vector4(12, 4, 12, 4)))
var line := HBoxContainer.new()
line.add_theme_constant_override("separation", 12)
card.add_child(line)
var name_l := UiKit.label(String(row.get("display_name", "")), 14, true)
name_l.custom_minimum_size = Vector2(210, 0)
line.add_child(name_l)
var progress_l := UiKit.label("", 13, false)
progress_l.add_theme_color_override("font_color", UiKit.WARM_SOFT)
progress_l.custom_minimum_size = Vector2(150, 0)
line.add_child(progress_l)
var status_l := UiKit.label("", 13, true)
line.add_child(status_l)
_workshop_holder.add_child(card)
_workshop_rows.append({"item_id": String(row.get("item_id", "")), "progress_l": progress_l, "status_l": status_l})
func _rebuild_recent(data: Dictionary) -> void:
var lines: Array = []
var recent: Array = data.get("recent", [])
if recent.is_empty():
lines.append(" (暂无冒险记录——完成或结束一次冒险后入账)")
for s in recent:
var verdict := "失败"
if String(s.get("result", "")) == "completed":
verdict = "完成"
elif String(s.get("result", "")) == "abandoned":
verdict = "放弃"
lines.append("%s · 通关 %d 关 · 钥石 +%d · 种子 %d" % [verdict,
int(s.get("levels_cleared", 0)), int(s.get("keystone_total", 0)), int(s.get("master_seed", 0))])
_recent_list.text = "\n".join(lines)