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

111 lines
4.5 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(GP+商店房实施批——商店房覆盖层呈现)
# 商店覆盖层(模态):标题「商店」/ 金币余额 / 三商品卡全陈列(显示名·档位文案·现价·已购档数)/
# 购买按钮(余额不足禁用置灰)/ 离开按钮。模态挂接方式仿既有覆盖层(route_overlay 现成参照——
# dim + CenterContainer + box,组合根 _register_modal 收口层级)。
# 高内聚边界:纯视图 setter 面——商品快照与余额经组合根喂入(runner.shop_products() 只读投影,
# 现价/已购档数由 runner 现算),购买/离开意图经信号外发——零核心写调用、零 runner 持有。
class_name ShopOverlay
extends Control
signal buy_requested(product_id: String)
signal leave_pressed
const ATTR_LABELS := {"atk": "攻击", "max_hp": "生命上限", "armor_class": "护甲"} # 档位文案轴(数据契约 attr 词表)
var _gp_label: Label
var _cards_box: VBoxContainer
var _card_rows: Array[Dictionary] = [] # [{product_id, price, bought, btn}]——refresh_state 就地重刷(零重建回流)
func _init() -> void:
var dim := ColorRect.new()
dim.color = Color(0, 0, 0, 0.6)
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", 12)
center.add_child(box)
var title := UiKit.label("商店", 26, true)
title.add_theme_color_override("font_color", UiKit.WARM)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
box.add_child(title)
_gp_label = UiKit.label("金币 0", 18, true)
_gp_label.add_theme_color_override("font_color", UiKit.GOLD)
_gp_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
box.add_child(_gp_label)
_cards_box = VBoxContainer.new()
_cards_box.add_theme_constant_override("separation", 8)
box.add_child(_cards_box)
var leave_btn := UiKit.button("离开商店")
leave_btn.pressed.connect(_emit_leave_pressed)
box.add_child(leave_btn)
visible = false
# 打开商店(products = runner.shop_products() 投影——[{product_id, display_name, attr, flat_value, price, purchased}])
func open_shop(products: Array, gp: int) -> void:
for child in _cards_box.get_children():
child.queue_free()
_card_rows.clear()
for p in products:
_card_rows.append(_build_card(p))
refresh_state(products, gp)
visible = true
# 状态重刷(购买/钱包变化后调用——就地改文案与按钮禁用态,零重建)
func refresh_state(products: Array, gp: int) -> void:
_gp_label.text = "金币 %d" % gp
var by_id := {}
for p in products:
by_id[String(p.product_id)] = p
for card in _card_rows:
var p: Dictionary = by_id.get(String(card.product_id), {})
if p.is_empty():
continue
card.price.text = "现价 %d 金币" % int(p.price)
card.bought.text = "已购 %d 档" % int(p.purchased)
card.btn.disabled = gp < int(p.price) # 余额不足禁用置灰
# 商品卡(显示名 + 档位文案 | 现价 | 已购档数 | 购买按钮)
func _build_card(p: 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, 6, 12, 6)))
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 14)
card.add_child(row)
var name_l := UiKit.label(_card_title(p), 15, true)
name_l.custom_minimum_size = Vector2(190, 0)
row.add_child(name_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(110, 0)
row.add_child(price_l)
var bought_l := UiKit.label("", 14, false)
bought_l.add_theme_color_override("font_color", UiKit.WARM_FAINT)
row.add_child(bought_l)
var btn := UiKit.button("购买")
btn.pressed.connect(_emit_buy_requested.bind(p))
row.add_child(btn)
_cards_box.add_child(card)
return {"product_id": String(p.product_id), "price": price_l, "bought": bought_l, "btn": btn}
# 卡题 = 显示名 + 档位文案(attr →「攻击 +1」形)
func _card_title(p: Dictionary) -> String:
var attr := String(p.get("attr", ""))
return "%s(%s %+d)" % [String(p.get("display_name", "")), String(ATTR_LABELS.get(attr, attr)), int(p.get("flat_value", 0))]
func _emit_leave_pressed() -> void:
leave_pressed.emit()
func _emit_buy_requested(p: Dictionary) -> void:
buy_requested.emit(String(p.product_id))