diff --git a/design/gdd/06-待办清单.md b/design/gdd/06-待办清单.md index 77a947e4..90900f5e 100644 --- a/design/gdd/06-待办清单.md +++ b/design/gdd/06-待办清单.md @@ -186,6 +186,8 @@ **B 单七条反哺落文**(实现报告 §三,用户逐点裁「落文」全落):①②③秘宝.md v3.0(目录三表实态列/试炼镐自动兑付换代/代价轴转化获取行含战斗级重置与兑付时序/解锁条件表实录〔钥石 10/25+四条件件三计数键〕/全部「实装批落地前」句换代);④元进度.md v1.17(秘宝匣行实装实录+柜台纪律排期态消解+P1 定价换代);⑤camp.md v1.3(秘宝匣柜台呈现细则节+暂停层 12 行只读概览+退役/封存件口径消解);⑥存档序列化.md v1.6(vision_used 域行+vision_counter_used 断点随件+bonus_aux_carry 退役+v6 扩列不升版注);⑦行动经济.md v0.12(finish_round_stop 转存率参数化注记)。连带:07㉑ 观测点随⑤换代;术语表 v1.2 补「自动兑付/真视使用计数」两词条。 +**⑤呈现集成补丁(2026-10-12 修复批)**:追认批遗留一缺陷——charges=0 件被 `_reseed_attune_charges` 注入循环跳过〔不入骑库存〕,而 C 区槽投影遍历骑库存→试炼镐在 C 区不可见、悬停浮窗进度无处承载。修复=C 区投影末段追加**自动兑付件展示行**(合成 handle=-1 非库存句柄+note 进度,槽点击走说明 toast 不进两步交互);孤儿函数头注释清一并修。重闸全绿:冒烟 898/0+单测 22 套〔新增④b 展示行断言两条〕+probe --full+render_gate golden 0.00%+doc 三闸对基线零新增(死链 3/拔旗 1/gdd_lint 三文件表格占比 FAIL 均实证存量——79aac07 版本对比表格行零变化)+熵增闸 PASS。 + #### 迭代 20 专长实装批 GDD 反哺点(五条清单——2026-10-11 随批登记,逐点待用户裁决后另批落文) diff --git a/design/qa/iter22-实现报告-2026-10-12.md b/design/qa/iter22-实现报告-2026-10-12.md index 29885a99..200cb103 100644 --- a/design/qa/iter22-实现报告-2026-10-12.md +++ b/design/qa/iter22-实现报告-2026-10-12.md @@ -22,7 +22,7 @@ 2. **回补点 2 消解**【追认】:迭代 20 已删 venom_edge/flag_burst,本批实删 12 行(非 14)。 3. **回补点 3 执行形态**【追认】:treasure_inventory **部分拆除+保留注记**——拆除=替换子交互死通道全清(装配不可变实现面保证);保留=库存类本体(新目录主动件线香/天眼的骑库存承载在役:handle 注入+use_treasure 受理+断点 aux_items 快照)。卡片「拆除或保留注记」双路线并写的第三形态,理由=T3 复用现役机制零新建。 4. **试炼镐两级积点单级化**【追认+积点账生命周期裁战斗级(每场战斗清零——设计留白处 2026-10-12 用户裁)】:设计「每开 4 安全格积 1 点、满 2 点换 AP+1」→ 实现=ap_charge_safe charge_n=8 滑耗(行为等价:每 8 格兑 1 AP,免上限滑动消耗)——代拍板白名单「订阅行等价实现细节」;desc 保留设计原文两级表述(反哺批⑤ 后随自动兑付换代)。 -5. **试炼镐受理口与入口**【**推翻·改自动兑付**(2026-10-12 用户裁,反哺落文批执行):满 charge_n=8 自动当轮 grant_ap、无玩家发起入口——HUD 兑换钮整链(convert_pressed 信号/转发/接线/set_convert_state)退役、use_attune_convert 受理口消解、行内 counter_spend 受理闸删除、desc 换代「每开 8 个安全格,当轮 AP +1(自动)」;兑付时序=揭示先于本格扣费(cell_revealed 批先于 spend),AP 归零恰逢满点时先兑再扣、轮次不因本格结束;进度显示挂 C 区秘宝槽悬停浮窗(attune_convert_state 读数口改造为进度投影);单测④段重写自动兑付断言,冒烟 898/0+单测 22 套+probe click/full 反哺批重跑全绿】:原实现绕库存直呼 use_attune_convert(charges=0 零骑库存)+HUD 行动条兑换钮——卡片未列明入口形态,本批落 HUD 最小入口。 +5. **试炼镐受理口与入口**【**推翻·改自动兑付**(2026-10-12 用户裁,反哺落文批执行):满 charge_n=8 自动当轮 grant_ap、无玩家发起入口——HUD 兑换钮整链(convert_pressed 信号/转发/接线/set_convert_state)退役、use_attune_convert 受理口消解、行内 counter_spend 受理闸删除、desc 换代「每开 8 个安全格,当轮 AP +1(自动)」;兑付时序=揭示先于本格扣费(cell_revealed 批先于 spend),AP 归零恰逢满点时先兑再扣、轮次不因本格结束;进度显示挂 C 区秘宝槽悬停浮窗(attune_convert_state 读数口改造为进度投影);单测④段重写自动兑付断言,冒烟 898/0+单测 22 套+probe click/full 反哺批重跑全绿。**呈现集成补丁(同日修复批)**:charges=0 件不入骑库存〔注入循环跳过〕致 C 区原无试炼镐槽、浮窗进度无处承载——C 区投影末段补自动兑付件展示行(合成 handle=-1+note 进度+点击说明 toast);单测新增④b 展示行断言两条;重闸全绿(含 render_gate golden 0.00%+doc 三闸存量对比+熵增闸)】:原实现绕库存直呼 use_attune_convert(charges=0 零骑库存)+HUD 行动条兑换钮——卡片未列明入口形态,本批落 HUD 最小入口。 6. **解锁条件表落位**:卡片 T1「attunement_growth.tres 改造为解锁条件表」——该表已被迭代 21 删除,落位改为 treasure_items 行内 unlock_requires(单一事实源,schema 校验单点)。 7. **计数键换代**:UNLOCK_COUNTER_KEYS 旧键 clear_count(旧工坊按关累计语义)与 C6 通关口径不符,换名 adventure_cleared(生涯通关局数)+扩 vision_used——旧键零行消费安全换名。 8. **vision_used 承载**:save v6 meta 域补零新键(不升版本,迭代 21 先例)+断点随件 vision_counter_used;计数点=use_treasure 受理成功(真视类 kind),冒险共享 dict 聚合、收束 add_vision_used 入档。 diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 79f47daa..8a05926e 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -263,9 +263,6 @@ func _reseed_attune_charges(reset: String) -> void: treasure_inventory.add_item(iid, "attunement", Vector2i(-1, -1), 0) -# 积点制主动件受理(迭代 22 秘宝批 T3——试炼镐「转化获取」唯一入口:不经库存 handle -# 〔charges=0 零骑库存〕,受理闸=行 triggers counter_spend 积点账〔dig_reveal 通用化计数段 -# 逐 charge_n 积 1 点〕;消耗成功才兑付当轮 AP=magnitude。返回 use_treasure 同构 verdict) # 积点制件进度读数(迭代 22 反哺批⑤——自动兑付无玩家发起入口,读数口=C 区秘宝槽悬停浮窗进度行: # assembled=装配在册/points=本战安全格进度(charge_n 取余)/need=charge_n/item_id=装配行 id。 # 未装配=assembled false) diff --git a/game/presentation/treasure_interact_controller.gd b/game/presentation/treasure_interact_controller.gd index 70bcdf60..f5137d1b 100644 --- a/game/presentation/treasure_interact_controller.gd +++ b/game/presentation/treasure_interact_controller.gd @@ -52,7 +52,8 @@ func active_selection() -> int: return _use_selection -# 槽点击(第一步):AP 类(target=none)点击即用(无目标);空间类进/出选中态(toggle 取消)。 +# 槽点击(第一步):AP 类(target=none)点击即用(无目标);空间类进/出选中态(toggle 取消); +# 自动兑付件展示行(handle<0)无使用动作——点击给说明提示(反哺批⑤)。 func on_slot_clicked(slot_handle: int) -> void: var settlement = _settlement_get.call() var runner = _runner_get.call() @@ -62,6 +63,9 @@ func on_slot_clicked(slot_handle: int) -> void: _use_selection = 0 refresh_slots() return + if slot_handle < 0: + toast.show_toast("自动兑付件:满足条件自动生效,无需手动使用", "info") + return var rec: Dictionary = {} for r in inventory_projection(): if int(r.get("handle", 0)) == slot_handle: @@ -113,6 +117,7 @@ func inventory_projection() -> Array: return ms.has_unrevealed() # 只读查询面(排雷玩法 has_unrevealed——手搓全板双循环收口) var out: Array = [] var cs: Dictionary = settlement.attune_convert_state() if settlement != null else {} + var auto_seen := false # 自动兑付件是否已随骑库存出现(恒否——charges=0 不入骑库存;守合成行重复) for rec in runner.treasure_inventory.query_inventory(cb): var arow: Dictionary = db.treasure_item_row(String(rec.get("item_id", ""))) var dn := String(arow.get("display_name", "")) @@ -120,6 +125,7 @@ func inventory_projection() -> Array: var note := "" if bool(cs.get("assembled", false)) and String(cs.get("item_id", "")) == String(rec.get("item_id", "")): note = "积点 %d/%d" % [int(cs.get("points", 0)), int(cs.get("need", 0))] + auto_seen = true out.append({ "handle": int(rec.get("handle", 0)), "item_id": String(rec.get("item_id", "")), @@ -129,6 +135,20 @@ func inventory_projection() -> Array: "usable": bool(rec.get("usable", true)), "note": note, }) + # 自动兑付件展示行(反哺批⑤补——charges=0 不入骑库存〔_reseed_attune_charges 跳过注入〕, + # C 区四槽末尾补合成行承载悬停进度;handle=-1 非库存句柄:槽点击走说明 toast 分支不进两步交互) + if bool(cs.get("assembled", false)) and not auto_seen: + var adn := String(cs.get("display_name", "")) + if adn != "": + out.append({ + "handle": -1, + "item_id": String(cs.get("item_id", "")), + "glyph": adn.left(1), + "name": adn, + "used": false, + "usable": true, + "note": "积点 %d/%d" % [int(cs.get("points", 0)), int(cs.get("need", 0))], + }) return out diff --git a/game/tests/unit/core/treasure_effects_test.gd b/game/tests/unit/core/treasure_effects_test.gd index 50b41a9a..4bf8880b 100644 --- a/game/tests/unit/core/treasure_effects_test.gd +++ b/game/tests/unit/core/treasure_effects_test.gd @@ -94,6 +94,23 @@ func _init() -> void: Kit.check_quiet(int(ob4b.action_economy._ap) == ap_base and not bool(ob4b.attune_convert_state().get("assembled", true)), "试炼镐反例:未装配零兑付零计数(AP=整轮表值、读数口 assembled=false)") + # ④b 自动兑付件 C 区展示行(反哺批⑤补——charges=0 不入骑库存〔_reseed 注入循环跳过〕, + # 展示行=C 区悬停浮窗进度唯一承载:handle=-1 非库存句柄+note 进度;空骑库存桩直构投影链) + var rng4c = load("res://core/rng_service.gd").new(); rng4c.set_master_seed(253) + var ob4c = orch_s.new(db, rng4c) + ob4c.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(db.hero.max_hp), "attunement": ["trial_pick"]}) + var ctrl4 = load("res://presentation/treasure_interact_controller.gd").new(db, null, null, null, null, + func(): return ob4c, func(): return StubAutoInvRunner.new()) + var proj4: Array = ctrl4.inventory_projection() + Kit.check_quiet(proj4.size() == 1 and int(proj4[0].get("handle", 0)) == -1 + and String(proj4[0].get("item_id", "")) == "trial_pick" + and String(proj4[0].get("note", "")) == "积点 0/8", + "试炼镐 C 区展示行:装配即入投影(handle=-1 合成行+悬停进度 note=积点 N/8——charges=0 不入骑库存的浮窗承载)") + ob4c._on_cell_revealed({"kind": "safe", "cell": Vector2i(0, 0)}) + var proj4b: Array = ctrl4.inventory_projection() + Kit.check_quiet(String(proj4b[0].get("note", "")) == "积点 1/8", + "试炼镐展示行进度刷新:开格后 note=积点 1/8(组合根重查链)") + # ⑤ 刷新练习(存量账一次性加仓/临时账不动) var rng5 = load("res://core/rng_service.gd").new(); rng5.set_master_seed(261) var adr5 = runner_s.new(db, rng5) @@ -319,3 +336,14 @@ func _cell_at(orch, c: Vector2i) -> Vector2i: if b != null and b.in_bounds(c): return c return Vector2i(-9, -9) + + +# 自动兑付件展示行测试桩(反哺批⑤——空骑库存 runner 桩:charges=0 件不入骑库存, +# 展示行合成逻辑在 treasure_interact_controller.inventory_projection 末段) +class StubAutoInvRunner: + extends RefCounted + var treasure_inventory: StubAutoInv = StubAutoInv.new() + class StubAutoInv: + extends RefCounted + func query_inventory(_cb: Callable) -> Array: + return []