diff --git a/design/gdd/03-内容规模.md b/design/gdd/03-内容规模.md index 3e1437cd..34ce9ae4 100644 --- a/design/gdd/03-内容规模.md +++ b/design/gdd/03-内容规模.md @@ -12,7 +12,7 @@ |---|---|---|---| | 第一层 | 地精洞窟 | 分支节点图(战斗 / 随机道具 / 恢复点 / Boss 等节点型) | 已落地(正式序列首关) | | 第二层 | 幽暗地域 | 同一分支节点图模板复用,第二关深度轴行值填充 | 已落地(正式序列已生效) | -| 第三层 | 灰矮人矿城 | 同一分支节点图模板复用,第三关深度轴行值填充 | 已落地(正式序列已生效;测试直入口保留) | +| 第三层 | 灰矮人矿城 | 同一分支节点图模板复用,第三关深度轴行值填充 | 已落地(正式序列已生效) | 现行节点行集与行值读 `data/tables/level.tres`(registry 为准)。再扩关卡走扩展批流程:先填深度轴行值、再接正式序列推进,不新造节点链结构。 @@ -62,7 +62,7 @@ MVP 不加新状态;扩容走 status_effects.tres 加行 + registry 登记。 垂直切片阶段明确砍掉或推迟的内容,写在这里防止回潮: -- 更多关卡(N>3)——第三关已按扩展批流程落地(行集 + 测试直入口);三关正式序列已生效(冒险结构实现批 2026-09-21) +- 更多关卡(N>3)——第三关已按扩展批流程落地(行集);三关正式序列已生效(冒险结构实现批 2026-09-21),唯一试玩入口 = 完整冒险(单关测试直入口退役,2026-09-23 用户拍板) - 精英节点与词缀系统——只预留了字段位,不定义语义 - 地形变异扩展内容——首批四类型已落地,反制道具与全局作用域排 Alpha(2026-09-20 用户拍板) - 道具/增益之间的组合协同(遗物式联动) diff --git a/design/gdd/systems/冒险循环/冒险结构.md b/design/gdd/systems/冒险循环/冒险结构.md index 70160fd8..0da4a2df 100644 --- a/design/gdd/systems/冒险循环/冒险结构.md +++ b/design/gdd/systems/冒险循环/冒险结构.md @@ -170,7 +170,7 @@ GP(显示名「金币」)是冒险内的一次性属性提升货币:战斗 重开 = 全新一轮冒险:注入新主种子、从第一关、满血、空手——不是"重打刚才那一关"。上一局清零的是生命账本、库存与种子;唯一保留面是冒险摘要,它给每次死亡一个可对照的坐标("这局到第二层"),重开因此是新局面而不是复读。 -主动放弃按与失败同型结算:已获得的大奖励不追回、已到手的保留,摘要标记未打穿。放弃请求只在非战斗期受理,战斗进行中一律拒绝(战斗域单事务在飞)。放弃的界面入口挂在[关卡节点地图](关卡节点地图.md)的选路覆盖层上——选路停点天然是非战斗期,按钮只在冒险模式开放(单关测试直入口没有可放弃的冒险对象);战斗进行中没有选路停点,入口因此天然不可达,拒绝分支仅作防御兜底。 +主动放弃按与失败同型结算:已获得的大奖励不追回、已到手的保留,摘要标记未打穿。放弃请求只在非战斗期受理,战斗进行中一律拒绝(战斗域单事务在飞)。放弃的界面入口挂在[关卡节点地图](关卡节点地图.md)的选路覆盖层上——选路停点天然是非战斗期,按钮只在冒险模式开放(选路覆盖层只在冒险模式出现);战斗进行中没有选路停点,入口因此天然不可达,拒绝分支仅作防御兜底。 主种子(本文含义:冒险开始时注入的一个随机种子,本局全部随机性都派生自它)在冒险开始时注入恰一次。冒险内的雷图生成、奖励抽取、遭遇掷骰与 GP 掉落掷定(消费 gp_drop 封闭流)全部派生自它,冒险内不重置、不逐关播种——早期切片"每关开局各自播种"是没有冒险层时的历史简化,已改写为全冒险单一种子。冒险的可复现域 = 主种子 + 各随机流计数器,供复盘与快照(见[存档序列化](../表现与支撑/存档序列化.md))。重开抽到的种子与上一局相同是允许的(随机源范围小,撞种子不算错误),但生成新种子不得复用玩法里的随机数。 @@ -263,7 +263,7 @@ GP(显示名「金币」)是冒险内的一次性属性提升货币:战斗 - 收束次序:通关宣布到达时库存已被关卡节点地图先行清空;重复的关卡结束宣布被拒绝,账本与摘要零变化。 - 覆盖义务:冒烟断言——行集内每个战斗与关底节点(关、深)在敌人与棋盘生成的数值表中有精确行;序列关数 = 行集关数。 - 生效前提未兑现(第二关行缺行):以单关冒险运行(合法态),两关断言显式跳过而非失败。 -- 放弃:战斗中的放弃请求被拒绝;非战斗期放弃按失败同型结算,已获大奖励不追回;放弃入口仅在选路停点呈现且只在冒险模式开放(单关测试直入口无放弃按钮)。 +- 放弃:战斗中的放弃请求被拒绝;非战斗期放弃按失败同型结算,已获大奖励不追回;放弃入口仅在选路停点呈现且只在冒险模式开放。 - 种子域:冒险内零随机流自开,一切随机派生自主种子(含 GP 掉落掷定的 gp_drop 封闭流);同种子同操作序列两局逐位可复现。 - GP 账本封闭:钱包只有三个收口——战斗完成入账恰一次(掉落掷定 + 关级加值 + 战斗内缓冲合计)、商店购买出账、冒险收束清零;战斗内缓冲与钱包隔离,战斗进行中钱包零变动。 - GP 不留痕:GP 不入存档、不入冒险摘要;重开后钱包从零开始。 @@ -284,3 +284,4 @@ GP(显示名「金币」)是冒险内的一次性属性提升货币:战斗 | v0.5 | 2026-09-20 | **GP+商店房批(用户拍板——与关卡节点地图 v1.1 / 战斗结算 v0.23 / 地图战斗资源 v0.15 / 难度曲线与Boss v0.18 / registry / 切片同批)**:① 新增「GP 冒险账本」节——钱包三收口(战斗完成统一入账 / 商店购买出账 / 冒险收束清零)+ 战斗内缓冲隔离语义(格面兑付入缓冲、战斗完成统一到账、战斗域零钱包状态);② 统一冒险加算区成文——商店购买与关卡大奖励上限提升同一本账(三轴 {攻击, 生命上限, 护甲等级},经覆盖值通道生效;购血 = 上限与当前生命同步 +Δ);③ GP 掉落掷定消费 gp_drop 封闭随机流(主种子派生)登记进主种子节与调参表;④ GP 不入存档、不入冒险摘要;范围表 / 大奖励表 / 依赖表 / 验收标准同步。数值全 provisional-pending-曲线调参批,行值读 shop.tres | | v0.6 | 2026-09-21 | **冒险结构实现批落地登记(切片 AdventureRunner + LevelRunner 账本外置 + AdventureTable 建表——与切片代码 / registry / 03 / 06 / roadmap 同批)**:本批只记实现批状态,正文规则口径零改动。落地面 = 三关正式序列生效(首页「开始冒险」)、接口契约四件套正式侧承载(开始关卡 start_level_external / 战斗结果上报共享账本直写 / 治疗封顶回报 apply_rest / 关卡结束 level_finished)、生命账本与 GP 冒险账本上交冒险结构层、大奖励非末关恰兑付一次(末关钥石照记)、冒险摘要五件事产出、主种子恰一次注入(registry rng_service new_adventure 调用权同批登记)、结算受理只算一次;06 待办「两关序列未生效」「切片行进代码与定稿的接口漂移」「冒险表未建表」三条随批解决移除。**规划书冲突按代码为准登记两处**:①大奖励 `_pending_next_level` 置位先于 emit(信号同步派发——处理器内立即确认需标记已就位);②放弃受理补 state=running 守卫(契约 7 结算受理只算一次的 abandoned 侧对齐) | | v0.7 | 2026-09-21 | **放弃入口界面呈现批(用户拍板「放弃接口也要有」——台账 119,推翻本文原「MVP 无界面入口、呈现后置」条款)**:① 放弃入口挂[关卡节点地图](关卡节点地图.md)选路覆盖层(选路停点 = 天然非战斗期;战斗中拒绝转为防御兜底分支——入口在战斗期不可达);② 入口只在冒险模式开放,单关测试直入口没有可放弃的冒险对象;③ 正文改现行口径(放弃节 / 验收标准放弃条),P1 后置表「放弃入口的界面呈现」随落地移除(同表「两关序列生效」系 v0.6 已落地漏收口,顺手同口径移除);④ 验收闸 = ui_probe 三断言(默认隐藏 / 冒险模式可见 / 首战中按下被拒 state 保持 running)+ 冒烟既有放弃两态 API 断言 | +| v0.8 | 2026-09-23 | **单关测试直入口退役(用户拍板——唯一试玩入口 = 完整三关冒险)**:正文改现行口径两处(放弃节「按钮只在冒险模式开放」的理由从「单关直入口无冒险对象」改为「选路覆盖层只在冒险模式出现」;验收标准放弃条同步去单关直入口从句)——放弃语义对象(整局冒险)与拒绝分支零改动。版本历史行(v0.7)按 ADR 纪律不回改 | diff --git a/design/gdd/systems/表现与支撑/存档序列化.md b/design/gdd/systems/表现与支撑/存档序列化.md index 15421ca8..5a6e8948 100644 --- a/design/gdd/systems/表现与支撑/存档序列化.md +++ b/design/gdd/systems/表现与支撑/存档序列化.md @@ -95,7 +95,7 @@ SaveManager 不依赖任何玩法系统就能工作:它是被动的承载方 写盘是原子操作:先写临时文件(`<路径>.tmp`),写完改名覆盖正式档——写一半断电不留烂档。 -单局测试入口不写档:关卡直入口(L2/L3)与调试两层路径不接写盘——测试模式不污染生涯数据;只有正式冒险(AdventureRunner)收束才入账。 +单局测试入口不写档:调试两层与 headless 单关测试路径(LevelRunner.start_level,仅 QA 工具使用)不接写盘——测试模式不污染生涯数据;只有正式冒险(AdventureRunner)收束才入账。 ### 版本与迁移 @@ -157,3 +157,4 @@ MVP 不要求本系统的闭环([不做清单](../../05-不做清单.md)把单 |---|---|---| | v0.1 | 2026-09-15 | 自归档线索整合为范围草案(旧体系未单独成文) | | v1.0 | 2026-09-21 | **存档序列化批(SaveManager 落地)**:格式裁决 JSON 单档 `user://save.json`;数据模型逐域成文(元进度四件 + 最近冒险摘要 cap 10);读写时机(启动读盘/冒险收束写盘/营地即时写/测试入口不写)+ tmp+rename 原子写;版本策略(高版本拒读禁写/低版本链式迁移/缺域补齐);损坏兜底(.corrupt.bak 备份 + 默认新档);待设计问题清单六条全部裁决成文(1/2/3/5/6 入核心规则,4 = 单档 MVP 多档留 P2)。自范围草案升定稿 | +| v1.1 | 2026-09-23 | 单关测试直入口(L2/L3)退役(用户拍板——唯一试玩入口 = 完整冒险):不写档条款改口径为「调试两层与 headless 单关测试路径(LevelRunner.start_level,仅 QA 工具)」;写盘面零改动(正式冒险收束唯一写档点不变) | diff --git a/design/registry/entities.yaml b/design/registry/entities.yaml index b60b91c3..bf7d9843 100644 --- a/design/registry/entities.yaml +++ b/design/registry/entities.yaml @@ -77,7 +77,7 @@ entities: format: JSON 单档(人可读可调试;字典键一律 String) version: SAVE_FORMAT_VERSION=1(registry schema_version 同源;高版本拒读 + 禁写盘 + 内存默认新档运行;低版本读档时链式迁移 register_migration,任一步失败按损坏兜底) domains: meta 域六件——keystone_balance(内容 owner=元进度)/ forge_levels 三档 max_hp·atk·armor_class(元进度)/ item_unlocks(元进度·道具工坊消费)/ level_clear_counts(元进度)/ boss_first_kill(元进度)/ recent_summaries cap 10 冒险摘要五件事副本(内容 owner=冒险结构;元素附 boss_drops 副本——Boss 两档金额入账随元进度实现批 2026-09-21 接通,apply_adventure_summary 第二参) - write_timing: 启动读盘;正式冒险收束(adventure_finished → apply_adventure_summary)写盘;营地每笔钥石变更后即时写(元进度实现批接线);单关测试入口与调试两层不写档 + write_timing: 启动读盘;正式冒险收束(adventure_finished → apply_adventure_summary)写盘;营地每笔钥石变更后即时写(元进度实现批接线);调试两层与单关测试路径(LevelRunner.start_level,仅 QA 工具)不写档 atomic_write: tmp + rename 原子写(写一半断电不留烂档——钥石不丢判据承载) corruption_policy: 坏档改名 .corrupt.bak(已存在覆盖)+ 默认新档继续 + 警告留痕(不静默丢档不 crash) domain_boundary: 行动点/RAP/道具库存/生命账本/GP 不入档(局内资源单局清零;冒烟断言承载) diff --git a/prototypes/dungeon-sweeper-vertical-slice/README.md b/prototypes/dungeon-sweeper-vertical-slice/README.md index 7e28610a..dd84aa5c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/README.md +++ b/prototypes/dungeon-sweeper-vertical-slice/README.md @@ -12,7 +12,7 @@ 1. 用 Godot 4.7.2 打开本目录(含 `project.godot` 的文件夹) 2. 按 F5 运行(主场景 `presentation/main.tscn`) -3. **主入口:`▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)`**——一条命走完三关(冒险结构实现批 2026-09-21:生命/GP/加算区跨关同账、非末关通关发大奖励〔上限提升+血回满+钥石〕、任意战斗倒下冒险终、结算屏出冒险摘要五件事;选路停点可「放弃冒险」(同失败结算,2026-09-21 用户拍板补入口));首关节点链:战→战→赏→战→战→疗→王。`▶ 测试直入口 · 第二关 / 第三关` 保留单关试玩模式 +3. **主入口:`▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)`**——一条命走完三关(冒险结构实现批 2026-09-21:生命/GP/加算区跨关同账、非末关通关发大奖励〔上限提升+血回满+钥石〕、任意战斗倒下冒险终、结算屏出冒险摘要五件事;选路停点可「放弃冒险」(同失败结算,2026-09-21 用户拍板补入口));首关节点链:战→战→赏→战→战→疗→王。唯一试玩入口 = 完整冒险——单关测试直入口(L2/L3)已退役(2026-09-23 用户拍板) 4. `测试用 · 角色数值调整板`:攻击/生命百分比加成(模拟天赋树解锁)+ 批量模拟(输出通关率/承伤期望/败北节点分布) headless 冒烟(55 项断言,含关卡线性推进/满配通关/裸装败北/回放确定性,约 30 秒): @@ -169,7 +169,7 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/s ## L2 试玩采集清单(分支节点图批 2026-09-17——关卡 2「幽暗地域」试玩观测项) -入口:首页"▶ 测试直入口 · 第二关 · 幽暗地域(分支节点图试玩——岔路口选下一跳)"(测试模式直入口,种子框同用;正式序列走「开始冒险」主入口)。 +入口:「开始冒险」主入口推进到第二关段(单关测试直入口已退役——2026-09-23 用户拍板,唯一试玩入口 = 完整三关冒险;种子框同用)。 - **选路行为**:战斗胜利/兑付确认后弹出选路覆盖层(整图 + 候选按钮)——玩家是否理解"零自动推进、每跳都要选";单候选节点(恢复点→编号 6)是否被读成"卡住"。 - **岔路使用率**:四处真实岔路的走向分布(入口 0→[1,2];战斗 1→[3,4];奖励 2→[4,5];深层 4→[6,7])——奖励点路线 vs 纯战斗路线的选择比例;整图渲染是否被实际用来规划(对比只点按钮的玩家)。 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd index d39b75d6..7ad51825 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/battle_narrator.gd @@ -46,15 +46,6 @@ static func debug_open_line(seed_val: int, level: int, level_mode: bool) -> Stri seed_val, level, "(关卡模式 · HP 延续)" if level_mode else ""] -# 关卡终局文案(result = victory/defeat;载荷 = runner level_finished 快照) -static func level_end_text(result: String, payload: Dictionary, effective_max_hp: int) -> String: - if result == "victory": - return "关卡通关!\n剩余 HP %d/%d" % [ - int(payload.get("hp", 0)), effective_max_hp] - return "关卡失败……探险者倒在了节点 %d(%s)。" % [ - int(payload.get("node_index", 0)) + 1, String(payload.get("node_type", ""))] - - # 调试战斗终局文案(level_end = 关内两层模式的关卡末) static func battle_end_text(result: String, level_end: bool) -> String: if level_end: diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd index de67f9f7..57c5f248 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/main.gd @@ -7,14 +7,14 @@ # debug_session.gd(规范 1.1 审视线 + 调试语义单一实现)。 # 组合根(composition root):唯一的模式流转与 core↔view 接线处。 # 职责边界:① 构建四区视图并接线信号(节点构建归 ui_assembler.gd——规范 1.1,本类只接线) -# ② 模式管理(完整关卡 关卡节点地图 runner / 调试两层——后者编排归 debug_session.gd;单场原语仅探针使用) +# ② 模式管理(正式冒险 AdventureRunner——唯一试玩入口;调试两层编排归 debug_session.gd;单场原语仅探针使用) # ③ 核心写调用只在此处发生(minesweeper.settle_batch / 刷新编排入口 / runner 推进; # E4 收口:加成值零表写——面板百分比经 runner.set_hero_override / debug_session 生效值 Callable # 以 encounter 参数入战斗,db.hero 运行期只读) # ④ 全屏 States 状态机(挖掘/结算/终局)与一次性提示去重。 # 2026-09-15 格面资源批:道具窗口与库存面板随可选类道具删除下线——E 区移除,窗口接线/选择序列删除。 # GP+商店房实施批(2026-09-21):商店覆盖层挂接与信号接线(装配器零信号连接纪律——句柄返回、 -# 本类连接,仿 lv2btn 模式)+ runner.wallet_changed 转发试玩日志(wallet 行族)+ A 区顶栏金币余额。 +# 本类连接)+ runner.wallet_changed 转发试玩日志(wallet 行族)+ A 区顶栏金币余额。 # 渲染与文案归 views/,核心状态→文案装配归 hud_presenter.gd 与 battle_narrator.gd,节点构建归 # ui_assembler.gd,模态层级归 modal_stack.gd,批量模拟编排归 tune_sim_controller.gd,调试模式编排归 # debug_session.gd——本类不拼呈现字符串。 @@ -31,7 +31,7 @@ const MetaProgressionScript := preload("res://core/meta_progression.gd") # 元 var db var settlement = null # 当前战斗编排器(关卡与调试模式统一读它) -var runner = null # 关卡节点地图 图版推进器(完整关卡模式;冒险模式 = 每关重取 adventure.runner()) +var runner = null # 关卡推进器(冒险模式每关重取 adventure.runner();单关 UI 直入口已退役——LevelRunner 仅存 headless 测试面) var adventure = null # 冒险结构编排器(冒险结构实现批——三关连探;转发信号照 runner 接线原样对接) var save_manager = null # 存档序列化承载(存档序列化批——启动读盘/冒险收束写盘;只经访问器与 apply 原语,禁读内部结构) var meta = null # 元进度账本服务(元进度实现批——余额/定价/购买/工坊解锁/Boss两档入账/注入计算;零信号纯被动) @@ -163,9 +163,7 @@ func _build_ui() -> void: var start: Dictionary = UiAssembler.build_start_overlay(self, db) _start_overlay = start.overlay _seed_edit = start.seed_edit - start.lvbtn.pressed.connect(_start_full_level.bind(1)) - start.lv2btn.pressed.connect(_start_full_level.bind(2)) - start.lv3btn.pressed.connect(_start_full_level.bind(3)) + start.lvbtn.pressed.connect(_start_adventure_from_menu) start.tunebtn.pressed.connect(_show_tune_panel) start.dbgbtn.pressed.connect(_start_debug_levels) start.campbtn.pressed.connect(_open_camp) @@ -216,36 +214,11 @@ func _input_locked() -> bool: or _start_overlay.visible or _inter_overlay.visible or settlement == null -# ==================== 完整关卡模式(关卡节点地图 图版推进器)==================== +# ==================== 冒险模式入口(唯一试玩入口——单关测试直入口退役 2026-09-23 用户拍板)==================== -func _start_full_level(level_index: int = 1) -> void: +func _start_adventure_from_menu() -> void: _start_overlay.visible = false - if level_index == 1: - _start_adventure() # 冒险首关即 L1——正式序列入口(冒险结构实现批) - return - # E4:加成经 runner.set_hero_override → encounter 参数入战斗(零表写——无双写叠加隐患) - runner = LevelRunner.new(db, get_node("/root/RngService")) - runner.set_hero_override(tune_panel.atk_pct(), tune_panel.hp_pct()) - runner.step_row.connect(_on_step_row) - runner.round_started.connect(_on_round_started) - runner.board_changed.connect(_on_board_changed) - runner.node_entered.connect(_on_runner_node) - runner.choices_presented.connect(_on_choices_presented) - runner.level_finished.connect(_on_level_finished) - runner.wallet_changed.connect(_on_wallet_changed) - _level_active = true - _adventure_mode = false - route_overlay.set_abandon_available(false) # 单关测试直入口无冒险可放弃(放弃语义对象 = 整局冒险) - _reset_run_scope() - log_view.clear() - var seed_val := _seed_value() - logger.start_session(seed_val) # 各战斗的 attach 在 _on_runner_node 内(settlement 逐战创建) - if not runner.start_level(level_index, seed_val): - _fatal_screen("关卡启动失败——见输出日志") - return - log_view.append_bb(Narrator.level_open_line(String(runner.level_row.get("display_name", "")), - seed_val, int(db.hero.atk), int(db.hero.max_hp), - tune_panel.atk_pct() > 0 or tune_panel.hp_pct() > 0)) + _start_adventure() # 冒险模式(三关连探——AdventureRunner 编排;转发信号签名与 runner 一致,接线照原样对接; @@ -322,28 +295,6 @@ func _on_runner_node(node: Dictionary, info: Dictionary) -> void: _refresh_presentations() -func _on_level_finished(payload: Dictionary) -> void: - _level_active = false - var result: String = String(payload.get("result", "")) - settlement = null - _close_modal(route_overlay) # 终局兜底(放弃入口新场景:选路层在开态——走模态栈恢复其遮挡面;未开过时 close 容错) - _sync_settlement_visuals() # → 全关 - info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(增益状态×4④) - stage.gray_all_enemies() # 死亡兜底:站桩灰化残位(禁瞬消语义的战斗收尾面) - if result == "victory": - # 胜利终局 overlay(金蜡封落于棋盘中央) - _end_overlay.open_victory(Narrator.level_end_text("victory", payload, runner.effective_max_hp()), - "返回冒险", board_view.get_global_rect()) - else: - _end_overlay.open_defeat(Narrator.level_end_text("defeat", payload, 0), - "返回", board_view.get_global_rect()) - logger.log_level_end(payload) # 试玩时间线:关卡终局 - runner.restore_hero_base() # 关卡结束——覆盖值归零(下次从面板重新应用) - strip_view.refresh(runner) - _open_modal(_end_overlay) - _refresh_presentations() - - # ---- 冒险模式终局面(大奖励屏 / 冒险结算屏——冒险结构实现批)---- # 大奖励兑付(非末关通关恰一次):复用 _inter_overlay(label + 确认按钮,零新增交互范式); @@ -364,7 +315,7 @@ func _on_adventure_finished(summary: Dictionary) -> void: info_panel.set_status_cleared_feedback() # 状态槽全清可见反馈(增益状态×4④) stage.gray_all_enemies() # 死亡兜底:站桩灰化残位(禁瞬消语义的战斗收尾面) # 存档入账(元进度实现批):settle = Boss 两档入账 + 工坊解锁 + 摘要入档 + 写盘(唯一写档点不变—— - # 单关测试入口与调试两层不经过本处理器,天然不写档不污染生涯数据)。 + # 调试两层不经过本处理器,天然不写档不污染生涯数据)。 var detail: Dictionary = {} if meta != null: detail = meta.settle_adventure(summary) @@ -490,7 +441,7 @@ func _show_board_fx_for_node(node_type: String = "") -> void: func _on_battle_finished(payload: Dictionary) -> void: - # 仅调试模式接线(完整关卡的结束归 关卡节点地图 runner._on_level_finished) + # 仅调试模式接线(冒险的关卡结束归 AdventureRunner._on_level_finished——大奖励/冒险终局分派) var result: String = String(payload.get("result", "")) _sync_settlement_visuals() # → 全关(battle_over 反馈面) stage.gray_all_enemies() # 死亡兜底:站桩灰化残位(禁瞬消语义的战斗收尾面) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd index 7a827e03..366253a3 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/ui_assembler.gd @@ -85,7 +85,7 @@ static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Di var end_overlay := BattleEndOverlay.new() host.add_child(end_overlay) - # 商店覆盖层(GP+商店房实施批 2026-09-21——商店房节点兑付呈现;按钮接线归组合根 lv2btn 模式) + # 商店覆盖层(GP+商店房实施批 2026-09-21——商店房节点兑付呈现;按钮接线归组合根,装配器零信号连接) var shop_overlay := ShopOverlay.new() host.add_child(shop_overlay) @@ -129,13 +129,9 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary: seed_edit.custom_minimum_size = Vector2(120, 0) seed_row.add_child(seed_edit) - # 冒险结构实现批(2026-09-21):lvbtn = 正式「开始冒险」入口(三关序列);L2/L3 保留测试直入口 + # 唯一试玩入口 = 正式「开始冒险」(三关序列);单关测试直入口(L2/L3)退役(2026-09-23 用户拍板) var lvbtn := UiKit.button("▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)") box.add_child(lvbtn) - var lv2btn := UiKit.button("▶ 测试直入口 · 第二关 · 幽暗地域(分支节点图试玩——岔路口选下一跳)") - box.add_child(lv2btn) - var lv3btn := UiKit.button("▶ 测试直入口 · 第三关 · 灰矮人矿城(基线红档属预期,配合调整板加成试玩)") - box.add_child(lv3btn) var tunebtn := UiKit.button("测试用 · 角色数值调整板(模拟天赋树属性提升)") box.add_child(tunebtn) var dbgbtn := UiKit.button("调试 · 连续关卡两层(HP 关内延续)") @@ -151,7 +147,7 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary: box.add_child(help) return {"overlay": overlay, "box": box, "seed_edit": seed_edit, - "lvbtn": lvbtn, "lv2btn": lv2btn, "lv3btn": lv3btn, "tunebtn": tunebtn, "dbgbtn": dbgbtn, + "lvbtn": lvbtn, "tunebtn": tunebtn, "dbgbtn": dbgbtn, "campbtn": campbtn} diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/route_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/route_overlay.gd index 53e7f852..3405a5a6 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/route_overlay.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/route_overlay.gd @@ -7,7 +7,7 @@ # 无任何写面;图形为程序化占位质量(占位纪律)。 # 放弃入口(2026-09-21 用户拍板「放弃接口也要有」——台账 119;推翻 冒险结构.md「MVP 无界面入口」 # 后置条款):选路停点 = 天然非战斗期(接口契约:战斗中放弃一律拒绝),按钮常驻本层底部; -# 可见性归组合根(冒险模式开、单关测试直入口关——放弃的语义对象是"整局冒险")。 +# 可见性归组合根(只在冒险模式开——放弃的语义对象是"整局冒险";调试两层无节点图天然不可达)。 class_name RouteOverlay extends Control @@ -25,7 +25,7 @@ const NODE_LABELS := {"battle": "战", "reward": "赏", "rest": "疗", "boss": " var _runner = null var _btn_box: VBoxContainer var _title: Label -var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——冒险模式开/单关测试直入口关) +var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——只在冒险模式开) func _init() -> void: @@ -58,7 +58,7 @@ func _init() -> void: visible = false -# 放弃入口可见性(组合根在冒险启动/单关直入口启动时切换——语义对象 = 整局冒险) +# 放弃入口可见性(组合根在冒险启动时开、默认关——语义对象 = 整局冒险) func set_abandon_available(v: bool) -> void: abandon_btn.visible = v diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd index f0646c99..c4f5c487 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_click_probe.gd @@ -24,7 +24,7 @@ var _tested_legend := false var _tested_review := false var _tested_refresh_chain := false var _tested_tooltip := false -var _tested_l2 := false # 分支节点图批:L2 入口行为段(首页直入口 + 岔路选路真实点击) +var _inter_confirms := 0 # 大奖励屏确认计数(冒险推进停点——首见确认推进 L2,再见 = L2 通关收口) var _route_clicks := 0 # 选路覆盖层点击计数(岔路使用率观测面) @@ -84,9 +84,9 @@ func _ready() -> void: var iso_reset = load("res://core/save_manager.gd").new("user://smoke_click_probe_reset.json") iso_reset.load_from_disk() main.meta = load("res://core/meta_progression.gd").new(main.db, iso_reset) - main.tune_panel.set_sliders(200, 100) + main.tune_panel.set_sliders(400, 200) # 高加成保 L1 可通关——L2 岔路选路段只能经冒险推进到达(单关直入口退役) var t1 := Time.get_ticks_msec() - main._start_full_level() + main._start_adventure_from_menu() var t_entry := Time.get_ticks_msec() - t1 # 性能 AC:进入战斗屏到首格可点 ≤500ms(棋盘 install 完成口径); # 场景实例化冷启动总时长(含全部脚本首载编译——引擎启动域,非 AC 口径)另列观测 @@ -98,7 +98,7 @@ func _ready() -> void: _problems.append("首格未可点(cells=0)") print("[click_probe] 首格可点计时 %dms(进入战斗屏口径,≤500ms AC)· 场景冷启动 %dms(含 UI 构建 %dms,观测项)" % [ first_cell_ms, t_total, t_ui_ready]) - var action_budget := 9000 + var action_budget := 20000 while not _done and action_budget > 0: action_budget -= 1 await get_tree().process_frame @@ -117,24 +117,33 @@ func _ready() -> void: main.shop_overlay.leave_pressed.emit() _flags_placed.clear() continue + if main._inter_overlay.visible: + # 大奖励屏(冒险级停点——非末关通关恰一次):首见确认推进 L2(真实按钮信号路径);再见 = L2 已通关,收口 + if not main._input_locked(): + _problems.append("大奖励屏打开时输入未锁定") + _inter_confirms += 1 + if _inter_confirms == 1: + var next_btn: Button = _find_button_by_prefix(main._inter_overlay, "进入下一层") + if next_btn == null: + _problems.append("大奖励屏确认按钮缺失") + else: + next_btn.pressed.emit() + await get_tree().process_frame + await get_tree().process_frame + if main.runner == null or int(main.runner.level_row.get("level_index", -1)) != 2: + _problems.append("大奖励确认后未推进到第二关(level_index != 2)") + else: + _done = true + print("[click_probe] 第二关大奖励屏(L2 通关)——探针收口 · 选路口点击 %d 次" % _route_clicks) + break + _flags_placed.clear() + continue if main._end_overlay.is_open(): - # 终局 overlay 模态断言(pattern 6:打开时输入锁定) + # 冒险终局(失败/放弃/打穿)——单关直入口退役后无二段入口,见终局即收口(模态断言照旧) if not main._input_locked(): _problems.append("终局 overlay 打开时输入未锁定") - if not _tested_l2: - # L1 终局 → 首页直入口切换 L2(分支节点图批行为段:真实"返回"路径 + start_level(2)) - _tested_l2 = true - print("[click_probe] L1 结束:%s——切换关卡 2 试玩入口" % main._end_overlay.verdict_text().replace("\n", " ")) - main._on_battle_end_continue() # 终局出口(真实"返回"按钮处理器——全量清理回开始层) - if not main._start_overlay.visible: - _problems.append("终局返回后未回到开始覆盖层") - main.tune_panel.set_sliders(200, 100) - main._start_full_level(2) - if main.runner == null or int(main.runner.level_row.get("level_index", -1)) != 2: - _problems.append("关卡 2 直入口启动失败(level_index != 2)") - continue _done = true - print("[click_probe] 关卡结束:%s · 选路口点击 %d 次" % [ + print("[click_probe] 冒险终局:%s · 选路口点击 %d 次" % [ main._end_overlay.verdict_text().replace("\n", " "), _route_clicks]) break if main.settlement == null or main.runner == null: diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd index 5b08e6e7..c01d0d26 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_layout_probe.gd @@ -10,7 +10,7 @@ func _ready() -> void: var main = load("res://presentation/main.tscn").instantiate() add_child(main) await get_tree().process_frame - main._start_full_level() + main._start_adventure_from_menu() for i in range(3): await get_tree().process_frame var win := get_window() diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd index 26fd3603..603a8655 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_probe.gd @@ -1,7 +1,7 @@ # VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(场景级 headless 探针——四区布局实例化断言;entry 17②③)。 # Date: 2026-09-12(战斗屏改造批扩展;基础 = 2026-09-11 首战棋盘探针——既有断言全保留) -# 项目运行模式(autoload 在位)实例化 main.tscn,直接驱动 _start_full_level(), +# 项目运行模式(autoload 在位)实例化 main.tscn,直接驱动 _start_adventure_from_menu()(完整冒险——唯一试玩入口), # 断言四区真实构建(棋盘/进度条/主角站桩/敌方站桩/AP 点阵/明细带/数据框;E 区道具面板已删除; # 2026-09-20 全站桩拍板——英雄卡/敌人卡断言随站桩改造批换装)。 # 运行:Godot_console.exe --headless --path . res://smoke/ui_probe.tscn @@ -14,9 +14,9 @@ func _ready() -> void: add_child(main) await get_tree().process_frame var problems: Array = [] - # ---- 放弃入口(2026-09-21 用户拍板——台账 119):默认隐藏(单关测试直入口无冒险可放弃)---- + # ---- 放弃入口(2026-09-21 用户拍板——台账 119):默认隐藏(非冒险模式无冒险可放弃——开始层/调试两层)---- if main.route_overlay.abandon_btn == null or main.route_overlay.abandon_btn.visible: - print("[ui_probe] FAIL — 放弃按钮默认应隐藏(单关测试直入口关)") + print("[ui_probe] FAIL — 放弃按钮默认应隐藏(非冒险模式关)") get_tree().quit(1) return # ---- 营地覆盖层(元进度实现批——主菜单入口/开合/余额文案/购买禁用态; @@ -58,7 +58,7 @@ func _ready() -> void: var iso_zero = load("res://core/save_manager.gd").new("user://smoke_ui_probe_camp.json") iso_zero.load_from_disk() main.meta = load("res://core/meta_progression.gd").new(main.db, iso_zero) - main._start_full_level() + main._start_adventure_from_menu() await get_tree().process_frame await get_tree().process_frame # ---- 放弃入口·冒险模式可见 + 战斗中按下被拒(真实按钮路径——入口挂选路停点,战斗中拒绝为防御分支)---- diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd index 3ddc0802..17465696 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/ui_tune_probe.gd @@ -61,7 +61,7 @@ func _ready() -> void: # (攻击 ×2 / 生命 +50%)——旧缺陷(残留值被当基线再乘)在 encounter 参数链上同样被抓获 main.tune_panel.set_sliders(100, 50) main.tune_panel.press_apply_for_probe() - main._start_full_level() # 完整关卡路径(_start_single 按语义清零滑条 = 恒基线,不承载加成断言) + main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言) await get_tree().process_frame await get_tree().process_frame var hero: Dictionary = main.settlement.presentation_snapshot().hero