diff --git a/active.md b/active.md index ebf5e2ea..51908539 100644 --- a/active.md +++ b/active.md @@ -11,6 +11,7 @@ - **工程路线图**:`production/roadmap.md`(2026-09-17 建——里程碑时间线 + 路线图;实时状态看下方快照节) ## 当前状态快照(收口批先更新本节,再追加台账;≤10 行) +- **更新于**:2026-10-06(**暂停层只读入口批完成**,迭代 17 卡③功能性 UI 交互批——纯呈现零数值改动=零 MC,worktree iter17-pause-20261006 基点 44328b1,两会话接力〔前任完成 T0/T1+组 B 代码后额度中断,续跑会话收尾跑闸+T3-T5〕,全部提交在 worktree 内未合并,台账 380):战斗屏/关卡地图屏各一枚「暂停」常驻角标开只读层(余额徽+三柜台进度+「继续」,零购买零写通路;badge 已工厂化入 ui_kit,营地钥石徽同步迁移观感零变化);他模态开着角标置灰不可点(has_open_excluding)。闸=冒烟 788/0(基线 783/0)+run_all 17 套+四探针全绿(含输入锁 case⑯/只读巡检/零动效契约/对比度新对)+doc 三闸零新增;存量移交两项=熵增闸 design/diagrams 越界(00712d5 落籍漏步,主区同红)+06/07 gdd_lint 存量注记红(本批零触碰)。出口报告=design/qa/iter17-pause-exit-report-2026-10-06.md。**下一步待用户=实机目检**(design/qa/iter17-pause-human-gate-checklist-2026-10-06.md,重点含排播排空副作用确认项 D2);过闸后合并落 master、06 目标条销号。 - **更新于**:2026-10-06(**生涯界面设计提纲批完成**,迭代 17 卡④纯设计批——零代码、零数据面、零测试执行,worktree iter17-career-20261006 基点 fc4dbde,全部提交在 worktree 内未合并):设计提纲已按 P0 甲裁定写成正式设计文档 design/gdd/systems/跨局循环/生涯.md v0.1(状态=范围草案),五节写全(信息架构/承载内容清单/与既有界面关系/分期实装建议/待裁点);P0-P5 六个已裁决结论全部落进对应章节,任务书 §2 十条现状事实(F1-F10)逐条对照代码复核全绿;复核新发现一项待用户裁决点「P6 营地底栏『最近冒险』条带去留」(数据源与生涯历史回看同一份存档摘要、条带拼行无上限,扩容后会爆——建议期二时撤下、逐局回看整体归生涯)。同批改动:02-系统清单.md 根索引补生涯行与 CAREER 代号行、06 生涯条状态行改「设计提纲已成文待评审」、roadmap 迭代 17 卡④行改已交付待评审、出口报告落 design/qa/iter17-career-exit-report-2026-10-06.md。**下一步待用户=评审设计提纲**(评审时随裁 P6);评审通过后期一/二/三实装批另行立项,本批未预占任何排期。 - **更新于**:2026-10-06(**迭代17五任务书联审修订落籍 master 68b8f15**,纯文档零代码零测试,工作日志 378):五个 subagent 并行审查五份任务书(卡0①②③④),挖出约 50 处问题、8 处开工即停机级;用户十裁(Q1-Q10)全落——卡0 v1.1 效果闭集扩 2 型+charge_full 事件+level_clear 归登记+对拍判据两套(确定性复放=逐位/MC 统计量=同带 m4iter12 口径)+映射表五处修正;卡① v1.1 **P3 改裁丙撤投放**(一裁乙与首遇教学位纪律三面冲突)+P1 补遭遇骰交互(每行一骰·RNG 扩签先报批)+实例总数≤4 消歧+R6 行序契约+F7 勘误 19 行 9 种,估时 3.75d;卡② v1.2 P3 转正顺延重扫读数后+检算工具 WEAKEN_PCT 入同步面+T3/T5 并账+估时勘误段一 4.25d;卡③ v1.1 R4 快照口径收敛+Q10 甲 badge 工厂化(营地徽迁入 ui_kit)+P1 丁 06 Esc 登记补办+基线换代 iter16ui 后;卡④ v1.1 F4 事实改正(recent_summaries 现役 cap=10,非「无历史记录面」)+P2 甲口径重述(扩容+补用时埋点)+总局数/通关局数涉 save v6;roadmap 四行+06 四处+07 两条义务(T7 判读带案C 复核+后续内容批投放六面)随批换代;**五卡零待裁点重建,开工门=U0**。 - **更新于**:2026-10-06(**迭代17四卡18项裁点全裁落籍 master d5df1f6**,纯文档零代码零测试,工作日志 376):卡①P1甲/P2甲/P3乙投放教学位行/P4乙否推荐Boss行亦放开(零内容投放不变,Boss多行内容归后续批);卡②P1甲维持2场·magnitude[60,85]当量锚复算/P2甲不对称有意/P3甲转正/P4甲不立直购价;卡③P1丁/P2甲/P3甲/P4甲;卡④甲×6;四任务书裁决回填+roadmap四行+06六处换代,**四卡全部零待裁点,开工门=U0**;四段发射prompt已交付chat(用户逐卡贴goal,卡0段本会话早前已交付)。 @@ -868,3 +869,4 @@ 379. **生涯界面设计提纲批完成(2026-10-06,迭代 17 卡④纯设计批——零代码、零数据面、零测试执行;worktree g-plan2-iter17-career 分支 iter17-career-20261006 基点 fc4dbde,提交 10df8ba 加批次收尾提交;本条为编排会话预配号,落籍序以合并为准)**:设计提纲写成正式设计文档 design/gdd/systems/跨局循环/生涯.md v0.1(状态=范围草案,2026-10-06 用户裁 P0 甲案)——五节写全:信息架构(主菜单一级入口与营地平级=P1 甲,局内不放入口、生涯内零跳转=P4 甲)、承载内容清单(跨局汇总/历史冒险回看/图鉴三段逐项标数据源与可行性,历史回看=P2 甲每局一行摘要加扩容加补用时埋点,图鉴=P3 甲敌人优先、奖励格图鉴只登记不激活,单局明细由 settlement_review_overlay 复活承接=P5 甲)、与既有界面的关系(营地/暂停层/结算屏/帮助手册/图例五界分工矩阵)、分期实装建议(期一汇总页/期二历史回看/期三图鉴,均为另行立项不预占排期,实装批前置义务=对齐迭代 16 UI 基线与 N 终值读数、用时埋点口径呈报)、待裁点(新发现 P6=营地底栏「最近冒险」条带去留,三案全列含淘汰依据,建议期二撤下归生涯——该条带与生涯历史回看读同一份存档摘要且拼行无上限,扩容后会把底栏撑爆,是本批复核发现的实际耦合)。任务书 §2 十条现状事实 F1-F10 逐条对照代码静态复核全绿(回看退役三处注记、settlement_review 遗留件在盘无引用、recent_summaries 现役 cap=10、用时零采集点、终身计数域缺失、营地最近冒险清单渲染行等全实证;一处既有滞后顺带记录=registry 存档条目版本注记停在 v4 而代码已 v5,不属本批范围移交后续对齐批)。同批落:02-系统清单.md 根索引补生涯行+CAREER 代号行+v1.12 版本行、06 生涯条状态行改「设计提纲已成文待评审」、roadmap 迭代 17 卡④行改已交付待评审、出口报告=design/qa/iter17-career-exit-report-2026-10-06.md(完成定义五条判定+事实复核差异记录+提交哈希)。**下一步待用户=评审设计提纲**(评审时随裁 P6);实装批另行立项。 +380. **暂停层只读入口批完成(2026-10-06,迭代 17 卡③功能性 UI 交互批——纯呈现零数值改动=零 MC;worktree g-plan2-iter17-pause 分支 iter17-pause-20261006,基点 44328b1,两会话接力:前任会话完成 T0/T1+组 B 代码后因额度中断,续跑会话收尾组 B 跑闸+T3-T5;本条为编排会话预配号,落籍序以合并为准)**:局内暂停层交付——战斗屏 HUD 右下角与关卡地图屏顶栏各一枚「暂停」常驻角标(P1 丁,debug 角标同形制紧凑钮;主菜单不误现),点击开只读层(P2 甲=登记 modal_stack 登记册设置层同档带 dim 遮罩、确认弹窗之前),层内=钥石余额徽+属性锻造三行已购等级+道具工坊六行解锁态+天赋树已购概览+「继续」关闭钮(P3 甲,行序复用营地屏同序),零购买零切换零写通路。badge 按 Q10 甲工厂化:UiKit 新增 KeystoneBadge 工厂件,营地顶栏钥石徽本地拼装同步迁工厂调用(观感零变化,消单点重复);零新素材零新色值零新字体。守卫=其他模态开着两角标置灰不可点(modal_stack 新增 has_open_excluding 排除式开态查询——地图屏自身开态不计;组合根 _on_pause_badge_pressed 处理器兜底+_sync_pause_entry 单一同步点挂模态开合单口与屏级互斥口五路径)。断言四面=ui_struct(pause 结构+只读性契约〔游戏语义信号十二名清单巡检,白名单外零连接,合法连接恰 2〕+零动效契约〔子树零 AnimationPlayer+源码零 tween〕)+ui_input case⑯(角标真点击开层/锁期挖掘零穿透/关后恢复无残留——R3)+ui_click(战斗屏开合守卫+地图屏一次性 case)+ui_contrast(钥石徽深墨/烛光黄对)+冒烟投影一致性 5 断言(展示值==MetaProgression 访问器逐项——R4)。续跑首轮两处 L1 探针红均系断言口径(全集巡检误判引擎布局生命线连接/域恰 2 枚时零无旗格可取),修正一轮全绿无复发。闸=冒烟 788/0(基线 783/0,+5 本批)+run_all 17 套+probe_gate --click 全绿+ui_layout exit0+doc 三闸零新增;存量移交两项(非本批产生,主区同红)=熵增闸 design/diagrams 越界〔00712d5 总图批落籍未同批修白名单〕+06/07 gdd_lint 存量注记红〔本分支零触碰〕。代拍板五项=关闭钮「继续」/层内行序营地同序/角标文案与落点/断言 case 命名/巡检信号清单(目检清单 F 节追认单)。出口报告=design/qa/iter17-pause-exit-report-2026-10-06.md;**人闸=用户实机目检** design/qa/iter17-pause-human-gate-checklist-2026-10-06.md(A-H 八节,重点=双屏入口/守卫态/只读性/零动效/排播队列排空副作用确认项 D2——开层瞬间待播明细行瞬时入带与「暂停」直觉之差系既有弹层同款行为,待用户确认可接受)。 diff --git a/design/gdd/06-待办清单.md b/design/gdd/06-待办清单.md index 672b3b5c..0c6b7388 100644 --- a/design/gdd/06-待办清单.md +++ b/design/gdd/06-待办清单.md @@ -279,9 +279,9 @@ **现状**:视觉终稿半已兑现——营地界面视觉终稿随 M-3 迭代 9 卡⑭落码(石纹全屏底+顶栏铭牌/钥石徽+三柜台列,前置样张闸过审);功能界面(营地入口/CampOverlay 三柜台/A 区顶栏钥石余额常驻/结算屏入账明细)2026-09-21 已落地。**剩余面=局内暂停层只读入口**:现行无暂停层,局内无可暂停查看钥石与解锁态的入口。 -**归属**:HUD 批(M-4,批未立项) +**归属**:HUD 批(M-4,批未立项)——已随迭代 17 卡③实施 -**状态**:待办(2026-10-03 06 清账批瘦身——视觉终稿半兑现销号,本条仅存暂停层残余)——**已编入 roadmap 迭代 17 卡③(2026-10-05——裁点已全裁〔2026-10-06 用户令全按推荐:P1丁/P2甲/P3甲/P4甲〕,任务书经同日联审修订 v1.1〔badge 工厂化裁决/基线换代/R4 口径收敛〕;Esc 快捷通道已按 P1 丁增补登记进本清单键盘/手柄条〔2026-10-06〕;待 U0:design/research/任务书-暂停层只读入口批-2026-10-05.md,美术红线=复用现行公共件零新素材)** +**状态**:✅ 代码面已交付待实机目检(2026-10-06,迭代 17 卡③暂停层只读入口批——worktree g-plan2-iter17-pause 分支 iter17-pause-20261006,提交 c08a901/fc9eb1e/d44fdd5+收口笔;P1丁常驻角标/P2甲设置层同档 dim 遮罩/P3甲余额+三柜台进度/P4甲零动效直开直关全落;badge 已按 Q10 甲工厂化入 ui_kit 并同步迁营地钥石徽;机检=冒烟 788/0+run_all 17 套+四探针全绿〔含输入锁 case⑯/只读巡检/零动效契约/钥石徽对比度对〕+doc 三闸零新增;**人闸=用户实机目检** design/qa/iter17-pause-human-gate-checklist-2026-10-06.md——过闸后本条销号;Esc 快捷通道维持本清单键盘/手柄条登记随键盘批办理) #### 第一关亮墨数字直画观感优化(2026-10-01 T17 目检体感登记) **现状**:迭代 9 T4 数字承载切换后第一关=亮墨三档直画(白亮字直接印棕色地板、无色牌垫底),机制可读性过闸(用户实机确认三档冷暖可分、凑近与离屏均可读);同日 T17 目检体感「表现和描述一样,但非常难看」——直画字与地板的组合观感待优化。第二/三关米色小牌形态未获同类反馈。 diff --git a/design/qa/iter17-pause-exit-report-2026-10-06.md b/design/qa/iter17-pause-exit-report-2026-10-06.md new file mode 100644 index 00000000..c4f652ad --- /dev/null +++ b/design/qa/iter17-pause-exit-report-2026-10-06.md @@ -0,0 +1,81 @@ +# 出口报告:暂停层只读入口批(迭代 17 卡③) + +> 批次:功能性 UI 交互新增(纯呈现零数值改动=零 MC;核心层零改动)。 +> worktree:`g-plan2-iter17-pause`(分支 `iter17-pause-20261006`,基点 = master 44328b1)。 +> 任务书:`design/research/任务书-暂停层只读入口批-2026-10-05.md`(v1.1 零待裁点,U0 已发;T0 基线回填已落任务书头注)。 +> 本报告按「吸收即死」纪律属中间文档,寿命=迭代寿命;结论已同步写进 06 / roadmap / active.md。 +> 执行备注:本批由两次会话接力完成——T0/T1 与组 B 大部系前任会话(因额度中断),断点续跑会话完成组 B 收尾(两处探针断言口径 L1 修正)与 T3-T5。 + +## 一、完成定义五条逐条判定(任务书 §1.2) + +| # | 判定项 | 判定 | 依据 | +|---|---|---|---| +| 1 | 入口可达 | ✅ | 战斗屏 HUD 右下角+关卡地图屏顶栏各一枚「暂停」常驻角标(P1 丁);主菜单零角标(`_sync_pause_entry` 以 `_level_active`∧屏态门可见性);ui_input case⑯ 角标真点击开层绿、ui_click 两屏 case 绿 | +| 2 | 只读数据正确 | ✅ | 冒烟 `_pause_overlay_projection` 5 断言(余额徽/锻造三行/工坊六行/天赋树概览 == MetaProgression 访问器逐项);结构探针只读巡检=层子树游戏语义信号连接白名单外零连接,合法连接恰 2(关闭钮+关层外发);层内唯一控件=「继续」 | +| 3 | 公共件复用 | ✅ | 全部构成件经 UiKit 工厂(modal_shell/panel_skinned/label/button/keystone_badge)+现行色板 token;badge 工厂件 Q10 甲收编入 ui_kit 并同步迁营地钥石徽(观感零变化);零新素材零新色值零新字体;ui_contrast 12 对含钥石徽深墨/烛光黄新对 | +| 4 | 模态纪律 | ✅ | PauseOverlay 登记进 main.gd 登记册设置层同档、确认弹窗之前(P2 甲);登记序=树序=z 序注记同步换代;带 dim 遮罩;开合走 `_open_modal`/`_close_modal` 单口;`has_open_excluding` 排除式开态查询(只读零副作用);ui_click 守卫 case 绿(他层开着=置灰+emit 兜底拒开) | +| 5 | 重闸全绿+人闸 | ✅(机检半)/⏳(人闸待用户) | 机检读数见 §三;实机目检清单=`design/qa/iter17-pause-human-gate-checklist-2026-10-06.md`(A-H 八节:双屏入口/守卫态/只读性数据正确/零动效与排播副作用观察/与设置面关系/代拍板追认/R4 口径披露/机检佐证),**人闸=用户实机目检** | + +## 二、任务卡完成态(T0-T5) + +| 卡 | 状态 | 提交/落点 | +|---|---|---| +| T0 | ✅(前任) | worktree 建立+基线回填任务书头注(冒烟 783/0·run_all 17 套·probe_gate 默认段绿·F1-F10 实核绿·迭代 15② 交集零命中);HEAD 时点=44328b1 | +| T1 | ✅(前任) | c08a901(组 A:pause_overlay.gd 本体+badge 工厂件收编+营地钥石徽迁移+main.gd 接线)+ fc9eb1e(pause_overlay.gd.uid 边车随批) | +| T2 | ✅(续跑会话收尾) | d44fdd5(组 B:两屏角标+has_open_excluding+守卫处理器与 _sync_pause_entry 单一同步点+四探针断言+冒烟投影断言+任务书 T0 注记随批)。前任中断于代码写完未跑闸态;续跑两处 L1 修正(见 §四)后全闸绿 | +| T3 | ✅(续跑会话) | 无代码改动,闸读数见 §三 | +| T4 | ✅(续跑会话) | `design/qa/iter17-pause-human-gate-checklist-2026-10-06.md` | +| T5 | ✅(续跑会话) | 本报告+06 状态行+roadmap 卡③行+active.md 快照/台账 380 | + +## 三、闸读数(T0 基线 → 批末) + +| 闸 | 基线(T0) | 批末 | 判 | +|---|---|---|---| +| 冒烟(run_smoke 全量) | 783/0 | **788/0**(+5=本批投影断言) | ✅ | +| run_all | 17 套全绿 | 17 套全绿(exit 0) | ✅ | +| ui_input_probe | 默认段绿 | **十七断言 PASS**(+⑯暂停层输入锁:真点击开层/锁期零穿透/关后恢复无残留) | ✅ | +| ui_struct_probe | 默认段绿 | 全屏零红(+pause 结构断言+只读性契约+零动效契约) | ✅ | +| ui_contrast_probe | 默认段绿 | **12 对测点 PASS**(+钥石徽深墨/烛光黄对,≥4.5:1) | ✅ | +| ui_click_probe | 轻闸三件外 | done=true 问题=0(+战斗屏开合守卫 case+地图屏一次性 case) | ✅ | +| ui_layout_probe | — | exit 0(观察型布局树工具,无断言判语) | ✅ | +| doc 三闸 | — | doc_link_audit 绿 / doc_term_audit 绿 / gdd_lint **存量红**(见 §五-2,本批零新增——06/07 两文件本分支零触碰) | ✅(零新增口径) | +| 熵增闸 | — | **存量红**(见 §五-1,主区同红佐证) | 移交披露 | +| MC | — | 零跑(纯呈现零数值改动——两入口铁规) | ✅ | + +## 四、L1 修正记录(T2 续跑首轮探针红两处,均为断言口径、非产品代码缺陷) + +1. **ui_struct_probe 只读巡检**:前任落码为「子树全控件 × get_signal_list() 全集」巡检——首跑把 Godot 容器系统对每控件自动建立的布局生命线连接(child_order_changed/item_rect_changed/visibility_changed/size_flags_changed/minimum/maximum_size_changed → 引擎内部脏标记方法)全数误判非法。修正=收敛为**游戏语义信号名清单**(pressed/button_down/button_up/toggled/text_changed/text_submitted/value_changed/item_selected/gui_input/focus_entered/focus_exited/closed 十二名)——这正是任务书 v1.1「巡检信号名清单 T2 落码时列全进探针」的本意;清单外任何新连接一律红须入册。 +2. **ui_input_probe case⑯ 取格**:原逻辑假设 case⑥ 后域内必余「无旗可挖格」——首跑实测踩中「域恰 2 枚」形态(flag_t 已插旗+dig_t 已揭开后域内零无旗格;旗不可拔=`flag_present` 拒绝在册)。修正=case 挪至全 case 之尾(板面扰动零波及)+域空时前沿移位兜底(真实点击 move 扩大可挖域,复用探针既有 `_pick_frontier` 手法)。修正后一轮全绿,无复发。 + +## 五、存量问题移交(均非本批产生,主区 master 同态) + +1. **熵增闸红:`design/diagrams` 子目录越界**——00712d5(2026-10-05 GDD 系统架构总图批)落籍时新增 `design/diagrams/`(JSON+HTML 两件)但未同批修订 CLAUDE.md Project Layout 与 `repo_structure_audit.py` 白名单,属该批收尾漏步;主区现跑同红已实证。处置建议=用户拍板「diagrams 收编为合法类别」或「两件归位」,随下一文档批或直接小批办理;本批不代修(最小批面)。 +2. **gdd_lint 存量红:06/07 两文件** R3 修订注记/删除线残留/R4 缺版本历史/R7 括号跨度超限——全为历史批次落文注记(本分支对两文件零 diff);doc 三闸「零新增」口径达成。 +3. **registry 存档版本注记滞后 v4→v5**——生涯批(台账 379)已登记移交,本批未触碰 registry,维持在册。 + +## 六、代拍板记录(白名单内决断,commit 留痕,目检可推翻——追认单见目检清单 F 节) + +| # | 项 | 决断 | 出处 | +|---|---|---|---| +| 1 | 关闭钮文案 | 「继续」(紧凑 90 宽返回钮,设置面先例形制) | T1 c08a901 | +| 2 | 层内行序 | 余额徽→属性锻造→道具工坊→天赋树(营地屏同序,MetaTable.FORGE_ATTRS 序) | T1 c08a901 | +| 3 | 角标文案与落点 | 「暂停」;战斗屏=HUD 右下角(角距 16px,debug 角标同形制);地图屏=顶栏 GP 徽右侧 | T2 d44fdd5 | +| 4 | 断言编号与 case 命名 | ui_input case⑯;ui_struct 契约 ⑧只读⑨零动效;冒烟 `_pause_overlay_projection` | T2 d44fdd5 | +| 5 | 只读巡检信号清单 | 十二名游戏语义信号(§四-1) | T2 d44fdd5 | + +## 七、提交哈希清单(worktree 内全部,含前任) + +| 提交 | 内容 | +|---|---| +| c08a901 | T1:暂停层组 A(pause_overlay 本体+badge 工厂收编+营地钥石徽迁移+main 接线) | +| fc9eb1e | T1:pause_overlay.gd.uid 边车随批 | +| d44fdd5 | T2:暂停层组 B(两屏角标+守卫+四探针断言+冒烟投影断言+任务书 T0 注记) | +| (收口提交) | T4+T5:目检清单+本报告+06 状态行+roadmap 卡③行+active.md | + +## 八、遗留人闸项 + +1. **实机目检**(唯一硬人闸):`design/qa/iter17-pause-human-gate-checklist-2026-10-06.md` A-H 八节,重点=双屏入口/守卫态/只读性/零动效/**排播队列排空副作用确认项**(D2——开层瞬间待播明细行瞬时入带,与「暂停」直觉之差,既有各弹层同款行为)。 +2. 存量移交三项(§五),均不阻塞本批。 +3. Esc 快捷通道=已在 06 键盘/手柄条登记(P1 丁裁点),随键盘批统一办理,本批不落码。 + +终态 = worktree 内全部提交,**未合并、未 push**(合并落回 master 由用户/编排会话执行)。 diff --git a/design/qa/iter17-pause-human-gate-checklist-2026-10-06.md b/design/qa/iter17-pause-human-gate-checklist-2026-10-06.md new file mode 100644 index 00000000..1698ccfe --- /dev/null +++ b/design/qa/iter17-pause-human-gate-checklist-2026-10-06.md @@ -0,0 +1,63 @@ +# 人闸目检清单:暂停层只读入口批(迭代 17 卡③)· 2026-10-06 + +> 执行=用户实机。启动:Godot 打开 `game/project.godot` 运行即可;逐项勾选,不过项随批登记返工。 +> 批区间:T0 基线 HEAD=44328b1 → 批末 d44fdd5(T1 c08a901 暂停层本体 / T2 d44fdd5 入口与断言)。 +> 任务书:`design/research/任务书-暂停层只读入口批-2026-10-05.md`(v1.1)。事实源=06-待办清单「解锁界面与钥石呈现的交互形态(残余:暂停层只读入口)」条。 + +## 怎么走到暂停层(操作路径) + +开始冒险 → 选一个战斗节点进战斗 → 战斗屏**右下角**「暂停」小钮(或:打完战斗到关卡地图屏 → 顶栏 GP 徽**右侧**「暂停」小钮)→ 弹出「暂停」只读层 → 看完点「继续」关闭。 + +## A · 双屏入口可达(P1 丁——常驻角标主入口) + +- [ ] A1 战斗屏 HUD 右下角有「暂停」角标,点击直接开层(无过渡动画) +- [ ] A2 关卡地图屏顶栏「暂停」角标可见可点,点击开层;层叠在地图屏之上,地图屏仍可见 +- [ ] A3 主菜单(开始菜单)**无**暂停角标——入口只在对局内出现 +- [ ] A4 冒险结束回主菜单后角标消失(离局熄灭,不误现) +- [ ] A5 暂停层开着时点「继续」原样回战斗屏/地图屏,可继续挖格/选路(无状态残留) + +## B · 入口守卫(他层开着=角标置灰不可点) + +- [ ] B1 战斗中先开图例(或触发三选一/商店/营地等任一弹层)→ 暂停角标置灰,点击无反应 +- [ ] B2 关闭该弹层后角标恢复可点 +- [ ] B3 地图屏自身开着时地图屏角标仍可点(地图屏是常态屏,不算「其他模态」) +- [ ] B4 暂停层自己开着时,再点任一处角标无反应(不可叠开第二层) + +## C · 只读性与数据正确(P3 甲——余额+三柜台进度) + +- [ ] C1 层内唯一可点控件=「继续」关闭钮;无任何购买/切换/滑杆类控件 +- [ ] C2 钥石余额徽与营地屏顶栏钥石徽同形制(烛光黄底+深墨字+钥石图标),数字=当前真实余额 +- [ ] C3 属性锻造三行(攻击/生命/防守族)= 已购等级「Lv n」,与营地柜台一读数一致 +- [ ] C4 道具工坊六行解锁态(未解锁/已解锁/已解锁·能力封存中/已退役)与累计通关进度分数,与营地柜台二一致 +- [ ] C5 天赋树概览=已购节点聚合文案(零已购时显示空态句「尚未购买任何节点」),与营地柜台三一致 +- [ ] C6 层开着时回营地柜台改数值(如买一级锻造)不可行(层无通路);关闭层后重新打开,显示为新值(实时读数) + +## D · 零动效与排播副作用观察(P4 甲 + T4 指定观察项) + +- [ ] D1 层进出均为直开直关——无淡入淡出/滑入等过渡动画 +- [ ] D2 **排播排空副作用**(确认项):战斗明细演播进行中(连续挖格后立刻)开暂停层——待播明细行会被瞬时排空入带(跳过演播节奏,行带直接补完整)。这是与「暂停」直觉的差异点:**本层只是查看层,不冻结战斗演算与演播**(既有各弹层同款行为)。确认此表现可接受,或登记异议 +- [ ] D3 减动效开关开/关两态下,层进出表现一致(本层零动效,开关无新增消费) + +## E · 与设置面的关系(P2 甲——登记同档) + +- [ ] E1 主菜单开设置面:无暂停角标出现(两层面不共存——设置入口在主菜单,暂停入口在对局内) +- [ ] E2 层级遮挡关系抽检:确认弹窗盖在暂停层之上、暂停层盖在图例/商店/营地之上(登记序=树序=遮挡序) + +## F · 代拍板追认单(白名单内已落,目检可推翻) + +| # | 项 | 已落值 | 推翻改法 | +|---|---|---|---| +| F1 | 关闭钮文案 | 「继续」(紧凑 90 宽返回钮,设置面先例形制) | 改「返回」等一词换文案一处 | +| F2 | 层内行序 | 余额徽 → 属性锻造 → 道具工坊 → 天赋树(营地屏同序) | 顺序对调=一处移动 | +| F3 | 角标文案与位置 | 「暂停」;战斗屏=HUD 右下角(角距 16px)、地图屏=顶栏 GP 徽右侧 | 换角/换角距=锚点一处 | +| F4 | 层标题与副题 | 「暂停」+「查看本局钥石与永久强化进度(只读——购买请回营地)」 | 文案一处 | +| F5 | 天赋树空态句 | 「尚未购买任何节点(天赋树在营地柜台三)」 | 文案一处 | +| F6 | 断言编号与 case 命名 | 探针 case⑯/struct 契约 ⑧⑨(机检面,不涉观感) | 仅重命名时改 | + +## G · 数据口径披露(R4 快照口径,机检佐证外的人闸知情项) + +暂停层展示值=**每次开层瞬间**实时读取元进度访问器拼装的快照(余额/锻造三行/工坊六行/天赋树概览七类访问器),快照只活到本次关层,重开重取——层开着期间外部数值不会变动(层内零写通路),不存在「层数字过期」问题。机检侧:冒烟投影一致性 5 断言(展示值==访问器逐项)+ 结构探针只读巡检(层子树游戏语义信号连接白名单外零连接,合法连接恰 2=关闭钮与关层外发)全绿。 + +## H · 机检佐证(已全绿,实机只抽核) + +冒烟 788/0(基线 783/0,+5=本批投影断言)· run_all 17 套全绿 · ui_input 17 断言(含⑯暂停层输入锁)/ui_struct 全屏零红(含 pause 结构+只读巡检+零动效契约)/ui_contrast 12 对测点(含钥石徽新对)/ui_click done=true 问题=0/ui_layout exit0 · doc 三闸零新增(06/07 存量注记红系历史批次落文,本批零触碰;design/diagrams 熵增越界系 00712d5 总图批落籍漏步,主区同红——两存量均已移交登记,非本批产生)· 素材零触碰(零新素材零新色值零新字体,全部构成件=UiKit 工厂件+现行色板 token)· MC 零跑(纯呈现零数值改动) diff --git a/design/research/任务书-暂停层只读入口批-2026-10-05.md b/design/research/任务书-暂停层只读入口批-2026-10-05.md index 3de46cc0..84a0ad7a 100644 --- a/design/research/任务书-暂停层只读入口批-2026-10-05.md +++ b/design/research/任务书-暂停层只读入口批-2026-10-05.md @@ -1,5 +1,7 @@ # 任务书:暂停层只读入口批(v1.1 定稿,零待裁点,开工门=U0 用户发令) +> **T0 基线回填(2026-10-06 执行会话实测,HEAD=44328b1)**:冒烟 **783/0**(§1.2 注记 782 系 ed38c54 时点参考值,卡④落籍后 +1,以本读数为对拍基线)· run_all **17 套全绿** · probe_gate 默认段(ui_input+ui_struct+ui_contrast)全绿。§2 事实表 F1-F10 逐条实核全绿(F2 登记册=main.gd:332、z 序注记=:242-243;F3 badge 工厂缺位+营地本地拼装=camp_overlay_view.gd:79-92;F5 访问器行号逐一符合、boss_kill_count=save_manager.gd:250 精确;F8 settings=main.gd:260;F10 dbgbtn=ui_assembler.gd:161-164)。**迭代 15② 交集核对:零命中**——卡②/卡③均已 2026-10-05 落籍(roadmap 现行口径,无在飞面);在飞 worktree 三件(iter17-dd 卡⓪〔尚无超前提交〕/uialign/standee〔均零 diff〕)与本批文件面零交集。 + > 成文 2026-10-05,v1.1 修订 2026-10-06(五任务书联审:R4 快照口径收敛、badge 工厂路径裁决〔Q10 甲〕、P1 丁的 06 Esc 登记补办、基线换代 iter16ui 批后、F5/F10 事实勘误、入口守卫与只读断言口径补列)。 > **性质**:功能性 UI 交互新增——局内暂停层,承载钥石余额与营地柜台解锁进度的**只读**查看。 > 事实源 = [06-待办清单](../gdd/06-待办清单.md)「解锁界面与钥石呈现的交互形态(残余:暂停层只读入口)」条。 diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 30b47ccb..6bc64d3c 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -117,6 +117,7 @@ var shop_overlay: ShopOverlay # 商店覆盖层(GP+商店房实施 var camp_overlay: CampOverlay # 营地覆盖层(元进度实现批——主菜单营地入口兑付呈现) var settings_store # 系统级设置独立存储(迭代 8 卡④——音量/全屏/减动效) var settings_overlay: SettingsOverlay # 设置覆盖层(迭代 8 卡④——最小设置面) +var pause_overlay: PauseOverlay # 局内暂停层·只读(迭代 17 卡③——钥石余额+营地三柜台进度查看;登记设置层同档) var debug_overlay: DebugOverlay # 调试浮窗(迭代 14.5 调试整合批——种子/数值调整/批量模拟/两层调试入口收一) var _modals = null # 模态层级状态机(modal_stack.gd——缺陷⑤纪律载体) @@ -223,6 +224,7 @@ func _build_ui() -> void: func() -> Variant: return settlement, func() -> Variant: return runner) board_zone.action_requested.connect(_on_board_action) + board_zone.pause_badge.pressed.connect(_on_pause_badge_pressed) # 暂停层入口(迭代 17 卡③——战斗屏 HUD 角标) info_panel.treasure_slot_clicked.connect(_treasure_ui.on_slot_clicked) # 迭代 7 T4——两步交互第一步 board_zone.stop_pressed.connect(_on_stop_pressed) board_zone.refresh_confirmed.connect(_on_refresh_confirmed) @@ -240,7 +242,7 @@ func _build_ui() -> void: logger.ap_per_round = int(db.action_economy.ap_per_round) # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 - # < 商店 < 营地 < 设置 < 确认弹窗 < 调试浮窗) + # < 商店 < 营地 < 设置 < 暂停(只读) < 确认弹窗 < 调试浮窗) var overlays: Dictionary = UiAssembler.build_overlays(self, db) legend_overlay = overlays.legend_overlay node_overlay = overlays.node_overlay @@ -255,11 +257,17 @@ func _build_ui() -> void: legend_overlay.closed.connect(_on_modal_closed.bind(legend_overlay)) node_overlay.continue_pressed.connect(_on_node_continue) level_screen.route_chosen.connect(_on_route_chosen) + level_screen.pause_badge.pressed.connect(_on_pause_badge_pressed) # 暂停层入口(迭代 17 卡③——地图屏顶栏角标) level_screen.abandon_requested.connect(_on_abandon_requested) # 放弃入口(2026-09-21 用户拍板——台账 119) _end_overlay.continue_pressed.connect(_on_battle_end_continue) settings_overlay = SettingsOverlay.new(settings_store) # 迭代 8 卡④——最小设置面(音量/全屏/减动效) add_child(settings_overlay) settings_overlay.closed.connect(_on_settings_closed) + # 局内暂停层·只读(迭代 17 卡③——P2 甲:登记设置层同档带 dim 遮罩,确认弹窗之前; + # 数据=组合根开层实时读 MetaProgression 访问器喂入(R4),本层零写通路) + pause_overlay = PauseOverlay.new() + add_child(pause_overlay) + pause_overlay.closed.connect(_on_pause_closed) # 提升三选一覆盖层(迭代 10——z 序 = 节点层之上、终局层之下;确认后同拍推进) curio_overlay = CurioPickOverlay.new() add_child(curio_overlay) @@ -327,9 +335,10 @@ func _build_ui() -> void: debug_overlay.simulate_requested.connect(_on_tune_simulate) debug_overlay.debug_levels_requested.connect(_start_debug_levels) - # 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位) + # 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位; + # 暂停层设置层同档、确认弹窗之前——P2 甲) _modals = ModalStack.new() - for m: Control in [legend_overlay, node_overlay, level_screen, curio_overlay, _end_overlay, shop_overlay, camp_overlay, settings_overlay, confirm_dialog, debug_overlay]: + for m: Control in [legend_overlay, node_overlay, level_screen, curio_overlay, _end_overlay, shop_overlay, camp_overlay, settings_overlay, pause_overlay, confirm_dialog, debug_overlay]: _register_modal(m) _tune_sim = TuneSimController.new(db, debug_overlay, int(db.hero.max_hp)) @@ -360,10 +369,12 @@ func _open_modal(m: Control) -> void: # 打开时显式隐藏树序在其上方的可见层(遮挡物隐藏),关闭恢复——单一模态栈 _flush_row_queue() # 卡⑦——任何覆盖层开启前排空排播队列(明细带恒完整,回看/停点面不缺行) _modals.open(m) + _sync_pause_entry() # 迭代 17 卡③——暂停层入口角标可见性/置灰随开态同步 func _close_modal(m: Control) -> void: _modals.close(m) + _sync_pause_entry() # 迭代 17 卡③——关闭恢复角标可点态(无残留——R3 同源口径) func _on_modal_closed(m: Control) -> void: @@ -429,6 +440,7 @@ func _resume_from_menu() -> void: _start_overlay.visible = false _wire_adventure() _level_active = true + _sync_pause_entry() # 迭代 17 卡③——续玩恢复点亮暂停入口角标 level_screen.set_abandon_available(true) _reset_run_scope() log_view.clear() @@ -463,6 +475,7 @@ func _wire_adventure() -> void: func _start_adventure() -> void: _wire_adventure() _level_active = true + _sync_pause_entry() # 迭代 17 卡③——进对局点亮暂停入口角标 level_screen.set_abandon_available(true) # 放弃入口挂选路停点(天然非战斗期——2026-09-21 用户拍板) _reset_run_scope() log_view.clear() @@ -539,6 +552,7 @@ func _on_adventure_finished(summary: Dictionary) -> void: var result: String = String(summary.get("result", "")) settlement = null _exit_level_screen() # 终局兜底:独立屏在开态(放弃入口)也先回战斗屏呈现摘要——屏级互斥单一出口 + _sync_pause_entry() # 迭代 17 卡③——离局熄灭暂停入口角标(防菜单误现) _sync_settlement_visuals() # → 全关 HudPresenter.battle_end_feedback(stage, ap_bar, false) # 灰化/idle 回正/状态全清——HUD 单通道(T3 收口批) # 存档入账(元进度实现批):settle = Boss 两档入账 + 工坊解锁 + 摘要入档 + 写盘(唯一写档点不变—— @@ -889,6 +903,7 @@ func _enter_level_screen() -> void: _level_screen_active = true _battle_root.visible = false _open_modal(level_screen) + _sync_pause_entry() # 迭代 17 卡③——屏级互斥切换同步(_open_modal 单口不识屏级状态) func _exit_level_screen() -> void: @@ -896,6 +911,7 @@ func _exit_level_screen() -> void: _battle_root.visible = true if level_screen.visible: _close_modal(level_screen) + _sync_pause_entry() # 迭代 17 卡③——同上 func _on_abandon_requested() -> void: @@ -924,6 +940,61 @@ func _on_settings_closed() -> void: _refresh_resume_button() +# ---- 局内暂停层·只读(迭代 17 卡③——入口角标在两屏装配段〔组 B〕;开合与数据喂入归组合根)---- + +# 暂停层快照投影(R4 数据只读同源:每次开层实时读 MetaProgression 访问器现值拼装—— +# 快照仅服务单次开层生命周期(open_pause 重建各行),跨开层零持有;零写调用) +func _pause_data() -> Dictionary: + var forge_rows: Array[Dictionary] = [] + for attr in MetaTable.FORGE_ATTRS: + var row: Dictionary = db.meta_forge_row(attr) + forge_rows.append({ + "attr": attr, + "display_name": String(row.get("display_name", "")), + "level": meta.forge_level(attr), + "flat": int(row.get("flat_value", 0)), + }) + return { + "balance": meta.keystone_balance(), + "forge": forge_rows, + "workshop": meta.workshop_status(), + "tree": meta.tree_camp_rows(), + "fork_groups": meta.tree_fork_groups(), + } + + +func _open_pause_layer() -> void: + _open_modal(pause_overlay) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项) + pause_overlay.open_pause(_pause_data()) + + +func _on_pause_closed() -> void: + _close_modal(pause_overlay) # 模态栈单口 + + +# 入口角标点击(两屏同款处理器):P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰, +# 探针 pressed.emit 绕过 disabled 时由此兜住;地图屏自身的登记开态不计——has_open_excluding) +func _on_pause_badge_pressed() -> void: + var map_on: bool = level_screen != null and level_screen.visible + if _modals == null or (map_on and _modals.has_open_excluding(level_screen)) \ + or (not map_on and _modals.has_open()): + return + _open_pause_layer() + + +# 入口角标可见性/置灰同步(P1 丁+守卫——单一同步点):战斗屏角标=对局内且地图屏未激活; +# 地图屏角标=随屏;置灰=登记册上除地图屏自身外任一层开着(防「看得见点不着」态)。 +# 调用面=模态开合单口与屏级互斥口(凡影响开态/屏态的路径必经其一)。 +func _sync_pause_entry() -> void: + if pause_overlay == null or _modals == null or board_zone == null or level_screen == null: + return + var map_on: bool = level_screen.visible + board_zone.pause_badge.visible = _level_active and not map_on + level_screen.pause_badge.visible = map_on + board_zone.pause_badge.disabled = _modals.has_open() + level_screen.pause_badge.disabled = _modals.has_open_excluding(level_screen) + + # ---- 探针缝保缝(T4/D4 拆件——click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.gd)---- func _on_camp_closed() -> void: diff --git a/game/presentation/modal_stack.gd b/game/presentation/modal_stack.gd index 642cf409..80f1ac55 100644 --- a/game/presentation/modal_stack.gd +++ b/game/presentation/modal_stack.gd @@ -41,3 +41,15 @@ func has_open() -> bool: if is_instance_valid(m) and m.visible: return true return false + + +# 排除式开态查询(迭代 17 卡③ 2026-10-06——暂停层入口守卫判据):skip 层自身的开态不计入 +#(关卡地图屏系登记册模态成员=常态屏态,其上的地图屏角标必须可点;其余任一层开着=角标置灰)。 +# 只读查询零副作用——登记序/开合记账语义零改动。 +func has_open_excluding(skip: Control) -> bool: + for m in _list: + if m == skip: + continue + if is_instance_valid(m) and m.visible: + return true + return false diff --git a/game/presentation/views/board_zone_view.gd b/game/presentation/views/board_zone_view.gd index 776ce1a1..301bf3bb 100644 --- a/game/presentation/views/board_zone_view.gd +++ b/game/presentation/views/board_zone_view.gd @@ -11,6 +11,7 @@ var ap_bar: ApBarView var board_view: BoardView var toast: ToastStack var fx: BoardFxView +var pause_badge: Button # 暂停层入口角标(迭代 17 卡③ P1 丁——debug 角标同形制;连接归组合根) var _board_area: Control # resized 命名转发用(原 lambda 局部捕获——P1-3 转命名 2026-09-29) signal action_requested(cell: Vector2i, kind: String) @@ -41,6 +42,15 @@ func _init() -> void: board_area.add_child(fx) toast = ToastStack.new() board_area.add_child(toast) + # 暂停层入口角标(迭代 17 卡③ P1 丁——战斗屏 HUD 右下角常驻;debug 角标同形制:紧凑钮豁免 + # 宽度基准+字号降档+角距 16px。可见性/置灰归组合根 _sync_pause_entry(其他模态开着=置灰 + # 不可点——has_open 守卫);信号连接归组合根(ADR-0001——装配器/视图零连接) + pause_badge = UiKit.button("暂停", 0.0) # 角落小件豁免宽度基准(同调试角标) + pause_badge.add_theme_font_size_override("font_size", UiKit.FS_HINT) + pause_badge.tooltip_text = "查看钥石与永久强化进度(只读)" + pause_badge.visible = false # 显式初始化——非对局期不误现(可见性归组合根同步) + pause_badge.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_RIGHT, Control.PRESET_MODE_MINSIZE, 16) + board_area.add_child(pause_badge) # M8(UI 落码批):棋盘底常驻操作提示条(demo ⑦——文案经 board_view 输入代码证实; # 旧 chord 一次性 toast 提示退役归此常驻条;审计第 7 条用户批) var hint := PanelContainer.new() diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index c042df7f..8a910815 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -76,20 +76,12 @@ func _init() -> void: var spacer := Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL top.add_child(spacer) - var badge := PanelContainer.new() - # M3(UI 落码批 2026-10-04):钥石徽章烛光黄底+深墨字(demo ②——旧暗底 CAMP_BADGE_PANEL 退役; - # BADGE_PANEL_COL 常量保留他用不动) - badge.add_theme_stylebox_override("panel", - UiKit.panel_flat(UiKit.CANDLE, UiKit.CAMP_HEADER_BAND,UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8))) + # M3(UI 落码批):钥石徽章烛光黄底+深墨字(demo ②——旧暗底 CAMP_BADGE_PANEL 退役; + # BADGE_PANEL_COL 常量保留他用不动)。Q10 甲(2026-10-06):本地拼装迁 UiKit.keystone_badge + # 工厂调用(badge 单点化——暂停层同款消费);形制参数逐项同原装配,观感零变化 + var badge := UiKit.keystone_badge("钥石 0") top.add_child(badge) - var bal_row := HBoxContainer.new() - bal_row.add_theme_constant_override("separation", 6) - badge.add_child(bal_row) - var keystone_icon := UiKit.icon_rect(UiKit.TEX_ICON_KEYSTONE, 32) # P6 甲②(2026-10-06):64 源 0.5×(原 20=0.3125× 违 §8.2);缺位 null → 纯文本回退 - if keystone_icon != null: - bal_row.add_child(keystone_icon) - _balance_label = UiKit.label("钥石 0",UiKit.FS_SUB, true, UiKit.INK_ON_GOLD) - bal_row.add_child(_balance_label) + _balance_label = badge.value_label # ── 三柜台列(方向稿同构:头带深棕+米色板身)── # 三柜等宽分配(2026-10-06 用户实测缺陷修——原定宽 356+行标签按文字全宽算 min,进度行文案 diff --git a/game/presentation/views/level_map_screen.gd b/game/presentation/views/level_map_screen.gd index 50bf204e..a627361b 100644 --- a/game/presentation/views/level_map_screen.gd +++ b/game/presentation/views/level_map_screen.gd @@ -61,6 +61,7 @@ var _cand_ids: Array[int] = [] # 本停点候选 id 集(图上命 var _content_size := Vector2(620, 240) # 图内容包络(open_choices 按图拟合——画布 min 尺寸随它) var _hover_id := -1 # 悬停预承诺候选(-1 = 无) var abandon_btn: Button # 放弃入口(可见性归组合根 set_abandon_available——只在冒险模式开) +var pause_badge: Button # 暂停层入口角标(迭代 17 卡③ P1 丁——顶栏 GP 徽右侧;连接归组合根) var _abandon_timer := ArmedTimer.new() # 放弃确认窗(T3/D3 P4 乙——_process 帧驱动倒计时机制收口;窗口=CONFIRM_WINDOW_S) var _node_icons := {} # 节点图标贴图(load_tex 守卫——缺位 null → 字形回退,冒烟/无素材检出安全) var _noise_rect: TextureRect # 屏底石纹(换层色板渐变承载面——null = 纯色回退无挂点) @@ -112,6 +113,14 @@ func _init() -> void: gp_box.add_child(gp_row) _gp_value = UiKit.label("",UiKit.FS_BODY, true, UiKit.INK_ON_GOLD) gp_row.add_child(_gp_value) + # 暂停层入口角标(迭代 17 卡③ P1 丁——顶栏常驻;debug 角标同形制:紧凑钮豁免宽度基准+字号降档。 + # 置灰守卫归组合根 _sync_pause_entry(其他模态开着=不可点;本屏自身的登记开态不计——has_open_excluding); + # 信号连接归组合根(ADR-0001——视图零连接)) + pause_badge = UiKit.button("暂停", 0.0) + pause_badge.add_theme_font_size_override("font_size", UiKit.FS_HINT) + pause_badge.tooltip_text = "查看钥石与永久强化进度(只读)" + pause_badge.size_flags_vertical = Control.SIZE_SHRINK_CENTER + top.add_child(pause_badge) _title = UiKit.label(TITLE_FORK,UiKit.FS_SUB, true, UiKit.WARM) _title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(_title) diff --git a/game/presentation/views/pause_overlay.gd b/game/presentation/views/pause_overlay.gd new file mode 100644 index 00000000..feaa36b2 --- /dev/null +++ b/game/presentation/views/pause_overlay.gd @@ -0,0 +1,166 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 局内暂停层·只读(迭代 17 卡③ 暂停层只读入口批 2026-10-06——任务书 v1.1;事实源=06-待办清单 +# 「解锁界面与钥石呈现的交互形态(残余:暂停层只读入口)」)。 +# 裁决落形:P1 丁=入口为战斗屏 HUD / 关卡地图屏常驻角标(入口装配在 ui_assembler/两屏,本类不含入口); +# P2 甲=登记 modal_stack 设置层同档(确认弹窗之前)带 dim 遮罩;P3 甲=展示钥石余额+三柜台进度 +# (属性锻造三行已购等级 / 道具工坊六件解锁态 / 天赋树已购概览——层内行序与分组复用营地屏同序); +# P4 甲=进出零动效直开直关(无 tween 无 AnimationPlayer——R5)。 +# 只读契约:R4 数据只读同源——账面快照由组合根 open_pause 喂入(每次开层实时读 MetaProgression +# 访问器重建,快照仅用单次开层生命周期,跨开层零持有);本类零核心访问、零写通路——唯一信号 +# closed(关闭意图,组合根经模态栈单口收口)。R1 公共件单点:全部构成件经 UiKit 工厂产出 +#(modal_shell/panel_skinned/label/button/keystone_badge),零裸色值零自建样式。 +class_name PauseOverlay +extends Control + +signal closed + +var _balance_badge: UiKit.KeystoneBadge # 钥石余额徽(badge 工厂件——Q10 甲) +var _forge_holder: VBoxContainer +var _workshop_holder: VBoxContainer +var _tree_label: Label + + +func _init() -> void: + var shell: Dictionary = UiKit.modal_shell(self, Color(UiKit.DIM, 0.55), Vector2(560, 0), 10, true, + UiKit.panel_skinned(Vector4(20, 16, 20, 16))) # 石板皮(缺位回退数据板——同设置面先例) + var box: VBoxContainer = shell.box + + var title := UiKit.label("暂停",UiKit.FS_TITLE, true, UiKit.INK_TITLE) + title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + box.add_child(title) + var hint := UiKit.label("查看本局钥石与永久强化进度(只读——购买请回营地)",UiKit.FS_HINT, false, UiKit.INK_HINT) + hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + box.add_child(hint) + + # 钥石余额行(badge 工厂件——与营地顶栏钥石徽同形制) + var bal_row := HBoxContainer.new() + bal_row.alignment = BoxContainer.ALIGNMENT_CENTER + box.add_child(bal_row) + _balance_badge = UiKit.keystone_badge("钥石 0") + bal_row.add_child(_balance_badge) + + box.add_child(HSeparator.new()) + _section_head(box, "属性锻造 · 已购等级") + _forge_holder = _section_holder(box) + + _section_head(box, "道具工坊 · 解锁态") + _workshop_holder = _section_holder(box) + + _section_head(box, "天赋树 · 已购概览") + _tree_label = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_LABEL) + _tree_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + box.add_child(_tree_label) + + box.add_child(HSeparator.new()) + var close_row := HBoxContainer.new() + close_row.alignment = BoxContainer.ALIGNMENT_CENTER + box.add_child(close_row) + var close_btn := UiKit.button("继续", 90.0) # 紧凑返回钮(设置面先例形制;文案=代拍板白名单「继续」级措辞) + close_btn.tooltip_text = "关闭暂停层,继续当前冒险(本层只读,不发生任何购买或修改)" + close_btn.pressed.connect(_emit_closed) + close_row.add_child(close_btn) + visible = false # 显式初始化——不靠树序巧合(缺陷⑤家族纪律) + + +# 分组小节头(营地屏分组同构——fs 副标题档深墨) +func _section_head(box: VBoxContainer, text: String) -> void: + var head := UiKit.label(text,UiKit.FS_BODY, true, UiKit.INK_LABEL) + box.add_child(head) + + +func _section_holder(box: VBoxContainer) -> VBoxContainer: + var holder := VBoxContainer.new() + holder.add_theme_constant_override("separation", 4) + box.add_child(holder) + return holder + + +# ---- 开层(组合根唯一调用口——快照随开随建,R4 单次开层生命周期口径)---- + +func open_pause(data: Dictionary) -> void: + _balance_badge.set_text("钥石 %d" % int(data.get("balance", 0))) + _rebuild_forge(data) + _rebuild_workshop(data) + _rebuild_tree(data) + visible = true + + +# 属性锻造三行(营地屏同序同文案族——名称+每级 flat / 已购等级;只读零价格零按钮) +func _rebuild_forge(data: Dictionary) -> void: + for c in _forge_holder.get_children(): + c.queue_free() + for row in (data.get("forge", []) as Array): + var line := HBoxContainer.new() + line.add_theme_constant_override("separation", 8) + var name_l := UiKit.label("%s %+d/级" % [String(row.get("display_name", "")), int(row.get("flat", 0))],UiKit.FS_HINT, true, UiKit.INK) + name_l.custom_minimum_size = Vector2(150, 0) + name_l.clip_text = true + name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + line.add_child(name_l) + var spacer := Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + line.add_child(spacer) + var lvl_l := UiKit.label("Lv %d" % int(row.get("level", 0)),UiKit.FS_HINT, true, UiKit.INK_TITLE) + line.add_child(lvl_l) + _forge_holder.add_child(line) + + +# 道具工坊六行(解锁态文案与营地柜台同口径——退役/封存/已解锁/未解锁;进度=解锁判定量读数) +func _rebuild_workshop(data: Dictionary) -> void: + for c in _workshop_holder.get_children(): + c.queue_free() + for row in (data.get("workshop", []) as Array): + var line := HBoxContainer.new() + line.add_theme_constant_override("separation", 8) + var name_l := UiKit.label(String(row.get("display_name", "")),UiKit.FS_HINT, true, UiKit.INK) + name_l.custom_minimum_size = Vector2(120, 0) + name_l.clip_text = true + name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + line.add_child(name_l) + var prog_l := UiKit.label("累计通关 %d/%d 次" % [int(row.get("progress", 0)), int(row.get("threshold", 0))],UiKit.FS_HINT, false, UiKit.INK_HINT) + prog_l.custom_minimum_size = Vector2(150, 0) + prog_l.clip_text = true + prog_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + line.add_child(prog_l) + var st_l := UiKit.label(_workshop_state_text(row),UiKit.FS_HINT, true, UiKit.INK_LABEL) + st_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL + line.add_child(st_l) + _workshop_holder.add_child(line) + + +# 解锁态文案(camp_overlay_view.refresh_camp 同款判据——退役 > 封存 > 已解锁 > 未解锁) +func _workshop_state_text(row: Dictionary) -> String: + if bool(row.get("retired", false)): + return "已退役" + if bool(row.get("deferred", false)) and bool(row.get("unlocked", false)): + return "已解锁 · 能力封存中" + if bool(row.get("unlocked", false)): + return "已解锁" + return "未解锁" + + +# 天赋树已购概览(单条聚合文案——已购节点等级 + Fork 组激活侧;零已购=空态句) +func _rebuild_tree(data: Dictionary) -> void: + var owned: Array[String] = [] + for row in (data.get("tree", []) as Array): + var lvl := int(row.get("level", 0)) + if lvl > 0: + owned.append("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, int(row.get("cap", 1))]) + for fg in (data.get("fork_groups", []) as Array): + var active := String(fg.get("active_side", "")) + if active == "": + continue + var side_name := active + for sd in (fg.get("sides", []) as Array): + if String(sd.get("side", "")) == active: + side_name = String(sd.get("display_name", active)) + break + owned.append("%s·激活 %s" % [String(fg.get("display_name", "")), side_name]) + if owned.is_empty(): + _tree_label.text = "尚未购买任何节点(天赋树在营地柜台三)" + else: + _tree_label.text = "已购 %d 项:%s" % [owned.size(), " · ".join(owned)] + + +func _emit_closed() -> void: + closed.emit() diff --git a/game/presentation/views/pause_overlay.gd.uid b/game/presentation/views/pause_overlay.gd.uid new file mode 100644 index 00000000..10fa85f8 --- /dev/null +++ b/game/presentation/views/pause_overlay.gd.uid @@ -0,0 +1 @@ +uid://dgmv0kar3ualk diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index be6846d1..96d22fd0 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -503,6 +503,34 @@ static func icon_rect(tex_path: String, px: float) -> TextureRect: return tr +# 钥石徽(badge 工厂件——Q10 甲收编 2026-10-06:原营地顶栏钥石徽本地拼装〔camp_overlay_view〕 +# 参数化收编,消费方 = 营地覆盖层 + 暂停层钥石余额行(迭代 17 卡③)。形制 = 烛光黄底+木色描边 +# +钥石图标(icon_rect 守卫装载,缺位=纯文本回退)+深墨字;零新素材零新色值——值全取现行 token) +class KeystoneBadge: + extends PanelContainer + var value_label: Label + + func _init(text: String, icon_path: String, icon_px: float) -> void: + add_theme_stylebox_override("panel", + UiKit.panel_flat(UiKit.CANDLE, UiKit.CAMP_HEADER_BAND, UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8))) + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 6) + add_child(row) + if icon_path != "": + var ic := UiKit.icon_rect(icon_path, icon_px) + if ic != null: + row.add_child(ic) + value_label = UiKit.label(text, UiKit.FS_SUB, true, UiKit.INK_ON_GOLD) + row.add_child(value_label) + + func set_text(t: String) -> void: + value_label.text = t + + +static func keystone_badge(text: String, icon_path := TEX_ICON_KEYSTONE, icon_px := 32.0) -> KeystoneBadge: + return KeystoneBadge.new(text, icon_path, icon_px) + + # 按钮皮(常态/按压两态+墨字——缺位 = 引擎默认主题,素材批前逐位一致) static func _apply_button_skin(b: Button) -> void: var tex_n := load_tex(TEX_BUTTON_STONE) diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index 9efd35de..5239d316 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -4112,6 +4112,82 @@ func _smoke_meta() -> void: _meta_pricing_and_buy() _meta_settle_and_workshop() _meta_injection() + _pause_overlay_projection() + + +# ---- 暂停层只读投影一致性(迭代 17 卡③——R4 数据只读同源:暂停层展示值逐项 == MetaProgression +# 访问器读数;投影拼装与 main._pause_data 同构(同访问器族同序)。只读性连接巡检与零动效 +# 断言归 ui_struct_probe 呈现侧,本段只管数据面)---- +func _pause_overlay_projection() -> void: + print("--- 暂停层·只读投影一致性(隔离档 smoke_meta_buy 复用)---") + _meta_smoke_cleanup() + var sv = _meta_new_save(_META_SMOKE_PATHS[0]) + var mp = load("res://core/meta_progression.gd").new(_db, sv) + # 已购态夹具:余额/锻造两档非零 + 工坊首件解锁——投影可辨非全零白板 + sv.add_keystone(37) + sv.set_forge_level("atk", 2) + sv.set_forge_level("max_hp", 1) + var ws_meta: Array = _db.meta_workshop_items() + if not ws_meta.is_empty(): + sv.unlock_item(String(ws_meta[0].get("item_id", ""))) + sv.write_to_disk() + # 投影拼装(main._pause_data 同构)+ 视图喂入 + var data := {"balance": mp.keystone_balance(), "forge": [], "workshop": mp.workshop_status(), + "tree": mp.tree_camp_rows(), "fork_groups": mp.tree_fork_groups()} + for attr in MetaTable.FORGE_ATTRS: + var frow: Dictionary = _db.meta_forge_row(attr) + data["forge"].append({"attr": attr, "display_name": String(frow.get("display_name", "")), + "level": mp.forge_level(attr), "flat": int(frow.get("flat_value", 0))}) + var po = load("res://presentation/views/pause_overlay.gd").new() + po.open_pause(data) + check(po.visible, "暂停层开层可见(open_pause 直开——P4 甲零动效路径)") + check(String(po._balance_badge.value_label.text) == "钥石 %d" % mp.keystone_balance(), + "余额徽投影 = keystone_balance() 读数(%s)" % po._balance_badge.value_label.text) + var forge_ok: bool = po._forge_holder.get_child_count() == MetaTable.FORGE_ATTRS.size() + var fi := 0 + for attr in MetaTable.FORGE_ATTRS: + if fi >= po._forge_holder.get_child_count(): + forge_ok = false + break + var lbls: Array = [] + for ch in po._forge_holder.get_child(fi).get_children(): + if ch is Label: + lbls.append(ch) + forge_ok = forge_ok and lbls.size() == 2 \ + and String(lbls[0].text).begins_with(String(_db.meta_forge_row(attr).get("display_name", ""))) \ + and String(lbls[1].text) == "Lv %d" % mp.forge_level(attr) + fi += 1 + check(forge_ok, "锻造三行投影逐行 = display_name + forge_level() 读数(已购夹具 atk Lv2 / max_hp Lv1 在列)") + var ws: Array = mp.workshop_status() + var ws_ok: bool = po._workshop_holder.get_child_count() == ws.size() + for i in mini(ws.size(), po._workshop_holder.get_child_count()): + var row: Dictionary = ws[i] + var lbls2: Array = [] + for ch in po._workshop_holder.get_child(i).get_children(): + if ch is Label: + lbls2.append(ch) + var unlocked: bool = bool(row.get("unlocked", false)) + var expect_st := "未解锁" + if bool(row.get("retired", false)): + expect_st = "已退役" + elif bool(row.get("deferred", false)) and unlocked: + expect_st = "已解锁 · 能力封存中" + elif unlocked: + expect_st = "已解锁" + ws_ok = ws_ok and lbls2.size() == 3 \ + and String(lbls2[0].text) == String(row.get("display_name", "")) \ + and String(lbls2[1].text) == "累计通关 %d/%d 次" % [int(row.get("progress", 0)), int(row.get("threshold", 0))] \ + and String(lbls2[2].text) == expect_st + check(ws_ok, "工坊六行投影 = workshop_status() 逐项(名称/判定量进度/解锁态——首件解锁夹具在列)") + var owned := 0 + for trow in (mp.tree_camp_rows() as Array): + if int(trow.get("level", 0)) > 0: + owned += 1 + var tree_txt := String(po._tree_label.text) + check(tree_txt == "尚未购买任何节点(天赋树在营地柜台三)" or tree_txt.begins_with("已购 %d 项:" % owned), + "天赋树概览聚合文案与已购节点数一致(零已购=空态句)") + po.visible = false + po.free() # 冒烟隔离存档名册(段首清陈旧 + 段尾清理——契约:测试不污染 user://save.json 真实档) diff --git a/game/smoke/ui_click_probe.gd b/game/smoke/ui_click_probe.gd index 0fa541e2..99b315c9 100644 --- a/game/smoke/ui_click_probe.gd +++ b/game/smoke/ui_click_probe.gd @@ -27,6 +27,8 @@ var _tested_review := false var _tested_end_review := false # 迭代 6 T7——终局面板回看入口(z 序修复回归) var _tested_refresh_chain := false var _tested_tooltip := false +var _tested_pause := false # 迭代 17 卡③——暂停层角标开合/守卫断言(每 run 一次) +var _tested_map_pause := false # 迭代 17 卡③——地图屏角标一次性 case(选路态零扰动) var _inter_confirms := 0 # 大奖励屏确认计数(冒险推进停点——首见确认推进 L2,再见 = L2 通关收口) var _settlement_confirms := 0 # 卡⑥ 胜利结算框确认计数(观测面——每场普通战斗胜利恰一) var _route_clicks := 0 # 选路覆盖层点击计数(岔路使用率观测面) @@ -220,6 +222,22 @@ func _ready() -> void: continue if main.level_screen.visible: # 节点图选路口(零自动推进停点)——图上命中路径交替选候选(岔路使用行为;布局决策 2 改造) + # 迭代 17 卡③——地图屏角标一次性 case:选路前经角标开/关暂停层(层在地图屏之上开合, + # 关闭后地图屏选路态零扰动——P2 甲层级语义) + if not _tested_map_pause and not main.pause_overlay.visible: + _tested_map_pause = true + var mpb: Button = main.level_screen.pause_badge + if mpb == null or not mpb.visible or mpb.disabled: + _problems.append("地图屏暂停角标缺失/未点亮/被置灰(P1 丁)") + else: + mpb.pressed.emit() + if not main.pause_overlay.visible or not main.level_screen.visible: + _problems.append("地图屏角标开暂停层失败或地图屏被误藏(P2 甲层级失守)") + var rbtn2: Button = ProbeUiUtil.find_button(main.pause_overlay, "继续", "prefix") + if rbtn2 != null: + rbtn2.pressed.emit() + if main.pause_overlay.visible or not main.level_screen.visible: + _problems.append("暂停层关闭后地图屏态失守(恢复语义破)") main.level_screen.press_candidate(_route_clicks % 2) _route_clicks += 1 _flags_placed.clear() @@ -479,6 +497,39 @@ func _run_modal_probes_once() -> void: if main.legend_overlay.is_open() or main._input_locked(): _problems.append("图例关闭后未恢复输入/可见性") # ②/②b 明细回看模态探针退役(M9/P-C7——冒险中回看归「生涯」,06 登记) + # ⑤ 暂停层(迭代 17 卡③——P1 丁角标真实信号路径 + P2 甲模态语义 + P1 守卫 + R3 锁恢复): + # 角标打开 → 只读层可见+输入锁定+余额徽非空 → 「继续」关闭恢复;图例开着 → 角标置灰且点击被守卫兜住 + if not _tested_pause and not main.legend_overlay.is_open() and not main.pause_overlay.visible: + _tested_pause = true + var pbtn: Button = main.board_zone.pause_badge + if pbtn == null or not pbtn.visible or pbtn.disabled: + _problems.append("战斗屏暂停角标缺失/未点亮/被置灰(P1 丁入口失守 visible=%s disabled=%s)" + % [str(pbtn != null and pbtn.visible), str(pbtn != null and pbtn.disabled)]) + else: + pbtn.pressed.emit() # 真实按钮信号路径 → 组合根 _on_pause_badge_pressed + if not main.pause_overlay.visible: + _problems.append("暂停层未随角标打开(组合根接线失守)") + if not main._input_locked(): + _problems.append("暂停层打开时输入未锁定(R3)") + var bal_txt: String = main.pause_overlay._balance_badge.value_label.text + if not bal_txt.begins_with("钥石 "): + _problems.append("暂停层余额徽投影缺失(%s)" % bal_txt) + var resume_btn: Button = ProbeUiUtil.find_button(main.pause_overlay, "继续", "prefix") + if resume_btn == null: + _problems.append("暂停层关闭钮缺失(「继续」)") + else: + resume_btn.pressed.emit() + if main.pause_overlay.visible or main._input_locked(): + _problems.append("暂停层关闭后未恢复(visible=%s locked=%s——R3 残留)" + % [str(main.pause_overlay.visible), str(main._input_locked())]) + # P1 守卫:图例开着 → 两处入口角标置灰;置灰态点击被处理器兜住(探针 emit 绕过 disabled) + main.ap_bar._btn_legend.pressed.emit() + if not main.board_zone.pause_badge.disabled: + _problems.append("图例开着时战斗屏暂停角标未置灰(P1 守卫失守)") + main.board_zone.pause_badge.pressed.emit() + if main.pause_overlay.visible: + _problems.append("置灰态仍可开暂停层(守卫兜底失守)") + main.legend_overlay.close() # ③ tooltip(状态效果槽悬停——来源+语义+剩余):有槽可见时触发 if not _tested_tooltip: var row = main.stage.hero_standee.slot_row # 状态 tooltip 载体 2026-09-26 迁 B 区站桩 diff --git a/game/smoke/ui_contrast_probe.gd b/game/smoke/ui_contrast_probe.gd index 866c8662..34c5141a 100644 --- a/game/smoke/ui_contrast_probe.gd +++ b/game/smoke/ui_contrast_probe.gd @@ -28,6 +28,7 @@ func _pairs() -> Array: ["锚·正文墨/数据板", UiKit.INK, UiKit.PANEL_DATA, 1.0], # 全站锚点:正文墨对数据板 ["锚·暖白/屏根", UiKit.WARM, UiKit.BG_ROOT, 1.0], # 全站锚点:暗底正文 ["锚·暖白弱/舞台底", UiKit.WARM_FAINT, UiKit.STAGE_BG, 1.0], # 全站锚点:B区辅助墨 + ["钥石徽·深墨/烛光黄", UiKit.INK_ON_GOLD, UiKit.CANDLE, 1.0], # 迭代17卡③——badge 工厂件文字(营地徽+暂停层余额行同形制) ] diff --git a/game/smoke/ui_input_probe.gd b/game/smoke/ui_input_probe.gd index cf3c5803..f4cba8e5 100644 --- a/game/smoke/ui_input_probe.gd +++ b/game/smoke/ui_input_probe.gd @@ -415,8 +415,61 @@ func _ready() -> void: _problems.append("case⑮ 提灯关闭后雾参数未复原(lantern 应=0)") if int(bv.cells_probe()[ring_cell].fog_state) == 0: _problems.append("case⑮ 提灯关闭后 dist==R+1 格未复原遮蔽") + # ---- case⑯ 暂停层输入锁语义(迭代 17 卡③ R3——开层锁棋盘、关层恢复无残留;真实 push_input + # 路径:入口角标本体 press+release 真点击 → 暂停层开;锁期挖掘点击零穿透;「继续」关层后 + # 同格挖掘重新到达——锁语义与既有模态同款,无状态残留。置于全 case 之尾:取格若需前沿移位 + #(真实点击 move 扩大可挖域),其板面扰动不再波及任何既有断言——域恰 2 枚时 flag_t 带旗+ + # dig_t 揭开后域内零无旗格,正是首跑实测踩中的形态)---- + var pause_dig := Vector2i(-9, -9) + for c in bv.diggable_probe(): + if not ms.has_flag(c): + pause_dig = c + break + if pause_dig.x == -9: + var mv16 := _pick_frontier(bv, ms) + if mv16.x == -9: + _problems.append("case⑯ 板面无可迫近边界(锁断言取格中断)") + else: + _push_click(_pos(bv, mv16), MOUSE_BUTTON_LEFT) + await get_tree().process_frame + await get_tree().process_frame + for c in bv.diggable_probe(): + if not ms.has_flag(c): + pause_dig = c + break + if pause_dig.x == -9: + _problems.append("case⑯ 无可挖无旗格(板面异常——锁断言中断)") + else: + var pbtn16: Button = main.board_zone.pause_badge + var bpos: Vector2 = pbtn16.get_global_rect().get_center() + _push_click(bpos, MOUSE_BUTTON_LEFT) + _push_release(bpos, MOUSE_BUTTON_LEFT) # Button pressed 信号走 release 沿——按压+释放成对 + await get_tree().process_frame + if not main.pause_overlay.visible or not main._input_locked(): + _problems.append("case⑯ 角标真点击未开暂停层(visible=%s locked=%s——入口可达性失守)" + % [str(main.pause_overlay.visible), str(main._input_locked())]) + else: + _push_click(_pos(bv, pause_dig), MOUSE_BUTTON_LEFT) + await get_tree().process_frame + await get_tree().process_frame + if _has_action(pause_dig, "dig") or ms.is_revealed(pause_dig): + _problems.append("case⑯ 暂停层开启时挖掘穿透(R3 输入锁破——目标=%s)" % pause_dig) + var resume_btn: Button = ProbeUiUtil.find_button(main.pause_overlay, "继续", "prefix") + if resume_btn == null: + _problems.append("case⑯ 暂停层关闭钮缺失") + else: + resume_btn.pressed.emit() + await get_tree().process_frame + if main.pause_overlay.visible or main._input_locked(): + _problems.append("case⑯ 关闭后未恢复(visible=%s locked=%s——R3 残留)" + % [str(main.pause_overlay.visible), str(main._input_locked())]) + _push_click(_pos(bv, pause_dig), MOUSE_BUTTON_LEFT) + await get_tree().process_frame + await get_tree().process_frame + if not _has_action(pause_dig, "dig") or not ms.is_revealed(pause_dig): + _problems.append("case⑯ 关闭后挖掘未到达(恢复无残留验证失守)") if _problems.is_empty(): - print("[ui_input_probe] PASS — 悬停金框/禁入信号/路径预览/flag_place/chord/dig/英雄落位/影子构成/朝向/缩格重排/缺位回退/变异角标/视野迷雾 十六断言全绿(真实 push_input 路径——⑫图上命中⑬画布拟合 2026-09-28 增,⑭变异角标 2026-09-29 增,④b 旗纹理槽回归 2026-10-02 增,⑮视野迷雾 2026-10-03 增〔+提灯接线断言 2026-10-05 迭代14⑤〕)") + print("[ui_input_probe] PASS — 悬停金框/禁入信号/路径预览/flag_place/chord/dig/英雄落位/影子构成/朝向/缩格重排/缺位回退/变异角标/视野迷雾/暂停层输入锁 十七断言全绿(真实 push_input 路径——⑫图上命中⑬画布拟合 2026-09-28 增,⑭变异角标 2026-09-29 增,④b 旗纹理槽回归 2026-10-02 增,⑮视野迷雾 2026-10-03 增〔+提灯接线断言 2026-10-05 迭代14⑤〕,⑯暂停层输入锁 2026-10-06 迭代17卡③增)") get_tree().quit(0) else: for p in _problems: @@ -493,6 +546,16 @@ func _push_click(pos: Vector2, btn: int) -> void: main.get_viewport().push_input(ev, true) +# 释放沿(case⑯——BaseButton pressed 信号默认 release 触发:角标真点击须按压+释放成对) +func _push_release(pos: Vector2, btn: int) -> void: + var ev := InputEventMouseButton.new() + ev.position = pos + ev.global_position = pos + ev.button_index = btn + ev.pressed = false + main.get_viewport().push_input(ev, true) + + func _pre_fail(why: String) -> void: push_error("[ui_input_probe] FAIL — " + why) print("[ui_input_probe] FAIL — " + why) diff --git a/game/smoke/ui_struct_probe.gd b/game/smoke/ui_struct_probe.gd index 743775e2..370068a3 100644 --- a/game/smoke/ui_struct_probe.gd +++ b/game/smoke/ui_struct_probe.gd @@ -72,6 +72,19 @@ func _ready() -> void: _settle(6) _audit("battle_hud", [main.board_zone, main.info_panel, main.stage]) _standeefit_contract(main) + # ---- 暂停层(迭代 17 卡③):组合根真实路径开层→结构断言→关闭。附只读性契约 + #(R4——子树信号连接白名单巡检)与零动效契约(R5/P4 甲——零 AnimationPlayer/零 tween)。 + # 巡检信号名清单=子树全控件的 get_signal_list() 全集(不放行任何未列名信号——新连接 + # 一律红,须先入册审查);白名单={视图自身 _emit_closed, 组合根 _on_pause_closed}。 + main._open_pause_layer() + _settle() + await _audit("pause", [main.pause_overlay]) + _pause_readonly_contract(main) + _pause_zero_motion_contract(main) + main._on_pause_closed() + _settle() + if main.pause_overlay.visible: + problems.append("[pause] 关闭后层未隐藏(模态栈单口失守)") else: problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.level_screen._cand_ids)) @@ -164,6 +177,61 @@ func _pick_battle_node(main: Node) -> int: return -1 +# ⑧pause 只读性契约(迭代 17 卡③ R4):暂停层子树全控件 × 游戏语义信号连接巡检——清单外零连接。 +# 巡检信号名清单(任务书 v1.1「巡检信号名清单落码时列全进探针」)= 玩家交互可携带写意图的通道 +# 全集(按钮按下/拨杆/文本/滑值/选项/键盘焦点)+ 本层外发信号 closed;引擎内部布局生命线信号 +# 不计(child_order_changed/item_rect_changed/visibility_changed/size_flags_changed/ +# minimum/maximum_size_changed 等系 Godot 容器系统对每控件自动建立的脏标记连接,与写通路无关 +#——首跑实测踩中:全集巡检把布局生命线误判为非法连接)。清单外任何连接(尤其指向 +# 购买/解锁/写入路径的)一律红,须先入册审查再改清单。 +const _PAUSE_READONLY_SIGNALS: Array[String] = [ + "pressed", "button_down", "button_up", "toggled", + "text_changed", "text_submitted", "value_changed", "item_selected", + "gui_input", "focus_entered", "focus_exited", "closed", +] + + +func _pause_readonly_contract(main: Node) -> void: + var overlay: Control = main.pause_overlay + var stack: Array = [overlay] + var conn_count := 0 + while not stack.is_empty(): + var n: Node = stack.pop_back() + for sig_name in _PAUSE_READONLY_SIGNALS: + for conn in n.get_signal_connection_list(sig_name): + conn_count += 1 + var cb: Callable = conn["callable"] + var host: Object = cb.get_object() + var m := String(cb.get_method()) + if host == overlay and m == "_emit_closed": + continue + if host == main and m == "_on_pause_closed": + continue + problems.append("[pause][readonly] 非法信号连接 %s.%s → %s.%s(零写通路巡检——白名单=视图 _emit_closed/组合根 _on_pause_closed)" + % [n.name, sig_name, host, m]) + for ch in n.get_children(): + stack.append(ch) + if conn_count != 2: + problems.append("[pause][readonly] 信号连接数=%d 应=2(关闭钮 pressed+closed 外发——多连接=写通路嫌疑)" % conn_count) + + +# ⑨pause 零动效契约(迭代 17 卡③ R5/P4 甲——零动效直开直关路径存在断言): +# 子树零 AnimationPlayer + 视图源码零 tween/动效包装调用(源码 token 扫描=ui_contrast 静态段同手法) +func _pause_zero_motion_contract(main: Node) -> void: + var overlay: Control = main.pause_overlay + var stack: Array = [overlay] + while not stack.is_empty(): + var n: Node = stack.pop_back() + if n is AnimationPlayer: + problems.append("[pause][motion] 子树含 AnimationPlayer(R5 零动效破)") + for ch in n.get_children(): + stack.append(ch) + var src := FileAccess.get_file_as_string("res://presentation/views/pause_overlay.gd") + for token in ["create_tween", "UiKit.motion"]: + if src.contains(token): + problems.append("[pause][motion] 视图源码含「%s」(R5 零动效直开直关破——P4 甲)" % token) + + # ⑥standee 专项:B 区站桩内容矩形 ⊆ 252px 舞台矩形(内缩 8px)且不被裁切 # (UI 设计对齐批 N2 2026-10-06——名行基线离盒底 1px 同型高危写法返修回归闸; # 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘) diff --git a/roadmap.md b/roadmap.md index 1ebc97d3..c45c6e3c 100644 --- a/roadmap.md +++ b/roadmap.md @@ -66,7 +66,7 @@ | 迭代 16 | **UI 设计对齐批(2026-10-05 用户令:迭代 16 改编为 UI 优化需求实现,原四卡全数顺延迭代 17;美术线用户自理立规的例外面=本批只改 UI 设计代码、零美术资源改动)**:三路改进整合轴的代码侧落地——事实源=`design/research/UI预览demo三路改进整合对照-2026-10-04.md`(A 落码差异对照+OPT-A 机检+WB 变体三路整合,基线原型 v2.1)。**红线=不修改美术资源**:game/assets/art 与 artwork/ 均不动、不生新素材不重烘,只改 UI 设计代码(ui_kit 色板 token/字号档/布局纪律/对比度垫底/抖动呈现/panel_skinned 接线等)。无分歧可直接收项=对比度 7 处 FAIL 修法(血数 2.09→12.29 垫底等已复算)/站桩溢出 3.2px/4 处裸色收 ui_kit/status 图标 preload 违规/tg-* 类型色接线。**前置待裁=六分歧点**(①底色冷暖 ②棋盘格边长 32/44/45 ③字号档位 ④九宫格切片 ⑤圆角去留 ⑥图标尺寸解法,对照表 §三)+开放项(抖动范围/色板唯一来源,§五)。任务书=`design/research/任务书-UI设计对齐批-2026-10-05.md`(T0-T11≈7.25d,**超软盒 3d 预挂**,三段切分=段一 2.0d 无阻断〔T0-T4a〕/ 段二 4.0d 待裁〔T4b-T8〕/ 段三 1.0d 收口〔T9-T11〕;🔴 契约 7 条 R1-R7;无分歧收项 N1-N6 已逐处 file:line 考证;**待用户裁点 11 项**=六分歧 P1 底色冷暖/P2 棋盘格边长/P3 字号档位/P4 九宫格切片/P5 圆角去留/P6 图标尺寸解法 + 开放项 P7 色板唯一来源/P8 抖动范围/P9 §五 已决注销清单/P10 记忆态口径/P11 装饰 caption 豁免;**六分歧与开放项一律呈全备选案含淘汰依据**)。**事实校正(实查,2026-10-05)**:9 处色板漂移实测 4 已收 5 未收;`tg-*` 五族在 ui_kit 并不存在(以裸 Color8 内联于 bonus_pick_overlay/shop_overlay_view,缺第五族);panel_skinned 消费面实测 8 处+工厂 1 处;棋盘格边长=动态公式 clamp[26,46] 与 §8.1 锚点 45/36 三方互不等;手绘图标十枚 20×20 已入库,真问题转为 64×64 柜台四枚显示倍率;站桩溢出实机存在 1px 余量同型写法(standee_body.gd:476);demo 基线 md5 已再次漂移(实测 78ebbee0…)。状态=**任务书已起草(2026-10-05)+ 取向已定(2026-10-06)**:✅ P1=甲(维持暖化零改动)/ P2=丁(棋盘格边长零改动,46/45/44 不一致登记为已知偏差);✅ **其余八项取向已定**(用户采纳推荐)——**跟 demo**:P4 九宫格切片 8→16 / P7 五处色板向 demo / P10 记忆态纹理灰罩;**按条款解**:P3 字号立 FS_* 档(demo 34 处<14、54 处<16 不可跟)/ P6 图标显示档(demo 自身 0.3125× 违规)/ P8 抖动只做卡顶色带(demo 渐变+blur 违反像素纪律)/ P11 装饰 caption 12px 豁免;P5 两路同结论。**「以 demo 为准」三层优先级栈=任务书 §8.3**(①用户终裁>②冻结条款>③demo);三条结构级差异(卡片 232×352 / 商店全屏化 / 地图底栏合并)**不入本批、已登记 06 待立项**。估时 T0-T11≈**6.9d**(段一 2.15d 无阻断可发令 / 段二 3.75d / 段三 1.0d;三段同属一迭代、**只在段三末合并一次**);**拷打轮一 9 问已全部落地**(Q1 `panel_skinned` content margin clamp / Q2 不走独立样张闸改并进 T10 目检 / Q3 柜台图标统一 32px / Q4 商店补类型分族 / Q5 血数 11px 与装饰 caption 合并写一条豁免 / Q6 槽两态不新增通道 / Q7 一次合并 / Q8 已知偏差已登记 06 / Q9 demo 取值抄录表为唯一引用源)。**✅ 已全批落籍(2026-10-06 单会话,worktree g-plan2-iter16ui 分支 iter16-ui-20261005 十二提交 85ea024..T11 收口笔,主区 merge --no-ff 哈希见 active.md 快照行;出口报告=[iter16-ui-exit-report](design/qa/iter16-ui-exit-report-2026-10-06.md))**:T0-T10 全落(T10 人闸材料包=[iter16-ui-human-gate-checklist](design/qa/iter16-ui-human-gate-checklist-2026-10-06.md)——实机目检+代拍板追认八项归用户);闸=冒烟 782/0(断言数持平基线)+run_all 16 套全绿+探针五件全绿(新增 ui_contrast_probe 11 对测点+ui_struct⑥standee 断言族入默认段)+doc 三闸/熵增零新增+MC 零跑(纯呈现批——核心/数据层 diff=0 实证)+素材零触碰;T0 实机复算三处修法改向(意图徽标垫牌/槽位字深化/RAP 零改动——demo 口径在实机反向)+N4a 裸色实数 25 处/10 文件全收 ui_kit(任务书 5 处系漏记)+字号 107 位点归档 FS_* 六档;偏差八条在出口报告§汇总;遗留=06 新登记「呈现层残余 <4.5:1 文字位」一条(名行四墨/顺位徽标/RAP 反馈——批外邻接发现) | 迭代 15 出口 | 预算 T0-T11≈6.9d(三段切分)——**实际单会话一次跑完(2026-10-06)** | | 迭代 17 | **⓪内容数据驱动扩展批(2026-10-06 用户令插队为头卡——①-④卡顺延,其任务书与裁点状态原样保留)**:把内容效果行为从结算代码硬编码搬进数据行「订阅事件(on)+条件(when)+效果类型(do)」,通用解释器 EffectEngine 统一执行——完成后加「已有效果类型」的新专长/秘宝=纯改 `.tres` 零代码。裁决八条全落(R1 发布订阅同步 emit 确定性优先/R2 仅一个改写窗口不做拦截器链/R3 一事件多订阅+组合规则封冲突面/R4 旧 23 挂点枚举删除·2 个退化条件行/R5 现有实现全改造+06 在册只登记〔curio_roll/shop_purchase/gp_settle 三事件登记不实施〕/R6 框架先行发射点不论有无订阅者全埋/R7 一个迭代四模块垂直切一次合并/R8 术语纪律:事件=发布订阅 topic、效果类型=订阅后逻辑、组合规则=叠加规则,禁 hook/合并律等自造词)。词汇表定稿=**实施 16 事件**+效果类型 19 行 23(v1.1 扩 damage_mult/open_ticket 两型装下减半/开单现状)+组合规则 6;存档零改动;文案表值化随批(现有实现判据);luck_coin 经 reward_roll 迁移随批。任务书=`design/research/任务书-内容数据驱动扩展批-2026-10-06.md`(v1.1 2026-10-06 联审修订:level_clear 归登记不实施/对拍判据两套〔确定性复放与冒烟=逐位、MC 统计量=成型画像同带 m4iter12 口径〕/映射表五处语义修正/断言面随迁与订阅重建时机补列;T0-T7≈4.5d,零待裁点,🔴 契约 5 条含行为等价两套判据+护栏保留+确定性;停机点 S1 首流派对拍/S2 批末闸;开工门=U0) | 迭代 15 出口(已解) | T0-T7≈4.5d | | 迭代 17 | **①敌人表行集多行批(2026-10-05 用户令编入本迭代,纯功能与数值轴四卡之一)**:放开敌人表「同一(关卡序号 × 节点深度)行集恰一敌种」加载期校验(enemy_table.gd:108-112 考证在册),允许同场遭遇混编多种敌人——生成(unit_container spawn 行集受理)/编排(battle_orchestrator/narrator)/断言面全链接通 + GDD 结构冻结面修订先行(敌人系统.md:75/:263)。事实源=06「同深度第二敌种与精英/Boss 编码位」条剩余面(前半已由关卡节点地图 v1.0 消解);前置=技能池实施批已落籍(d3fa887)开工门已解。任务书=`design/research/任务书-敌人表行集多行批-2026-10-05.md`(**v1.1 2026-10-06 联审修订**:F7 勘误 19 行 9 敌种/P1 补遭遇骰交互〔每行一骰·RNG 扩签先报批〕与实例总数≤4 消歧/P3 改裁丙撤投放/新增契约 R6 混编行行序纪律/expect_pools 断言换代入组 D;T0-T8≈3.75d〔T5 撤投后,超软盒预挂,T4 末切分点〕;🔴 契约 6 条;**✅ 裁点全裁(2026-10-06 用户令:P1甲〔Q8/Q9 二裁补全〕/P2甲/P3丙〔Q3 二裁撤投放——一裁乙与首遇教学位纪律三面冲突〕/P4乙 Boss 行亦放开·知情代价六面在册);开工=U0**) | 迭代 15 出口 | T0-T8≈3.75d(超软盒预挂,T4 末切分点) | -| 迭代 17 | **③暂停层只读入口批(2026-10-05 用户令编入本迭代)**:新增局内暂停层,承载钥石余额与解锁态(营地三柜台进度)只读查看——事实源=06「解锁界面与钥石呈现的交互形态(残余:暂停层只读入口)」条。考证在册:现行无暂停层;模态栈/设置面/调试浮窗三先例齐;UiKit 公共件(modal_shell/bcard/verb_button/徽章形制)与色板 token 单点现成;MetaProgression 只读访问器面齐全;战斗屏 GP/钥石已迁出=局内零查看入口正是本批由来。**美术红线:复用现行公共件与色板,零新素材零新色值(2026-10-05 用户立规)**。任务书=`design/research/任务书-暂停层只读入口批-2026-10-05.md`(T0-T5≈3.0d;🔴 契约 5 条含公共件单点/模态登记纪律;**✅ 裁点全裁(2026-10-06 用户令全按推荐:P1丁/P2甲/P3甲/P4甲);开工=U0**) | 迭代 15 出口 | T0-T5≈3.0d(贴软盒,T2 末切分点) | +| 迭代 17 | **③暂停层只读入口批(2026-10-05 用户令编入本迭代)**:新增局内暂停层,承载钥石余额与解锁态(营地三柜台进度)只读查看——事实源=06「解锁界面与钥石呈现的交互形态(残余:暂停层只读入口)」条。考证在册:现行无暂停层;模态栈/设置面/调试浮窗三先例齐;UiKit 公共件(modal_shell/bcard/verb_button/徽章形制)与色板 token 单点现成;MetaProgression 只读访问器面齐全;战斗屏 GP/钥石已迁出=局内零查看入口正是本批由来。**美术红线:复用现行公共件与色板,零新素材零新色值(2026-10-05 用户立规)**。任务书=`design/research/任务书-暂停层只读入口批-2026-10-05.md`(T0-T5≈3.0d;🔴 契约 5 条含公共件单点/模态登记纪律;**✅ 裁点全裁(2026-10-06 用户令全按推荐:P1丁/P2甲/P3甲/P4甲);开工=U0**) | 迭代 15 出口 | **✅ 已全批落码待人闸(2026-10-06,worktree g-plan2-iter17-pause 分支 iter17-pause-20261006 两会话接力完成,提交 c08a901/fc9eb1e/d44fdd5+收口笔;出口报告=[iter17-pause-exit-report](design/qa/iter17-pause-exit-report-2026-10-06.md))**:暂停层本体+badge 工厂件收编(Q10 甲,营地钥石徽同步迁移观感零变化)+两屏常驻角标入口与 has_open 守卫+四探针断言(结构/点击/输入锁 case⑯/只读巡检/零动效契约/对比度对)+冒烟投影一致性 5 断言;闸=冒烟 788/0(基线 783/0)+run_all 17 套+probe_gate --click 全绿+doc 三闸零新增+MC 零跑(纯呈现零数值);存量移交=熵增闸 design/diagrams 越界(00712d5 总图批落籍漏步,主区同红)+06/07 gdd_lint 存量注记红(本批零触碰);**人闸=用户实机目检**(design/qa/iter17-pause-human-gate-checklist-2026-10-06.md,重点含排播排空副作用确认项 D2。原估 T0-T5≈3.0d 贴软盒内) | | 迭代 17 | **④生涯界面设计立项(2026-10-05 用户令编入本迭代;只做设计提纲不做实装)**:事实源=06「『生涯』界面——冒险中数据回看的唯一承接处」条(P-C7 裁:M9 已退役局内结算明细回看,历史数据现无承接面=用户知情裁决)。考证在册:回看退役已落码(battle_end_overlay/battle_log_view/ui_assembler 三处注记)+settlement_review_overlay.gd 遗留件在盘+跨局持久面=save v5 meta 域+recent_summaries 现役逐局留痕(cap=10,save_manager.gd SUMMARY_CAP——2026-10-06 联审勘误:原记「无历史冒险记录面」系事实错误;真实缺口=cap 扩容+用时埋点+总局数/通关局数 lifetime 计数域〔涉存档 schema〕+营地既有最近冒险清单的去留设计)。任务书形态=设计提纲版:信息架构+承载内容清单(跨局汇总/历史冒险回看/图鉴承接)+与既有界面关系+三期实装建议+待裁点,标注「实装批另行立项」。任务书=`design/research/任务书-生涯界面设计立项-2026-10-05.md`(**v1.1 2026-10-06 联审修订**:F4 改正 recent_summaries 现役/P2 甲口径重述=扩容+用时埋点/总局数通关局数标注涉 schema/营地清单去列入设计稿必答项/N 候选补「维持 10」;T0-T2≈1.25d 纯文档零测试;**✅ 裁点全裁(2026-10-06 用户令全按推荐:P0甲/P1甲/P2甲〔N 实装期定〕/P3甲/P4甲/P5甲);开工=U0**) | 迭代 15 出口 | ✅ **设计提纲已成文待用户评审(2026-10-06 卡④设计批完成,worktree iter17-career-20261006,纯文档零代码零测试)**:提纲=design/gdd/systems/跨局循环/生涯.md v0.1(范围草案——信息架构/承载内容清单/界面关系/分期建议/待裁点五节,P0-P5 已裁结论落文,新发现待裁点 P6 营地最近冒险条带去留随评审一并裁);02 根索引补行+06 状态行换代「设计提纲已成文待评审」。**人闸=用户评审提纲**;评审通过后期一/二/三实装批另行立项(不预占排期。原估 T0-T2≈1.25d 软盒内) | | 迭代 18 | **M4 数值批(2026-10-05 用户令编入迭代 17 卡②;2026-10-06 用户令移出独立成批——不影响迭代 17 在飞开发,17 卡号 ③④ 维持原号不重排)**:四件数值欠账合一批——①削弱档位终值(weaken 现值 −65/duration 2/refresh 考证在册;叠层改续时语义已定案,差 duration 终值+当量锚复算)②调参批数值点评(06 条目现行登记五条——任务口径「八条」与条目五条不符,任务书 §2.2 已呈报差异以条目为准)③迭代 10 奖励重构联动欠账①②(钥石产出重标 boss_keystone 首杀 3/3/3·复杀 2/2/3 provisional + 通关率带重扫〔通关回血 20%+AP 件首轮 +1〕)④元进度定价剩余面(forge base 20/比 150 考证在册——06 条目文本「base 10」滞后于 2026-09-22 对齿批已呈报;Boss 两档终值)。读数义务随批=迭代 14 两件后置复算(T6 MC 四带复算+T7 技能池节奏重测,均挂 07);顺路修复 damage_probe A1 改前既有红灯(判式换代批出口报告 §四.7 在册)。**硬约束:一切 MC/verifier 跑批发起前须用户批准;调参联动禁独立调参网络(β 带 [1.2,2.5] 逐 u 检算)**。任务书=`design/research/任务书-M4数值批-2026-10-05.md`(**v1.2 2026-10-06 案C 增补+联审修订**:v1.1 三事实与第五推算件照旧;联审=P3 转正顺延至重扫读数后/检算工具 WEAKEN_PCT 入同步面/F11 两句死文本全刷/T3·T5 四带复算并账一次跑批双认账/R5 改前锚=本批新立案C 锚/估时段一勘误 4.25d;案C 增补内容照旧=§2.6 三事实〔行为换代/锚全作废/难度预判〕+T0 推算稿第五件=对拍锚按新序重推导+T6 四带复算按案C 新行为首采;T0-T8≈6.25d,天然切分点 T4 末;🔴 契约 5 条含跑批批准制;**✅ 裁点全裁(2026-10-06 用户令:P1甲=维持 2 场·magnitude [60,85] 带内当量锚复算、具体值随出口 provisional 追认/P2甲 不对称有意/P3甲 3-3-3·2-2-3 维持、转正顺延至重扫读数到账且带内(Q4 二裁)/P4甲 不立直购价);开工=U0**) | 迭代 17 出口 | T0-T8≈6.25d(超软盒预挂,段一 T0-T4≈4.25d〔v1.2 勘误,原 4.5 系加总错误〕/段二 T5-T8≈2.0d) |