迭代11第②批:GP商店房重构——三选一同构化(P1共享池账本+抽3买1全重抽+A/b乘法定价ceil+断点扩键+货架同构呈现+MC off/on分层;冒烟671/0+off对拍off_n60逐位PASS+探针三件绿)
This commit is contained in:
1 parent
bc17b66c00
commit
d79afd2241
18 files changed
+557
-45
No files matched your search
@@ -447,7 +447,7 @@ func _start_adventure() -> void:
|
||||
# 永久强化注入(元进度实现批):冒险开始参数面——meta 现算 override 显式传参(全零 = 白板)
|
||||
# 首次冒险脚本(关卡节点图生成 v0.4):存档布尔位 false = 全关固定模板 0(教学重放);置位后逐关抽签
|
||||
var first_script: bool = not save_manager.is_first_adventure_done()
|
||||
if not adventure.start_adventure(seed_val, {"hero_override": meta.hero_override() if meta != null else {}, "first_script": first_script, "aux_unlocked": _aux.unlocked_set()}):
|
||||
if not adventure.start_adventure(seed_val, {"hero_override": meta.hero_override() if meta != null else {}, "first_script": first_script, "aux_unlocked": _aux.unlocked_set(), "shop_mode": "pool"}):
|
||||
_fatal_screen("冒险启动失败——见输出日志")
|
||||
return
|
||||
|
||||
@@ -496,10 +496,14 @@ func _on_runner_node(node: Dictionary, info: Dictionary) -> void:
|
||||
node_overlay.show_rest(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0)))
|
||||
log_view.append_bb(Narrator.rest_line(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0))))
|
||||
elif t == "shop":
|
||||
# 商店房兑付(GP+商店房实施批):到达即开店——商品快照/余额经 runner 只读投影喂入;
|
||||
# 购买/离开归玩家意图(buy_requested/leave_pressed 接线),零自动推进
|
||||
# 商店房兑付(GP+商店房实施批):到达即开店——商品/货架快照与余额经 runner 只读投影喂入;
|
||||
# 购买/离开归玩家意图(buy_requested/leave_pressed 接线),零自动推进。
|
||||
# pool 面 = 共享池货架三选一同构卡(迭代 11 第②批 T4);legacy 面 = 固定三件(off 对照承载)
|
||||
_open_modal(shop_overlay)
|
||||
shop_overlay.open_shop(runner.shop_products(), int(runner.gp))
|
||||
if runner.is_shop_pool():
|
||||
shop_overlay.open_shelf(runner.shop_shelf(), int(runner.gp))
|
||||
else:
|
||||
shop_overlay.open_shop(runner.shop_products(), int(runner.gp))
|
||||
log_view.append_bb(Narrator.shop_line(int(runner.gp)))
|
||||
logger.log_node(node, info, runner.node_index)
|
||||
_refresh_presentations()
|
||||
@@ -1084,6 +1088,15 @@ func _on_camp_attune_toggle(item_id: String) -> void:
|
||||
func _on_shop_buy(product_id: String) -> void:
|
||||
if runner == null:
|
||||
return
|
||||
if runner.is_shop_pool():
|
||||
# pool 面:买 1 → 核心全重抽——货架整架重投喂(重抽反馈;余额不足核心拒绝零副作用)
|
||||
if not runner.buy_pool_item(product_id):
|
||||
logger.log_ux("ux_reject", {"source": "wallet", "sub_reason": "insufficient_gp"})
|
||||
board_zone.toast.show_toast("金币不足——买不起这件商品", "warn")
|
||||
return
|
||||
shop_overlay.open_shelf(runner.shop_shelf(), int(runner.gp))
|
||||
_refresh_presentations()
|
||||
return
|
||||
if not runner.buy_product(product_id):
|
||||
# 余额不足(按钮禁用为常态防线——兜底拒绝可见,pattern 1)
|
||||
logger.log_ux("ux_reject", {"source": "wallet", "sub_reason": "insufficient_gp"})
|
||||
@@ -1107,7 +1120,10 @@ func _on_wallet_changed(event: String, gp: int, extra: Dictionary) -> void:
|
||||
if String(event) == "gain":
|
||||
_last_gain_amount = int(extra.get("amount", 0)) # 卡⑥ GP 增量取数(每战恰一笔 gain)
|
||||
if shop_overlay.visible and runner != null:
|
||||
shop_overlay.refresh_state(runner.shop_products(), int(runner.gp))
|
||||
if runner.is_shop_pool():
|
||||
shop_overlay.refresh_shelf_state(int(runner.gp)) # pool 面余额联动(货架内容仅买后重抽变)
|
||||
else:
|
||||
shop_overlay.refresh_state(runner.shop_products(), int(runner.gp))
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-21(GP+商店房实施批——商店房覆盖层呈现)
|
||||
# 商店覆盖层(模态):标题「商店」/ 金币余额 / 三商品卡全陈列(显示名·档位文案·现价·已购档数)/
|
||||
# 购买按钮(余额不足禁用置灰)/ 离开按钮。模态挂接方式仿既有覆盖层(legend_overlay 现成参照——
|
||||
# dim + CenterContainer + box,组合根 _register_modal 收口层级)。
|
||||
# 高内聚边界:纯视图 setter 面——商品快照与余额经组合根喂入(runner.shop_products() 只读投影,
|
||||
# 现价/已购档数由 runner 现算),购买/离开意图经信号外发——零核心写调用、零 runner 持有。
|
||||
# 商店覆盖层(模态):标题「商店」/ 金币余额 / 商品卡陈列 / 购买按钮(余额不足禁用置灰)/ 离开按钮。
|
||||
# 模态挂接方式仿既有覆盖层(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)
|
||||
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:
|
||||
@@ -95,3 +100,55 @@ func _emit_leave_pressed() -> void:
|
||||
|
||||
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)
|
||||
Reference in new issue
Block a user