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

155 lines
7.1 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+商店房实施批——商店房覆盖层呈现)
# 商店覆盖层(模态):标题「商店」/ 金币余额 / 商品卡陈列 / 购买按钮(余额不足禁用置灰)/ 离开按钮。
# 模态挂接方式仿既有覆盖层(legend_overlay 现成参照——dim + CenterContainer + box,组合根
# _register_modal 收口层级)。
# 双面承载(M-4 迭代 11 第②批 T4):legacy 面 = 三商品卡全陈列(open_shop/refresh_state——shop=off
# 对照承载原样保留);pool 面 = 共享池货架三选一同构卡(open_shelf——进店抽 3 + 买 1 全重抽反馈 +
# 库存余量就近呈现,买后组合根重投喂货架整架重建)。高内聚边界:纯视图 setter 面——商品/货架快照
# 与余额经组合根喂入(runner 投影现算现价,视图零复算定价公式),购买/离开意图经信号外发——
# 零核心写调用、零 runner 持有。
class_name ShopOverlay
extends Control
signal buy_requested(product_id: String) # legacy=product_id / pool=kind_id(组合根按模式分发)
signal leave_pressed
const ATTR_LABELS := {"atk": "攻击", "max_hp": "生命上限", "armor_class": "护甲"} # 档位文案轴(数据契约 attr 词表)
const KIND_LABELS := {"attack": "攻击", "max_hp": "生命上限", "armor": "护甲", "ap": "行动点"} # 货架件值文案轴(共享池 kind 词表——迭代 11 第②批)
var _gp_label: Label
var _cards_box: VBoxContainer
var _card_rows: Array[Dictionary] = [] # [{product_id, price, bought, btn}]——refresh_state 就地重刷(零重建回流)
var _shelf_rows: Array[Dictionary] = [] # [{kind_id, btn}]——pool 面卡片(买后整架重建=重抽反馈)
func _init() -> void:
var box: VBoxContainer = UiKit.modal_shell(self, Color(0, 0, 0, 0.6)).box
var title := UiKit.label("商店", 26, true, UiKit.WARM)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
box.add_child(title)
_gp_label = UiKit.label("金币 0", 18, true, 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, UiKit.WARM_SOFT)
price_l.custom_minimum_size = Vector2(110, 0)
row.add_child(price_l)
var bought_l := UiKit.label("", 14, false, 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))
# ---- 共享池货架面(M-4 迭代 11 第②批 T4——P3 甲程序占位,三选一同构卡;UiKit 既有件)----
# 打开/重刷货架(cards = runner.shop_shelf() 投影——[{kind_id, display_name, value, price, stock_left}])。
# 买后组合根重投喂新货架 = 整架重建呈现(三件全重抽反馈——未买不保留);空架 = 池空硬闸合法态。
func open_shelf(cards: Array, gp: int) -> void:
for child in _cards_box.get_children():
child.queue_free()
_shelf_rows.clear()
for c in cards:
_shelf_rows.append(_build_shelf_card(c))
if cards.is_empty():
var empty := UiKit.label("货架售罄——共享池已买空(属性件全冒险共 3+3+3 份 / AP 件 2 份)", 14, false, UiKit.WARM_FAINT)
empty.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_cards_box.add_child(empty)
refresh_shelf_state(gp)
visible = true
# 货架余额重刷(就地改禁用态——买后重建路径经 open_shelf 覆盖,本口供 wallet 变化联动)
func refresh_shelf_state(gp: int) -> void:
_gp_label.text = "金币 %d" % gp
for card in _shelf_rows:
card.btn.disabled = gp < int(card.price)
# 货架卡(显示名+件值文案 | 现价 | 库存余量 | 购买钮)——三选一同构语言:买 1 = 一次真实购买
func _build_shelf_card(c: Dictionary) -> Dictionary:
var kind := String(c.get("kind_id", ""))
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("%s(%s %+d)" % [String(c.get("display_name", kind)), String(KIND_LABELS.get(kind, kind)), int(c.get("value", 0))], 15, true)
name_l.custom_minimum_size = Vector2(190, 0)
row.add_child(name_l)
var price_l := UiKit.label("现价 %d 金币" % int(c.get("price", 0)), 14, false, UiKit.WARM_SOFT)
price_l.custom_minimum_size = Vector2(110, 0)
row.add_child(price_l)
var stock_l := UiKit.label("库存余 %s" % str(int(c.get("stock_left", 0))) if int(c.get("stock_left", 0)) < (1 << 20) else "库存余 ∞", 14, false, UiKit.WARM_FAINT)
row.add_child(stock_l)
var btn := UiKit.button("购买")
btn.pressed.connect(_emit_shelf_buy.bind(kind))
row.add_child(btn)
_cards_box.add_child(card)
return {"kind_id": kind, "price": int(c.get("price", 0)), "btn": btn}
func _emit_shelf_buy(kind: String) -> void:
buy_requested.emit(kind)