迭代11第②批:GP商店房重构——三选一同构化(P1共享池账本+抽3买1全重抽+A/b乘法定价ceil+断点扩键+货架同构呈现+MC off/on分层;冒烟671/0+off对拍off_n60逐位PASS+探针三件绿)

This commit is contained in:
DKChan committed 2026-10-03 18:19:57 +08:00
1 parent bc17b66c00
commit d79afd2241
18 files changed
+557 -45

No files matched your search

+122 -1
View File
@@ -67,6 +67,12 @@ var _aux_unlocked := {} # aux 解锁门控集(迭代 7
var _attunement: Array = [] # 同调装配集(迭代 11 第①批 T5——adventure params.attunement 下传,begin_battle enc 携带进战斗结算;空集=off 零扰动)
var _attune_charges: Dictionary = {} # 同调主动件充能账本(迭代 11 第①批 T6——adventure 共享 dict 引用;settlement use_aux 扣减直改同引用)
# 关卡末清空+快照计数入 level_finished 载荷)
# ---- 商店共享池模式态(迭代 11 第②批 T3——缺省 legacy=固定三件加定价 off 对照承载,零 RNG 零池消费)----
var _shop_pool_mode := false # pool 模式旗(set_shop_pool 注入时置位——AdventureRunner 按 params.shop_mode 下传)
var _pool_taken: Dictionary = {} # 共享池库存账本共享引用(AdventureRunner._bonus_taken——stock=cap−taken 单点)
var _pool_consume := Callable() # 池消费 handler(AdventureRunner._consume_pool_purchase——_apply_bonus_pick 同口写加算区+账本)
var _shop_shelf: Array[String] = [] # 当前货架(进店抽 3/买后全重抽——kind_id 序)
var _shop_visit_buys := 0 # 本次进店已购次数(A 链定价输入——离开清零)
var hero_atk_pct := 0 # 测试面板覆盖值(切片专用)
var hero_hp_pct := 0
@@ -441,7 +447,11 @@ func _enter_node(node: Dictionary) -> bool:
"shop":
# 商店房兑付(GP+商店房实施批 2026-09-21):到达即开店(覆盖层归呈现层),购买出账/
# 离开归玩家意图——零自动推进;离开确认经 continue_after_node 回选路口
node_entered.emit(node, {"gp": gp})
if _shop_pool_mode:
_shop_open_pool() # 迭代 11 第②批 T3——进店抽 3(恰一 shop_draw scope;买后全重抽同签)
node_entered.emit(node, {"gp": gp, "shelf": true})
return true
node_entered.emit(node, {"gp": gp}) # legacy 固定三件(off 对照承载——零 RNG 零池消费)
return true
push_error("[关卡节点地图] 未知节点类型: %s" % str(node))
return false
@@ -615,6 +625,9 @@ func continue_after_node() -> bool:
var t := String(current_node().get("type", ""))
if t != "reward" and t != "rest" and t != "shop":
return false
if t == "shop":
_shop_shelf = [] # 离开店面货架态清零(断点快照 shop_shelf 键随离开落空架合法态——迭代 11 第②批 T2)
_shop_visit_buys = 0
return _present_choices()
@@ -689,3 +702,111 @@ func player_request_refresh() -> bool:
if _settlement == null:
return false
return _settlement.player_request_refresh()
# ---- 商店共享池模式(迭代 11 第②批 T3——P1 共享池/P4 ceil;legacy 固定三件路径上方原样保留为 off 对照承载)----
# 注入(AdventureRunner._start_current_level 唯一调用方——pool 模式专用):库存账本共享引用 +
# 池消费 handler。legacy 模式不注入(_shop_pool_mode 恒 false——off 态零 RNG 零池消费零扰动)。
func set_shop_pool(taken_ref: Dictionary, consume_handler: Callable) -> void:
_pool_taken = taken_ref
_pool_consume = consume_handler
_shop_pool_mode = true
# 进店开店(_enter_node shop 分支 pool 模式):进店购买次计数清零 + 抽 3 落架(恰一 shop_draw scope)
func _shop_open_pool() -> void:
_shop_visit_buys = 0
_shop_draw_shelf()
# 货架抽签(进店 + 每次买后全重抽共用):牌库 = 货架词表四种中库存 >0 者(库存=上限即库存——
# 与奖励三选一达上限剔牌同一账本承载);E-S weight-key 加权洗牌不放回取 3(数学同奖励洗牌族,
# 自包含实现防奖励侧 RNG 序漂移);消费 shop_draw 封闭流恰一 scope(每次抽 3 恰一取号)。牌库空
# = 空货架合法态(池空硬闸——买光即枯竭既定后果)。
func _shop_draw_shelf() -> void:
_shop_shelf = []
var deck: Array = []
for row in db.shop_pool_rows():
var kind := String(row.get("kind_id", ""))
if pool_stock(kind) > 0:
deck.append(kind)
if deck.is_empty():
return
var stream: RandomNumberGenerator = rng.open_stream("shop_draw")
var keyed: Array = []
for kind in deck:
var w := float(maxi(1, int(db.adventure.bonus_weight(kind))))
keyed.append({"id": kind, "key": pow(stream.randf(), 1.0 / w)})
keyed.sort_custom(func(a, b): return float(a["key"]) > float(b["key"]))
for i in mini(3, keyed.size()):
_shop_shelf.append(String(keyed[i]["id"]))
# 库存余量(账本单点读——gp 无上限种不入货架,防御读返回大数)
func pool_stock(kind: String) -> int:
var cap := int(db.adventure.bonus_cap(kind))
if cap == db.adventure.BONUS_CAP_UNLIMITED:
return 1 << 30
return cap - int(_pool_taken.get(kind, 0))
# 现价 = ceil(基价 × b^(关序−1) × A^(本次进店已购次数))(P4 甲 ceil 向上单一取整——玩家不利向;
# A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5)
func pool_price(kind: String) -> int:
var row: Dictionary = db.shop_pool_row(kind)
var base := float(maxi(1, int(row.get("base_price", 1))))
var price_f: float = base * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) \
* pow(db.shop_price_a(), float(_shop_visit_buys))
return int(ceil(price_f))
# 货架只读投影(呈现层数据源——kind/显示名/件值/现价/库存余量齐备,UI 零复算)。
# 件值=奖励件本体值(db.adventure.bonus_value——池件与三选一件同一本体,买一件=奖励面少一件)。
func shop_shelf() -> Array:
var out: Array[Dictionary] = []
for kind in _shop_shelf:
var row: Dictionary = db.shop_pool_row(kind)
out.append({
"kind_id": kind,
"display_name": String(row.get("display_name", kind)),
"value": int(db.adventure.bonus_value(kind)),
"price": pool_price(kind),
"stock_left": pool_stock(kind),
})
return out
# 购买(pool 模式唯一购买口):在架/余额前置校验零副作用 → handler 消费池库存(_apply_bonus_pick
# 同口写加算区+账本——跨渠道共账)→ 出账 → 进店购买次计数 → wallet 事件 → 三件全重抽(未买
# 不保留——重抽绑购买,无独立刷新动作)
func buy_pool_item(kind: String) -> bool:
if not _shop_pool_mode or not _shop_shelf.has(kind):
push_error("[关卡节点地图] 商店购买拒绝——非池模式或件不在架: %s" % kind)
return false
var price := pool_price(kind)
if gp < price:
push_warning("[关卡节点地图] 购买拒绝——余额不足: %s(需 %d,钱包 %d)" % [kind, price, gp])
return false
if _pool_consume.is_null() or not bool(_pool_consume.call(kind)):
push_error("[关卡节点地图] 购买拒绝——池库存消费失败: %s" % kind)
return false
gp -= price
_shop_visit_buys += 1
wallet_changed.emit("buy", gp, {"product": kind, "price": price})
_shop_draw_shelf() # 买 1 → 三件全重抽(恰一 shop_draw scope——同进店取号签)
return true
# 池模式谓词(呈现层组合根分发用——货架面 vs legacy 商品面)
func is_shop_pool() -> bool:
return _shop_pool_mode
# 断点快照读面(P2 甲货架+账本入断点——AdventureRunner._snapshot_breakpoint 消费;离开店即空架)
func shop_bp_shelf() -> Array:
return _shop_shelf.duplicate()
func shop_bp_visit_buys() -> int:
return _shop_visit_buys