T5(迭代22秘宝批): 存档面+死通道拆除——treasure_inventory保留注记(主动件骑库存承载在役:线香/天眼handle+断点aux_items快照+set_attune_charges差额注入全链保留); 替换子交互整体拆除(CURIO_KINDS除名后不可达死通道=装配不可变契约1实现面保证): adventure_runner六函数删(_pick_treasure_item/resolve_treasure_replace/_resolve_node_replace/_replace_in_transit/_draw_pending_treasure/resolve_curio_replace)+_bonus_treasure_carry跨关携带/_last_draw_treasure掷定链/_roll_from/_bonus_treasure_pool/bonus_aux_carry断点键; level_runner.grant_treasure_item/treasure_items.holding_records删; 呈现面=controller替换子态三函数+aux_detail卡片直显链/curio_pick_overlay replace状态机(信号+面板+open_replace)/main两接线/level_autoplay替换策略; smoke与ui_click_probe替换分支摘除+reward_overlay_test替换段摘除; 修正则贪吃误删=apply_node_curio普通兑付主体补回。冒烟895/0+run_all 22套+探针全绿〔秘宝批 V15/V16拆除路线/V13〕
This commit is contained in:
1 parent
b7a2eadd2c
commit
73db171cb4
13 files changed
+42
-346
No files matched your search
@@ -43,7 +43,6 @@ extends RefCounted
|
||||
signal level_started(level_index: int, display_name: String) # 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行
|
||||
signal curio_choice_presented(payload: Dictionary) # 专长三选一停点(非末关通关恰一次;迭代 20 T5 换代正名——旧「提升三选一」退役,通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
|
||||
signal curio_resolved(result: Dictionary) # 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index}
|
||||
signal treasure_replace_presented(items: Array, incoming: Dictionary) # 同调位满位替换子交互(pick_curio 道具件满位时)——items=现有持有件记录、incoming=新件记录
|
||||
signal adventure_finished(summary: Dictionary) # 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops 迭代 10)
|
||||
# ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明;ADR-0001 S6 经 _forward_* 方法)----
|
||||
signal node_entered(node: Dictionary, info: Dictionary)
|
||||
@@ -94,9 +93,7 @@ var _feats: Array[String] = [] # 特性持有集(迭代 12 T1——
|
||||
var _tree_rules: Array = [] # 树 rule 键集(迭代 12 T10——Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面)
|
||||
var _curio_pending := {} # 停点态:{"candidates": Array, "payload": Dictionary, "chosen"/"incoming": 替换在途}——空 = 无停点
|
||||
var _curio_result := {} # 已兑付待推进态(curio_resolved 载荷副本——中断续玩不重兑付)
|
||||
var _bonus_treasure_carry: Array[String] = [] # 道具件跨关携带(兑付后注入下一关库存——清库后授予的体验窗)
|
||||
var _keystone_drops: Array = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源)
|
||||
var _last_draw_treasure: Array[String] = [] # 最近一次抽签掷定的道具件(掷定前置——卡片直显具体道具;追加批二)
|
||||
var _node_curio_pending := {} # 奖励节点三选一停点态(迭代 10 追加批——共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
|
||||
var _feat_ticket_pity := 0 # 门票保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零)
|
||||
var _feat_ticket_sold := 0 # 门票限购计数(T5——选中/买下即 +1;拿到即开不进持有集)
|
||||
@@ -212,8 +209,6 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
_feats = []
|
||||
for r in (bp.get("relics", []) as Array):
|
||||
_feats.append(String(r))
|
||||
for v in (bp.get("bonus_aux_carry", []) as Array):
|
||||
_bonus_treasure_carry.append(String(v))
|
||||
_keystone_drops = (bp.get("keystone_drops", []) as Array).duplicate(true)
|
||||
_base_override = _normalize_override(params.get("hero_override", {}))
|
||||
# 生命账本越界钳制(T17——spawn 守卫 EC16 免疫):断点可能由旧口径版本写出(如调试上限
|
||||
@@ -256,19 +251,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
|
||||
else:
|
||||
migrated.append({"kind": String(c), "tier": db.adventure.TIER_COMMON})
|
||||
_curio_pending = {"candidates": migrated}
|
||||
if bp_pending.has("aux_detail"):
|
||||
_curio_pending["aux_detail"] = (bp_pending.get("aux_detail", []) as Array).duplicate(true) # 掷定前置随停点快照续接
|
||||
var payload: Dictionary = (bp_pending.get("payload", {}) as Dictionary).duplicate(true)
|
||||
if bp_pending.has("incoming"):
|
||||
_curio_pending["chosen"] = "treasure_item"
|
||||
var incoming: Array = (bp_pending.get("incoming", []) as Array).duplicate(true)
|
||||
_curio_pending["incoming"] = incoming
|
||||
state = "running"
|
||||
curio_choice_presented.emit(payload)
|
||||
# 替换在途续发(呈现层直落替换子态——持有件记录按恢复后 carry 重建)
|
||||
treasure_replace_presented.emit(_carry_records(),
|
||||
{"index": -1, "item_id": String(incoming[0]) if not incoming.is_empty() else "", "count": incoming.size()})
|
||||
return true
|
||||
state = "running"
|
||||
curio_choice_presented.emit(payload)
|
||||
return true
|
||||
@@ -295,7 +278,6 @@ func _reset_curio_state() -> void:
|
||||
_feats = []
|
||||
_curio_pending = {}
|
||||
_curio_result = {}
|
||||
_bonus_treasure_carry = []
|
||||
_keystone_drops = []
|
||||
_node_curio_pending = {}
|
||||
adventure_counters = {}
|
||||
@@ -420,8 +402,6 @@ func pick_curio(kind: String, tier: String = "", feat_id: String = "") -> bool:
|
||||
return _redeem_feat_ticket() # T5 拿到即开——停点候选替换为专长三选一(零摘牌/不进持有集)
|
||||
var value: int = int(db.adventure.curio_value(kind, tier))
|
||||
var hp_before := int(ledger.get("hero_hp", 0))
|
||||
if kind == "treasure_item":
|
||||
return _pick_treasure_item(value) # 道具件分派通关渠道(持有容器 = 跨关携带——与节点渠道分轨;池种退役后不可达)
|
||||
if not _apply_curio_pick(kind, tier, value, feat_id):
|
||||
push_error("[冒险结构] pick_curio 拒绝——未知件: %s|%s|%s" % [kind, tier, feat_id])
|
||||
return false
|
||||
@@ -549,44 +529,6 @@ func _pool_stock_snapshot() -> Dictionary:
|
||||
return out
|
||||
|
||||
|
||||
# 道具件兑付(同调位约束):有位直接入跨关携带;满位转替换子交互(incoming 暂存、等 resolve_treasure_replace)。
|
||||
# 掷定前置(追加批二):具体道具随候选抽定(_last_draw_treasure / _curio_pending.aux_detail)——本函数消费预掷值,
|
||||
# 断点续玩/无预掷值时回退当场掷定(流序不同但消费数不变,确定性由调用序保证)。
|
||||
# 持有容器 = 跨关携带 _bonus_treasure_carry(与节点渠道的关卡域库存分轨——容器差异保守不合,注记在案)。
|
||||
func _pick_treasure_item(count: int) -> bool:
|
||||
var picked: Array = _draw_pending_treasure(_curio_pending, count, true)
|
||||
if picked.is_empty():
|
||||
push_error("[冒险结构] pick_curio(treasure_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
|
||||
return false
|
||||
var hp_before := int(ledger.get("hero_hp", 0))
|
||||
# 迭代 11 第①批 T7——同调位守卫随过渡键退役拆除(aux 卡出池后本路径不可达;能力件容量闸
|
||||
# 正主 = 营地装配面 Attunement.slots 满位禁选)
|
||||
for iid in picked:
|
||||
_bonus_treasure_carry.append(String(iid))
|
||||
_finish_curio_pick("treasure_item", picked.size(), hp_before)
|
||||
return true
|
||||
|
||||
|
||||
# 替换子交互受理(discard_index = -1 放弃新件不获取;否则丢该件腾位收新)——终态恒 ≤ 同调位
|
||||
# 持有容器 = 跨关携带(序号寻址);与节点渠道(库存句柄寻址)的丢弃/授予/收尾三步保守不合。
|
||||
func resolve_treasure_replace(discard_index: int) -> bool:
|
||||
if not _replace_in_transit(_curio_pending):
|
||||
push_error("[冒险结构] resolve_treasure_replace 拒绝——无替换在途")
|
||||
return false
|
||||
var incoming: Array = _curio_pending.get("incoming", []) as Array
|
||||
var hp_before := int(ledger.get("hero_hp", 0))
|
||||
if discard_index >= 0:
|
||||
if discard_index >= _bonus_treasure_carry.size():
|
||||
push_error("[冒险结构] resolve_treasure_replace 拒绝——序号不在持有集: %d" % discard_index)
|
||||
return false
|
||||
_bonus_treasure_carry.remove_at(discard_index)
|
||||
for iid in incoming:
|
||||
_bonus_treasure_carry.append(String(iid))
|
||||
# 放弃新件(discard_index < 0)= 原持有集不动、新件不获取
|
||||
_finish_curio_pick("treasure_item", incoming.size(), hp_before)
|
||||
return true
|
||||
|
||||
|
||||
# 兑付收口(恰一次):清停点态 → 记已兑付态 → emit → 停点快照(等继续确认——中断续玩不重兑付)。
|
||||
# picked = kind;tier 随载荷(TP0——大小档位读数面);relic 伴随键随载荷(迭代 12 T1——反馈行取数)
|
||||
func _finish_curio_pick(picked: String, value: int, hp_before: int, tier: String = "", feat_id: String = "") -> void:
|
||||
@@ -608,16 +550,15 @@ func _finish_curio_pick(picked: String, value: int, hp_before: int, tier: String
|
||||
|
||||
# 候选呈现(节点到达时调用——恰一 reward_pick scope 洗牌 + 道具件掷定 scope;本停点断点不载,
|
||||
# 续玩重建到选路停点后重走重抽,恢复的全局计数器保证同候选逐位复现)。
|
||||
# 掷定前置(追加批二):返回 dict 带 aux_detail(道具件具体道具)供卡片直显。
|
||||
func node_curio_candidates() -> Dictionary:
|
||||
var cands: Array = _draw_curio_candidates("reward_node")
|
||||
return {"candidates": cands, "aux_detail": _last_draw_treasure.duplicate()}
|
||||
return {"candidates": cands}
|
||||
|
||||
|
||||
func present_node_curio(candidates: Array, treasure_detail: Array = []) -> void:
|
||||
func present_node_curio(candidates: Array) -> void:
|
||||
_reset_refresh_x() # 迭代 11 第①批 T5b——碰三选一重置 X 余量(不结转)
|
||||
_curio_paid_refreshes = 0 # 付费刷计数随停点清零(对齐商店进店清零——2026-10-06)
|
||||
_node_curio_pending = {"candidates": candidates.duplicate(true), "aux_detail": treasure_detail.duplicate(true)}
|
||||
_node_curio_pending = {"candidates": candidates.duplicate(true)}
|
||||
|
||||
|
||||
# 点选兑付(节点域——无部分回血无钥石;令牌消费与牌库摘牌与通关三选一同库;relic 同权入持有集)
|
||||
@@ -632,8 +573,6 @@ func apply_node_curio(kind: String, tier: String = "", feat_id: String = "") ->
|
||||
push_error("[冒险结构] apply_node_curio 拒绝——令牌不在候选: %s|%s|%s" % [kind, tier, feat_id])
|
||||
return false
|
||||
var value: int = int(db.adventure.curio_value(kind, tier))
|
||||
if kind == "treasure_item":
|
||||
return _apply_node_treasure(value) # 道具件分派节点渠道(持有容器 = 关卡域库存——与通关渠道分轨;池种退役后不可达)
|
||||
if not _apply_curio_pick(kind, tier, value, feat_id):
|
||||
push_error("[冒险结构] apply_node_curio 拒绝——未知件: %s|%s|%s" % [kind, tier, feat_id])
|
||||
return false
|
||||
@@ -641,46 +580,6 @@ func apply_node_curio(kind: String, tier: String = "", feat_id: String = "") ->
|
||||
return true
|
||||
|
||||
|
||||
# 节点渠道道具件(同调位约束):持有=关卡域库存+跨关携带,有位直接入本关库存(当关可用);
|
||||
# 满位转替换子交互(丢弃目标=库存件 handle——与通关渠道的 carry 序号分轨,容器语义保守不合)。
|
||||
# 掷定前置:消费 _node_curio_pending.aux_detail 预掷值(缺省回退当场掷定)。
|
||||
func _apply_node_treasure(count: int) -> bool:
|
||||
var picked: Array = _draw_pending_treasure(_node_curio_pending, count, false)
|
||||
if picked.is_empty():
|
||||
push_error("[冒险结构] apply_node_curio(treasure_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
|
||||
return false
|
||||
var records: Array = []
|
||||
if _runner != null and _runner.treasure_inventory != null:
|
||||
records = _runner.treasure_inventory.holding_records()
|
||||
# 迭代 11 第①批 T7——守卫随过渡键退役拆除(CURIO_KINDS 已除名 treasure_item,本分支不可达;
|
||||
# 替换子交互路由随容量闸一并退役——能力件容量闸正主 = 营地装配面满位禁选)
|
||||
if _runner != null:
|
||||
for iid in picked:
|
||||
var rec: Dictionary = _runner.grant_treasure_item(String(iid), "reward_node")
|
||||
if rec.is_empty():
|
||||
push_warning("[冒险结构] 奖励节点道具授予被拒(防御兜底留痕): %s" % String(iid))
|
||||
_finish_node_bonus("treasure_item", picked.size())
|
||||
return true
|
||||
|
||||
|
||||
func _resolve_node_replace(discard_handle: int) -> bool:
|
||||
if not _replace_in_transit(_node_curio_pending):
|
||||
push_error("[冒险结构] resolve_node_replace 拒绝——无替换在途")
|
||||
return false
|
||||
var incoming: Array = _node_curio_pending.get("incoming", []) as Array
|
||||
if discard_handle >= 0:
|
||||
if _runner == null or _runner.treasure_inventory == null or not _runner.treasure_inventory.discard_item(discard_handle):
|
||||
push_error("[冒险结构] resolve_node_replace 拒绝——句柄不在持有集: %d" % discard_handle)
|
||||
return false
|
||||
for iid in incoming:
|
||||
var rec: Dictionary = _runner.grant_treasure_item(String(iid), "reward_node")
|
||||
if rec.is_empty():
|
||||
push_warning("[冒险结构] 替换子道具授予被拒(防御兜底留痕): %s" % String(iid))
|
||||
# 放弃新件(discard_handle < 0)= 原持有集不动、新件不获取
|
||||
_finish_node_bonus("treasure_item", incoming.size())
|
||||
return true
|
||||
|
||||
|
||||
# 冒险开始充能账本重置(T6——装备 active_charged 件各回 charges_per_adventure;被动件不入账)
|
||||
func _seed_attune_charges() -> void:
|
||||
attune_charges = {}
|
||||
@@ -742,11 +641,10 @@ func battery_pending() -> int:
|
||||
return _battery_pending
|
||||
|
||||
|
||||
# 节点停点在途态读回(组合根重 present 用——candidates/aux_detail 值拷贝)
|
||||
# 节点停点在途态读回(组合根重 present 用——candidates 值拷贝)
|
||||
func node_curio_pending_state() -> Dictionary:
|
||||
return {
|
||||
"candidates": (_node_curio_pending.get("candidates", []) as Array).duplicate(true),
|
||||
"aux_detail": (_node_curio_pending.get("aux_detail", []) as Array).duplicate(true),
|
||||
}
|
||||
|
||||
|
||||
@@ -792,10 +690,8 @@ func _refresh_curio_common(paid: bool) -> bool:
|
||||
if not _curio_pending.is_empty():
|
||||
var cands: Array = _draw_curio_candidates("level_clear")
|
||||
_curio_pending["candidates"] = cands.duplicate(true)
|
||||
_curio_pending["aux_detail"] = _last_draw_treasure.duplicate()
|
||||
var payload: Dictionary = (_curio_pending.get("payload", {}) as Dictionary).duplicate(true)
|
||||
payload["candidates"] = cands
|
||||
payload["aux_detail"] = _last_draw_treasure.duplicate()
|
||||
payload["refreshed"] = true # 重发载荷带标——呈现层直落选择面,不再重开胜利总结面(2026-10-06)
|
||||
curio_choice_presented.emit(payload) # 同信号重 present(零自动推进契约不破——停点仍在途)
|
||||
_snapshot_breakpoint()
|
||||
@@ -803,44 +699,10 @@ func _refresh_curio_common(paid: bool) -> bool:
|
||||
if not _node_curio_pending.is_empty():
|
||||
var cands2: Array = _draw_curio_candidates("reward_node")
|
||||
_node_curio_pending["candidates"] = cands2.duplicate(true)
|
||||
_node_curio_pending["aux_detail"] = _last_draw_treasure.duplicate()
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# 替换在途守卫(2026-10-02 修复批——双渠道替换受理的共用前置谓词:停点态非空且 chosen=treasure_item)
|
||||
func _replace_in_transit(pending: Dictionary) -> bool:
|
||||
return not pending.is_empty() and String(pending.get("chosen", "")) == "treasure_item"
|
||||
|
||||
|
||||
# 双渠道道具件掷定共享体(2026-10-02 修复批——_pick_treasure_item/_apply_node_treasure 复制债收口一步):
|
||||
# 解锁道具池空集守卫 + N 件掷定(先消费停点预掷 aux_detail,不足部分当场经 reward_pick 封闭流补掷——
|
||||
# 取数序与拆分前逐位一致)。返回空数组 = 池空集(调用方按渠道名报错)。
|
||||
# 保守不合处:通关渠道允许回退 _last_draw_treasure(allow_detail_fallback)、节点渠道不回退——掷定回退
|
||||
# 语义渠道各异,参数化保留差异;持有容器与兑付收尾仍归调用方渠道。
|
||||
func _draw_pending_treasure(pending: Dictionary, count: int, allow_detail_fallback: bool) -> Array:
|
||||
var pool: Array = _bonus_treasure_pool()
|
||||
if pool.is_empty():
|
||||
return []
|
||||
var picked: Array = []
|
||||
var detail: Array = (pending.get("aux_detail", []) as Array).duplicate()
|
||||
if detail.is_empty() and allow_detail_fallback:
|
||||
detail = _last_draw_treasure.duplicate()
|
||||
for i in maxi(1, count):
|
||||
if i < detail.size():
|
||||
picked.append(String(detail[i]))
|
||||
else:
|
||||
picked.append(_roll_bonus_treasure_item(pool))
|
||||
return picked
|
||||
|
||||
|
||||
# 替换受理分发(单入口——节点停点在途优先按库存句柄,否则按通关停点的携带序号)
|
||||
func resolve_curio_replace(discard: int) -> bool:
|
||||
if not _node_curio_pending.is_empty() and _node_curio_pending.has("incoming"):
|
||||
return _resolve_node_replace(discard)
|
||||
return resolve_treasure_replace(discard)
|
||||
|
||||
|
||||
func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id: String = "") -> void:
|
||||
_node_curio_pending = {}
|
||||
var payload := {
|
||||
@@ -858,8 +720,7 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id:
|
||||
# 一枚**〔先到先得,档位取该 kind 首枚——「三选一=三种不同选项」体验延续,与货架抽签同规则〕;
|
||||
# kind 出现率∝剩余令牌数〔档位配额即分布——无独立权重 knob〕;抽签不摘牌、兑付才摘牌=未选
|
||||
# 自动回池;池耗即枯竭——余库不足 N kind 时出齐剩余,空库 = 零候选合法态)。候选 = 令牌字典
|
||||
# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选;道具件掷定前置
|
||||
# 随池种退役拆除(_last_draw_treasure 恒空集,机械惰性承载)。
|
||||
# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选。
|
||||
# 渠道白名单过滤(迭代 15 卡② T4——渠道轴定位:关底 Boss 击败=通关渠道只出专长(feat 令牌);
|
||||
# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以门票形态出现(T5)。
|
||||
# 过滤式而非物理拆池:deck 物理结构/存档键 bonus_deck 混编余库/消耗语义(选中踢池未选回池)原样,
|
||||
@@ -869,7 +730,6 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id:
|
||||
# 〔独立计数体系〕;已拿上限 cap(案②提前退役):taken≥cap 的身份在候选视图剔除〔牌库本体不动,
|
||||
# 拿了才计、刷出不计——_curio_cap_ok〕。
|
||||
func _draw_curio_candidates(channel: String = "reward_node") -> Array:
|
||||
_last_draw_treasure = []
|
||||
var pool := _curio_deck.duplicate()
|
||||
if channel == "level_clear":
|
||||
pool = pool.filter(func(tok): return String((tok as Dictionary).get("kind", "")) == "relic")
|
||||
@@ -1007,33 +867,6 @@ func _redeem_feat_ticket() -> bool:
|
||||
return true
|
||||
|
||||
|
||||
func _roll_from(rg: RandomNumberGenerator) -> String:
|
||||
var pool: Array = _bonus_treasure_pool()
|
||||
return String(pool[rg.randi_range(0, pool.size() - 1)])
|
||||
|
||||
|
||||
# 三选一道具件随机池(与投放门控同源:aux 活行 ∩ 解锁门控集——空集 = 道具件不入牌库)
|
||||
func _bonus_treasure_pool() -> Array:
|
||||
var out: Array = []
|
||||
var live: Dictionary = db.treasure_live_ids()
|
||||
for iid in live.keys():
|
||||
if _treasure_unlocked.has(String(iid)):
|
||||
out.append(String(iid))
|
||||
return out
|
||||
|
||||
|
||||
func _roll_bonus_treasure_item(pool: Array) -> String:
|
||||
var rg: RandomNumberGenerator = rng.open_stream("reward_pick")
|
||||
return String(pool[rg.randi_range(0, pool.size() - 1)])
|
||||
|
||||
|
||||
func _carry_records() -> Array:
|
||||
var out: Array = []
|
||||
for i in _bonus_treasure_carry.size():
|
||||
out.append({"index": i, "item_id": _bonus_treasure_carry[i]})
|
||||
return out
|
||||
|
||||
|
||||
# Boss 钥石逐杀掉落(迭代 10——渠道改 Boss/精英击杀掉落;首杀/复杀两档读 meta.tres boss_keystone;
|
||||
# 首杀判据 = 存档 boss_first_kill 标记位〔断点未注入 = 恒首杀——模拟/冒烟零扰动;标记仍在收束
|
||||
# apply_adventure_summary 逐通关关次置位〕;末关 Boss 照常入账;明细随摘要 additive 键供结算面归因)
|
||||
@@ -1158,7 +991,6 @@ func _on_level_finished(payload: Dictionary) -> void:
|
||||
var pick_payload: Dictionary = {
|
||||
"level_index": level_index,
|
||||
"candidates": _draw_curio_candidates("level_clear"), # T4 渠道白名单——关底 Boss 击败=通关渠道只出专长
|
||||
"aux_detail": _last_draw_treasure.duplicate(), # 掷定前置(追加批二——道具件具体道具随候选抽定,卡片直显)
|
||||
"heal_gained": heal,
|
||||
"hp_before": hp_before,
|
||||
"hp_after": int(ledger.get("hero_hp", 0)),
|
||||
@@ -1168,7 +1000,6 @@ func _on_level_finished(payload: Dictionary) -> void:
|
||||
"aux_cleared": int(payload.get("aux_cleared", 0)), # additive 扩签(迭代 7 T4——关卡末清空反馈取数)
|
||||
}
|
||||
_curio_pending = {"candidates": (pick_payload["candidates"] as Array).duplicate(true),
|
||||
"aux_detail": _last_draw_treasure.duplicate(),
|
||||
"payload": pick_payload.duplicate(true)}
|
||||
curio_choice_presented.emit(pick_payload)
|
||||
_snapshot_breakpoint() # 选择停点可中断(契约 5——续玩不重抽不静默丢失)
|
||||
@@ -1267,7 +1098,6 @@ func _snapshot_breakpoint() -> void:
|
||||
"refresh_stock": _refresh_stock, # 迭代 20 T4/T7——刷新存量账随断点(F-1 新账)
|
||||
"battery_pending": _battery_pending, # 迭代 20 T4/T7——电池待分配充能随断点
|
||||
"bonus_result": _curio_result.duplicate(true), # 已兑付待推进态(空 = 无——中断续玩不重兑付)
|
||||
"bonus_aux_carry": _bonus_treasure_carry.duplicate(true), # 道具件跨关携带(注入前中断防静默丢失)
|
||||
"keystone_drops": _keystone_drops.duplicate(true), # 逐杀掉落明细(摘要归因续接——迭代 10)
|
||||
}
|
||||
_save_manager.set_breakpoint(bp)
|
||||
|
||||
@@ -679,15 +679,6 @@ func set_attune_charges(charges: Dictionary) -> void:
|
||||
func set_vision_counter(counter: Dictionary) -> void:
|
||||
_vision_counter = counter
|
||||
|
||||
# 奖励节点兑付透传(迭代 7 T5——呈现层三选一 → 关卡域库存;来源归因 reward_node)。
|
||||
# 迭代 11 第①批 T7——同调位持有闸随过渡键退役拆除(aux 卡出池后仅剩防御面;能力件容量闸
|
||||
# 正主 = 营地装配面 Attunement.slots 满位禁选)。
|
||||
func grant_treasure_item(item_id: String, source: String) -> Dictionary:
|
||||
if treasure_inventory == null:
|
||||
treasure_inventory = TreasureItems.new(db)
|
||||
return treasure_inventory.add_item(item_id, source, Vector2i(-1, -1), 0)
|
||||
|
||||
|
||||
# 三选一 GP 袋件入账(迭代 10——钱包收口扩「奖励入账」入口;复用战斗完成同一入账原语,
|
||||
# wallet_changed 载荷带 source 供呈现归因)
|
||||
func grant_curio_gp(amount: int) -> void:
|
||||
|
||||
@@ -78,7 +78,7 @@ func _migrate_v1_v2(data: Dictionary) -> Dictionary:
|
||||
|
||||
|
||||
# v2→v3:断点附随件扩列(迭代 10 奖励重构批——bonus_taken/bonus_pending/bonus_result/
|
||||
# bonus_aux_carry/keystone_drops + bonus.ap 轴)与摘要 additive 键 keystone_drops——旧档缺键
|
||||
# keystone_drops + bonus.ap 轴)与摘要 additive 键 keystone_drops——旧档缺键〔bonus_aux_carry 键随迭代 22 T5 替换子交互拆除退役〕
|
||||
# = 合法空态(读取方 .get 兜底),无结构迁移体;版本号推进仅为 schema_version 对齐
|
||||
func _migrate_v2_v3(data: Dictionary) -> Dictionary:
|
||||
data["version"] = SAVE_FORMAT_VERSION
|
||||
|
||||
@@ -79,14 +79,6 @@ func holding_count() -> int:
|
||||
|
||||
|
||||
# 持有件记录(迭代 10 追加批——奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
|
||||
func holding_records() -> Array:
|
||||
var out: Array = []
|
||||
for rec in _items:
|
||||
if not bool(rec.get("used", false)):
|
||||
out.append({"handle": int(rec.get("handle", 0)), "item_id": String(rec.get("item_id", ""))})
|
||||
return out
|
||||
|
||||
|
||||
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
|
||||
func discard_item(handle: int) -> bool:
|
||||
for i in _items.size():
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# 装配纪律(ADR-0001,仿 treasure_interact_controller 先例):信号连接不在此发生——curio_overlay
|
||||
# 四信号与 adventure 两信号由组合根接至本类 on_* 入口;模态栈/演播后排窗/呈现刷新/终局面/
|
||||
# 推进失败 fatal 经组合根回调注入;adventure/runner 随局更替——现值回调注入;兑付写调用唯一
|
||||
# 经 adventure.pick_curio/apply_node_curio/resolve_curio_replace(core 侧单口,本类零直写)。
|
||||
# 经 adventure.pick_curio/apply_node_curio(core 侧单口,本类零直写;替换子交互随迭代 22 T5 拆除——装配不可变契约 1,冒险中零更换途径)。
|
||||
extends RefCounted
|
||||
|
||||
const Narrator := preload("res://presentation/battle_narrator.gd")
|
||||
@@ -29,8 +29,6 @@ var _gp_summary: Callable # () → 战斗总结 GP 行文案(组
|
||||
|
||||
var _pending_curio := {} # 三选一停点载荷暂存(迭代 10——战斗总结结算确认后开三选一面;续玩停点复用)
|
||||
var _curio_context := "level_clear" # 三选一面上下文(level_clear=通关停点→推进下一关 / reward_node=奖励节点→回图选路)
|
||||
var _last_replace_items: Array = [] # 替换子态续玩重建缓存(treasure_replace_presented 载荷)
|
||||
var _last_replace_incoming := {} # 同上——incoming 记录
|
||||
|
||||
|
||||
func _init(db_ref, meta_get: Callable, curio_overlay_ref: CurioPickOverlay, end_overlay_ref: BattleEndOverlay,
|
||||
@@ -112,8 +110,7 @@ func on_presented(payload: Dictionary) -> void:
|
||||
toast.show_toast("上关剩余 %d 件秘宝已随关卡结束清空" % int(payload.get("aux_cleared", 0)), "info") # 迭代 7 T4 清空反馈
|
||||
_pending_curio = payload.duplicate(true)
|
||||
_curio_context = "level_clear"
|
||||
if bool(payload.get("replace_pending", false)) or bool(payload.get("refreshed", false)):
|
||||
# 续玩:替换子交互在途中断——重建三选一面后直落替换子态(候选不重抽)
|
||||
if bool(payload.get("refreshed", false)):
|
||||
# 刷新重发(2026-10-06):refreshed 标直落选择面——不再重开胜利总结面
|
||||
show_pick()
|
||||
return
|
||||
@@ -124,9 +121,8 @@ func on_presented(payload: Dictionary) -> void:
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复;替换在途由 treasure_replace_presented 续发直落子态)
|
||||
# 奖励节点三选一(迭代 10 追加批——与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面,
|
||||
# 道具件满位替换子交互同构)。原「秘宝投放渠道」三选一(迭代 7 过渡形态)随批退役。
|
||||
# 奖励节点三选一(迭代 10 追加批——与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
|
||||
# 原「秘宝投放渠道」三选一(迭代 7 过渡形态)随批退役;替换子交互随迭代 22 T5 拆除。
|
||||
func present_node_curio() -> void:
|
||||
var adventure = _adventure_get.call()
|
||||
_curio_context = "reward_node"
|
||||
@@ -141,16 +137,16 @@ func present_node_curio() -> void:
|
||||
runner.continue_after_node()
|
||||
return
|
||||
_open_modal.call(curio_overlay)
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands, node_draw.get("aux_detail", []) as Array))
|
||||
adventure.present_node_curio(node_cands, node_draw.get("aux_detail", []) as Array)
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands))
|
||||
adventure.present_node_curio(node_cands)
|
||||
_feed_refresh_display(adventure) # 迭代 11 第①批 T5b + 2026-10-06 对齐商店——刷新面投喂单点
|
||||
|
||||
|
||||
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复;替换在途由 treasure_replace_presented 续发直落子态)
|
||||
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复)
|
||||
func show_pick() -> void:
|
||||
var payload: Dictionary = _pending_curio
|
||||
_open_modal.call(curio_overlay)
|
||||
curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array, payload.get("aux_detail", []) as Array))
|
||||
curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array))
|
||||
var adventure = _adventure_get.call()
|
||||
if adventure != null:
|
||||
_feed_refresh_display(adventure) # 迭代 11 第①批 T5b + 2026-10-06 对齐商店——刷新面投喂单点
|
||||
@@ -165,11 +161,10 @@ func _feed_refresh_display(adventure) -> void:
|
||||
curio_overlay.set_refresh_display(adventure.attune_refresh_left(), cost, gp >= cost)
|
||||
|
||||
|
||||
func _curio_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
|
||||
func _curio_card_texts(candidates: Array) -> Array:
|
||||
# TP0 令牌池——候选 = {"kind","tier"} 令牌字典;卡片 id = "kind|tier|feat_id" 复合键(同 kind
|
||||
# 大小两档/同档多 relic 可同屏,kind 单键不构成唯一身份),确认回程拆键(on_pick_confirmed)
|
||||
var out: Array = []
|
||||
var treasure_i := 0
|
||||
for tok in candidates:
|
||||
var kind := String((tok as Dictionary).get("kind", ""))
|
||||
var tier := String((tok as Dictionary).get("tier", ""))
|
||||
@@ -181,10 +176,6 @@ func _curio_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
|
||||
选定即开一次奇物三选一(T5 门票——拿到即开不进持有集)"
|
||||
elif kind == "relic":
|
||||
text = Narrator.feat_card_text(db.feat_row(rid))
|
||||
elif kind == "treasure_item":
|
||||
var iid := String(treasure_detail[treasure_i]) if treasure_i < treasure_detail.size() else ""
|
||||
treasure_i += 1
|
||||
text = Narrator.bonus_card_text(kind, tier, int(db.adventure.curio_value(kind, tier)), _treasure_card_line(iid))
|
||||
else:
|
||||
text = Narrator.bonus_card_text(kind, tier, int(db.adventure.curio_value(kind, tier)))
|
||||
out.append({"id": cid, "text": text})
|
||||
@@ -194,17 +185,6 @@ func _curio_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
|
||||
# 道具件卡片行(掷定前置——具体道具名 + 效果摘要;空 iid = 无预掷回退态显示通用文案)。
|
||||
# 内容数据驱动扩展批 T4 文案表值化:效果摘要=数据行 desc 字段直读(零 kind 硬编码映射——
|
||||
# 新件写行即有名有姓)
|
||||
func _treasure_card_line(iid: String) -> String:
|
||||
if iid == "":
|
||||
return ""
|
||||
var row: Dictionary = db.treasure_item_row(iid)
|
||||
if row.is_empty():
|
||||
return ""
|
||||
var desc := String(row.get("desc", ""))
|
||||
if desc != "":
|
||||
return "%s · %s" % [String(row.get("display_name", iid)), desc]
|
||||
return String(row.get("display_name", iid))
|
||||
|
||||
|
||||
# 三选一刷新(迭代 11 第①批 T5b X/Y 双源 + 2026-10-06 用户令对齐商店 MC-1——免费位用尽转 GP 付费档:
|
||||
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;通关停点经重发 curio_choice_presented 直落
|
||||
@@ -227,7 +207,7 @@ func on_refresh() -> void:
|
||||
return
|
||||
if _curio_context == "reward_node":
|
||||
var st: Dictionary = adventure.node_curio_pending_state()
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array, st.get("aux_detail", []) as Array))
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array))
|
||||
_feed_refresh_display(adventure)
|
||||
|
||||
|
||||
@@ -251,30 +231,6 @@ func on_pick_confirmed(item_id: String) -> void:
|
||||
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st2.get("candidates", []) as Array))
|
||||
|
||||
|
||||
func on_treasure_replace_presented(items: Array, incoming: Dictionary) -> void:
|
||||
_last_replace_items = items.duplicate(true)
|
||||
_last_replace_incoming = incoming.duplicate(true)
|
||||
if not curio_overlay.visible:
|
||||
_open_modal.call(curio_overlay) # 续玩替换在途直落——三选一面未开时先入栈(模态栈纪律)
|
||||
_open_replace(items, incoming)
|
||||
|
||||
|
||||
func _open_replace(items: Array, incoming: Dictionary) -> void:
|
||||
var named: Array = []
|
||||
for it in items:
|
||||
var rec: Dictionary = it.duplicate()
|
||||
rec["display_name"] = String(item_name_lookup().get(String(rec.get("item_id", "")), String(rec.get("item_id", ""))))
|
||||
named.append(rec)
|
||||
curio_overlay.open_replace(named, String(item_name_lookup().get(String(incoming.get("item_id", "")), String(incoming.get("item_id", "")))),
|
||||
named.size(), Attunement.slots(db, _save_get.call())) # 迭代 11 第①批 T7——呈现读数源切谓词表求值(过渡键退役)
|
||||
|
||||
|
||||
func on_replace_chosen(discard: int) -> void:
|
||||
var adventure = _adventure_get.call()
|
||||
if adventure == null or not adventure.resolve_curio_replace(discard):
|
||||
push_error("[bonus_interact] 同调位替换受理被拒——见输出日志")
|
||||
|
||||
|
||||
# 兑付反馈(curio_resolved 到达——结果行上屏,唯一出口 = 继续)
|
||||
func on_resolved(result: Dictionary) -> void:
|
||||
_pending_curio = {}
|
||||
|
||||
@@ -294,7 +294,6 @@ func _build_ui() -> void:
|
||||
_open_modal, _open_modal_after_playback, _close_modal, _refresh_presentations,
|
||||
_fatal_screen, func() -> String: return _gp_summary_text({"gp_dropped": _last_gain_amount}))
|
||||
curio_overlay.pick_confirmed.connect(_bonus.on_pick_confirmed)
|
||||
curio_overlay.replace_chosen.connect(_bonus.on_replace_chosen)
|
||||
curio_overlay.continue_pressed.connect(_bonus.on_continue)
|
||||
curio_overlay.refresh_requested.connect(_bonus.on_refresh)
|
||||
move_child(curio_overlay, _end_overlay.get_index())
|
||||
@@ -488,7 +487,6 @@ func _wire_adventure() -> void:
|
||||
# 迭代 10 奖励重构批——big_reward_paid 退役,三选一信号族接线
|
||||
adventure.curio_choice_presented.connect(_bonus.on_presented)
|
||||
adventure.curio_resolved.connect(_bonus.on_resolved)
|
||||
adventure.treasure_replace_presented.connect(_bonus.on_treasure_replace_presented)
|
||||
adventure.adventure_finished.connect(_on_adventure_finished)
|
||||
adventure.level_started.connect(_on_adventure_level_started)
|
||||
adventure.set_save_manager(save_manager) # 断点承载注入(迭代 8 卡①——停点快照写盘;调试/模拟不经过此路径)
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-10-01(迭代 10 奖励重构批——level-clear.md v2 三选一选择面承载)
|
||||
# 提升三选一覆盖层(模态):非末关通关的选择停点呈现——部分回血反馈 + 三卡点选 + 确认兑付 +
|
||||
# 同调位替换子交互。高内聚边界:文案拼装归 Narrator(组合根喂入),选择意图经信号外发;
|
||||
# 视图零核心引用。交互范式 = node_overlay choice_box 按钮族 + 选路两段式(点选→确认)复用,零新原语。
|
||||
# 状态机:pick(三卡点选→确认)→ [replace(满位 N+1 选 N)] → result(兑付反馈→继续)。
|
||||
# 提升三选一覆盖层(模态):非末关通关的选择停点呈现——部分回血反馈 + 三卡点选 + 确认兑付。
|
||||
# 高内聚边界:文案拼装归 Narrator(组合根喂入),选择意图经信号外发;视图零核心引用。
|
||||
# 交互范式 = node_overlay choice_box 按钮族 + 选路两段式(点选→确认)复用,零新原语。
|
||||
# 状态机:pick(三卡点选→确认)→ result(兑付反馈→继续)。〔replace 满位替换子态随迭代 22 T5
|
||||
# 拆除——装配不可变契约 1(冒险中零更换途径),core 写口与组合根接线同批摘除。〕
|
||||
class_name CurioPickOverlay
|
||||
extends Control
|
||||
|
||||
signal pick_confirmed(item_id: String)
|
||||
signal replace_chosen(discard_index: int) # -1 = 放弃新件不获取
|
||||
signal continue_pressed
|
||||
signal refresh_requested # 三选一刷新意图(迭代 11 第①批 T5b——P1 甲 X/Y 双源;重抽归组合根)
|
||||
|
||||
var _label: Label
|
||||
var _choice_box: HBoxContainer # 三卡区(pick 态——M7 bcard 竖卡)
|
||||
var _replace_box: VBoxContainer # 替换子面板(replace 态)
|
||||
var _confirm_btn: Button # 确认钮(点选后点亮——两段式第二拍)
|
||||
var _continue_btn: Button # 继续钮(兑付反馈态唯一出口)
|
||||
var _refresh_btn: Button # 刷新钮(迭代 11 第①批 T5b——余量>0 才显示;M7 升顶条图标钮)
|
||||
@@ -33,7 +32,7 @@ func _init() -> void:
|
||||
# T7(UI 设计对齐批 2026-10-06):全屏页形态(demo ⑥ #v-bonus——与商店 T6 同壳 page_shell,
|
||||
# 根底暗档)——56px 通栏顶条(标题+刷新图标钮/角标)+ 居中入账信息行(GP 汇总行承载)+
|
||||
# bcard 三竖卡(D1 232×352)+ 底部确认主钮与免费选择注记。差异白名单=免费选择/✓章/无价签。
|
||||
# 状态机不变:pick→[replace]→result。M7 同构裁决遗产保留。
|
||||
# 状态机不变:pick→result。M7 同构裁决遗产保留。
|
||||
var page: Dictionary = UiKit.page_shell(self, false)
|
||||
var top: HBoxContainer = page.top_row
|
||||
top.add_child(UiKit.label("战利品三选一",UiKit.FS_TITLE, true, UiKit.WARM))
|
||||
@@ -79,11 +78,6 @@ func _init() -> void:
|
||||
foot.add_child(_continue_btn)
|
||||
_foot_hint = UiKit.label("免费选择 · 刷新不消耗 AP",UiKit.FS_HINT, false, UiKit.WARM_FAINT)
|
||||
foot.add_child(_foot_hint)
|
||||
_replace_box = VBoxContainer.new()
|
||||
_replace_box.add_theme_constant_override("separation", 8)
|
||||
_replace_box.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
_replace_box.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
box.add_child(_replace_box)
|
||||
visible = false
|
||||
|
||||
|
||||
@@ -115,9 +109,8 @@ func open_pick(header: String, cards: Array) -> void:
|
||||
_bcards.append(card)
|
||||
_choice_box.visible = true
|
||||
_confirm_btn.visible = false
|
||||
_replace_box.visible = false
|
||||
_continue_btn.visible = false
|
||||
_foot_hint.visible = true # T7 底注=pick 态专属(replace/result 态让位)
|
||||
_foot_hint.visible = true # T7 底注=pick 态专属(result 态让位)
|
||||
_refresh_btn.visible = false # 刷新面随 set_refresh_display 投喂后现形(open 先归零防残留;店内门票面不投喂=无刷新)
|
||||
_refresh_l.text = ""
|
||||
visible = true
|
||||
@@ -183,40 +176,15 @@ func show_result(text: String) -> void:
|
||||
_label.text = _header + "\n" + text
|
||||
_choice_box.visible = false
|
||||
_confirm_btn.visible = false
|
||||
_replace_box.visible = false
|
||||
_continue_btn.visible = true
|
||||
_refresh_btn.visible = false # 刷新=选择态专属功能(结果态停点已兑付——残钮零意图可点)
|
||||
_foot_hint.visible = false
|
||||
visible = true
|
||||
|
||||
|
||||
# 同调位替换子态(treasure_replace_presented 到达):items = [{index, item_id, display_name}](组合根
|
||||
# 喂显示名)。两路出口:丢旧收新(逐件钮)/ 放弃新件——终态恒 ≤ 同调位由核心受理面保证。
|
||||
func open_replace(items: Array, incoming_name: String, holding: int, slots: int) -> void:
|
||||
_label.text = "同调位已满(%d/%d)——收下「%s」需丢弃一件已有道具,或放弃新件" % [holding, slots, incoming_name]
|
||||
for c in _replace_box.get_children():
|
||||
c.queue_free()
|
||||
for it in items:
|
||||
var tag: int = int(it.get("index", it.get("handle", -1))) # 通关渠道=携带序号 / 节点渠道=库存句柄(核心分发面裁决)
|
||||
var btn := UiKit.button("丢弃「%s」换新" % String(it.get("display_name", it.get("item_id", ""))))
|
||||
btn.pressed.connect(_emit_replace_chosen.bind(tag))
|
||||
_replace_box.add_child(btn)
|
||||
var decline := UiKit.button("放弃新件(不获取)")
|
||||
decline.pressed.connect(_emit_replace_chosen.bind(-1))
|
||||
_replace_box.add_child(decline)
|
||||
_choice_box.visible = false
|
||||
_confirm_btn.visible = false
|
||||
_replace_box.visible = true
|
||||
_continue_btn.visible = false
|
||||
_refresh_btn.visible = false # 刷新=选择态专属功能(替换子态不换候选)
|
||||
_foot_hint.visible = false
|
||||
visible = true
|
||||
|
||||
|
||||
# 收屏(组合根在 continue_pressed 处理毕调——推进下一关同拍)
|
||||
func close_pick() -> void:
|
||||
_choice_box.visible = false
|
||||
_replace_box.visible = false
|
||||
_continue_btn.visible = false
|
||||
_confirm_btn.visible = false
|
||||
_refresh_btn.visible = false
|
||||
@@ -228,10 +196,6 @@ func _emit_continue_pressed() -> void:
|
||||
continue_pressed.emit()
|
||||
|
||||
|
||||
func _emit_replace_chosen(tag: int) -> void:
|
||||
replace_chosen.emit(tag)
|
||||
|
||||
|
||||
# M7:id → 手绘图标键(子串映射容错 id 词表演进;缺位=BCard 空图标槽不炸)
|
||||
func _icon_for_id(idl: String) -> String:
|
||||
if idl.contains("heal"):
|
||||
|
||||
@@ -150,10 +150,9 @@ static func run_adventure(db, seed_val: int, atk_pct: int, hp_pct: int, collect_
|
||||
adr.node_entered.connect(_on_adventure_node_entered.bind(adr, st, logger))
|
||||
adr.wallet_changed.connect(_on_adventure_wallet.bind(st, logger))
|
||||
# 三选一停点驱动(迭代 10 奖励重构批——big_reward_paid 退役:确定性策略候选取首件、
|
||||
# 道具件满位替换放弃新件、兑付后推进下一关——零随机流自开,同种子逐位可复放)
|
||||
# 兑付后推进下一关——零随机流自开,同种子逐位可复放;满位替换策略随迭代 22 T5 拆除)
|
||||
adr.curio_choice_presented.connect(_on_adventure_bonus.bind(st, adr))
|
||||
adr.curio_resolved.connect(_on_adventure_curio_resolved.bind(st, adr))
|
||||
adr.treasure_replace_presented.connect(_on_adventure_treasure_replace.bind(st, adr))
|
||||
adr.adventure_finished.connect(_on_adventure_finished.bind(st, logger))
|
||||
adr.set_hero_override(atk_pct, hp_pct)
|
||||
# 同调分层注入(迭代 11 第①批 T8——缺省 {} 与既有调用方逐位一致零行为变更)
|
||||
@@ -226,7 +225,7 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
|
||||
if ncands.is_empty():
|
||||
r.continue_after_node() # 牌库空合法态直通(池耗即枯竭——防御保留)
|
||||
else:
|
||||
adr.present_node_curio(ncands, ndraw.get("aux_detail", []) as Array)
|
||||
adr.present_node_curio(ncands)
|
||||
var npick: Dictionary = _pick_token(ncands, String(st.get("feat_mode", "off")))
|
||||
if adr.apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", ""))):
|
||||
# T5 门票拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
|
||||
@@ -236,8 +235,6 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
|
||||
if not fcands.is_empty():
|
||||
var fpick: Dictionary = _pick_token(fcands, String(st.get("feat_mode", "off")))
|
||||
adr.apply_node_curio(String(fpick.get("kind", "")), String(fpick.get("tier", "")), String(fpick.get("feat_id", "")))
|
||||
if not adr._node_curio_pending.is_empty() and adr._node_curio_pending.has("incoming"):
|
||||
adr.resolve_curio_replace(-1) # 满位替换:放弃新件
|
||||
else:
|
||||
st.result = "advance_failed"
|
||||
elif t == "rest":
|
||||
@@ -310,13 +307,6 @@ static func _pick_token(candidates: Array, feat_mode: String = "off") -> Diction
|
||||
return best
|
||||
|
||||
|
||||
# 同调位满位替换子交互(确定性策略:放弃新件不获取——分发面按停点上下文裁决节点/通关渠道)
|
||||
static func _on_adventure_treasure_replace(items: Array, incoming: Dictionary, st: Dictionary, adr) -> void:
|
||||
st.reward_events.append({"treasure_replace": incoming.duplicate(true)})
|
||||
if not adr.resolve_curio_replace(-1):
|
||||
st.result = "advance_failed"
|
||||
|
||||
|
||||
# 三选一兑付完成 → 上下文分派(通关停点=推进下一关 / 奖励节点=回图选路)
|
||||
static func _on_adventure_curio_resolved(result: Dictionary, st: Dictionary, adr) -> void:
|
||||
st.reward_events.append({"curio_resolved": result.duplicate(true)})
|
||||
|
||||
@@ -649,7 +649,7 @@ func _smoke_adventure() -> void:
|
||||
break
|
||||
check(not has_attack13,
|
||||
"奖励节点共享池:令牌耗尽同样不出候选(同库同池——%s)" % str(ncands13))
|
||||
a13.present_node_curio(ncands13, ndraw.get("aux_detail", []) as Array)
|
||||
a13.present_node_curio(ncands13)
|
||||
a13.present_node_curio([{"kind": "gp", "tier": "common"}])
|
||||
var gp13 := int(a13.ledger.get("gp", 0))
|
||||
check(a13.apply_node_curio("gp", "common") and int(a13.ledger.get("gp", 0)) == gp13 + int(_db.adventure.curio_value("gp", "common")),
|
||||
@@ -750,7 +750,7 @@ func _smoke_feats() -> void:
|
||||
var stock_before := {}
|
||||
for kk in ["attack", "gp", "heal"]:
|
||||
stock_before[kk] = a_t.pool_stock(kk)
|
||||
a_t._node_curio_pending = {"candidates": [{"kind": "feat_ticket", "tier": "excellent"}, {"kind": "attack", "tier": "common"}], "aux_detail": []}
|
||||
a_t._node_curio_pending = {"candidates": [{"kind": "feat_ticket", "tier": "excellent"}, {"kind": "attack", "tier": "common"}]}
|
||||
check(a_t.apply_node_curio("feat_ticket", "excellent"), "A5 门票兑付受理(节点渠道——拿到即开)")
|
||||
var tkt_cands: Array = (a_t.node_curio_pending_state().get("candidates", []) as Array)
|
||||
var tkt_all_relic := tkt_cands.size() > 0
|
||||
@@ -1040,11 +1040,9 @@ func _adv_drive_to_shop(a, st: Dictionary, autoplay) -> bool:
|
||||
var ndraw: Dictionary = a.node_curio_candidates()
|
||||
var ncands: Array = ndraw.get("candidates", []) as Array
|
||||
if not ncands.is_empty():
|
||||
a.present_node_curio(ncands, ndraw.get("aux_detail", []) as Array)
|
||||
a.present_node_curio(ncands)
|
||||
var npick: Dictionary = autoplay._pick_token(ncands)
|
||||
if a.apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", ""))):
|
||||
if not a._node_curio_pending.is_empty() and a._node_curio_pending.has("incoming"):
|
||||
a.resolve_curio_replace(-1)
|
||||
a.apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", "")))
|
||||
r.continue_after_node()
|
||||
elif t == "rest":
|
||||
r.continue_after_node()
|
||||
|
||||
@@ -34,7 +34,7 @@ func _ready() -> void:
|
||||
main.save_manager.set_breakpoint({
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 20, "gp": 24, "keystone": 4,
|
||||
"level_index": 1, "bonus": {}, "shop_purchased": {}, "relics": [],
|
||||
"bonus_aux_carry": [], "keystone_drops": [], "bonus_taken": {},
|
||||
"keystone_drops": [], "bonus_taken": {},
|
||||
})
|
||||
main._refresh_resume_button()
|
||||
await get_tree().process_frame
|
||||
|
||||
@@ -262,14 +262,12 @@ func _ready() -> void:
|
||||
continue
|
||||
if main.curio_overlay.visible:
|
||||
# 迭代 10 三选一停点(CurioPickOverlay——奖励节点/通关兑付共用面;T17 硬锁批适配——
|
||||
# 本探针此前未适配该层,走到即卡死)。三态分派:替换子态(满位——放弃新件)/
|
||||
# result 态(兑付反馈——点继续推进)/ 待选态(点首件+确认)。真实信号路径全线。
|
||||
# T1 D1——三选一停点输入锁定断言(停点态断言面:替换子/待选/兑付三态共用此层)。
|
||||
# 本探针此前未适配该层,走到即卡死)。两态分派:result 态(兑付反馈——点继续推进)/
|
||||
# 待选态(点首件+确认)。真实信号路径全线。〔替换子态随迭代 22 T5 拆除——装配不可变契约。〕
|
||||
# T1 D1——三选一停点输入锁定断言(停点态断言面:待选/兑付两态共用此层)。
|
||||
if not main._input_locked():
|
||||
_problems.append("三选一停点打开时输入未锁定")
|
||||
if main.curio_overlay._replace_box.visible:
|
||||
main.curio_overlay.replace_chosen.emit(-1)
|
||||
elif main.curio_overlay._continue_btn.visible:
|
||||
if main.curio_overlay._continue_btn.visible:
|
||||
main.curio_overlay.press_continue()
|
||||
elif main.adventure != null and not main.adventure._curio_pending.is_empty():
|
||||
main.curio_overlay.press_card(0)
|
||||
@@ -409,9 +407,7 @@ func _ready() -> void:
|
||||
else:
|
||||
break
|
||||
elif main.curio_overlay.visible:
|
||||
if main.curio_overlay._replace_box.visible:
|
||||
main.curio_overlay.replace_chosen.emit(-1)
|
||||
elif main.curio_overlay._continue_btn.visible:
|
||||
if main.curio_overlay._continue_btn.visible:
|
||||
main.curio_overlay.press_continue()
|
||||
elif main.adventure != null and not main.adventure._curio_pending.is_empty():
|
||||
main.curio_overlay.press_card(0)
|
||||
|
||||
@@ -198,7 +198,7 @@ func _resume_bp_fixture() -> Dictionary:
|
||||
return {
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 20, "gp": 24, "keystone": 0,
|
||||
"level_index": 1, "bonus": {}, "shop_purchased": {}, "relics": [],
|
||||
"bonus_aux_carry": [], "keystone_drops": [], "bonus_taken": {},
|
||||
"keystone_drops": [], "bonus_taken": {},
|
||||
"bonus_pending": {"candidates": [{"kind": "heal", "tier": "common"}], "payload": {"gp_dropped": 0}},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
extends SceneTree
|
||||
# 三选一覆盖层 UI 直验套件(迭代 7 T5-hotfix 转正;迭代 10 追加批随面迁移改写)——
|
||||
# 原守 node_overlay.show_reward_items;奖励节点三选一与通关三选一合并共用 CurioPickOverlay 后,
|
||||
# 本套件改守共用面的「功能存在 ≠ 功能可达」盲区:选择外发/强制确认两段式/替换子面板两路/结果态。
|
||||
# 本套件改守共用面的「功能存在 ≠ 功能可达」盲区:选择外发/强制确认两段式/结果态。
|
||||
# 〔替换子面板两路段随迭代 22 秘宝批 T5 拆除——装配不可变契约 1,overlay replace 态退役。〕
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/reward_overlay_test.gd(expect 0)
|
||||
# T6 收口批:断言走 test_kit 计数(Kit.check)+ 失败即停早退协议保留(顺序状态机——坏态下后继断言无效)。
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
@@ -21,10 +22,8 @@ func _run() -> void:
|
||||
var ov = load("res://presentation/views/curio_pick_overlay.gd").new()
|
||||
root.add_child(ov)
|
||||
var picked := []
|
||||
var replaced := []
|
||||
var continued := []
|
||||
ov.pick_confirmed.connect(func(id: String): picked.append(id))
|
||||
ov.replace_chosen.connect(func(tag: int): replaced.append(tag))
|
||||
ov.continue_pressed.connect(func(): continued.append(true))
|
||||
# ① 选择态:三卡呈现、确认钮隐藏(两段式——点选前不给确认)
|
||||
ov.open_pick("第 1 层通过!", [
|
||||
@@ -68,29 +67,11 @@ func _run() -> void:
|
||||
if not _req("点选确认未外发 pick_confirmed(gp)(%s)" % str(picked),
|
||||
ov._confirm_btn.visible and picked.size() == 1 and String(picked[0]) == "gp"):
|
||||
return
|
||||
# ③ 替换子面板:丢旧收新与放弃新件两路外发(通关渠道=携带序号 / 节点渠道=库存句柄——tag 直通)
|
||||
ov.open_replace([{"index": 0, "item_id": "incense_stick", "display_name": "线香"}], "天眼", 1, 1)
|
||||
var replace_btns := 0
|
||||
for c in ov._replace_box.get_children():
|
||||
if c is Button:
|
||||
replace_btns += 1
|
||||
if not _req("替换面板应 2 钮(N+1 选 N+放弃)且三卡隐藏(btns=%d)" % replace_btns,
|
||||
replace_btns == 2 and not ov._choice_box.visible):
|
||||
return
|
||||
var replaced_first := false
|
||||
for c in ov._replace_box.get_children():
|
||||
if c is Button:
|
||||
c.pressed.emit()
|
||||
replaced_first = true
|
||||
break
|
||||
if not _req("丢旧收新未外发 replace_chosen(0)(%s)" % str(replaced),
|
||||
replaced_first and replaced.size() == 1 and int(replaced[0]) == 0):
|
||||
return
|
||||
# ④ 结果态:继续钮点亮、唯一出口(刷新钮随选择态收——停点已兑付残钮零意图可点)
|
||||
# ③ 结果态:继续钮点亮、唯一出口(刷新钮随选择态收——停点已兑付残钮零意图可点)
|
||||
ov.set_refresh_display(2, 20, false)
|
||||
ov.show_result("已选择:攻击力提升 · 攻击 +1")
|
||||
if not _req("结果态应点亮继续且收替换面板与刷新钮",
|
||||
ov._continue_btn.visible and not ov._replace_box.visible and not ov._refresh_btn.visible):
|
||||
if not _req("结果态应点亮继续且收三卡与刷新钮",
|
||||
ov._continue_btn.visible and not ov._choice_box.visible and not ov._refresh_btn.visible):
|
||||
return
|
||||
ov.press_continue()
|
||||
if not _req("继续未外发 continue_pressed", continued.size() == 1):
|
||||
@@ -98,4 +79,4 @@ func _run() -> void:
|
||||
ov.close_pick()
|
||||
if not _req("close_pick 应收屏", not ov.visible):
|
||||
return
|
||||
quit(Kit.finish("reward_verify", "PASS — 三卡两段式/确认外发/替换两路/结果态/收屏 全过"))
|
||||
quit(Kit.finish("reward_verify", "PASS — 三卡两段式/确认外发/结果态/收屏 全过"))
|
||||
Reference in new issue
Block a user