diff --git a/design/gdd/07-体验验证清单.md b/design/gdd/07-体验验证清单.md index 7c70366f..2b2e8b3e 100644 --- a/design/gdd/07-体验验证清单.md +++ b/design/gdd/07-体验验证清单.md @@ -14,6 +14,7 @@ | 乏力(踩雷负向) | 无感(乘区没接线,死效果) | 输出合计 −25% | 踩雷后攻击真的变弱了 | | 失衡(标错旗) | 承伤加深 +25% | 护甲等级 −2(更易被命中) | 惩罚变隐性——挨打概率上升而非每刀固定加深 | | 卓尔 Boss 新招(新效应词汇批 09-19) | 毒爆 = 纯倍率伤害;Boss 无妨害手段 | 毒爆追加**必中毒分量** 1–3 点(不吃护甲骰不出暴击);新妨害技**蛛网束缚**:施放 → 主角护甲等级 −2 持续 1 个有效结算 | Boss 战更难更花——堆护甲被毒绕开、被网住后每刀都更容易吃全额 | +| 插旗不可逆(09-19 真人试玩反馈批) | 挖旗格被拒("须先拔旗");旗可右键拔除重插 | 旗格**左键直接挖开**=旗消失:安全格零奖励、雷格只吃挖雷惩罚;右键拔旗退役、插旗=一次性承诺 | 误旗不能零成本反悔——标中/标空照常、直挖按四情况改判,插旗前想清楚 | ## 试玩观测项(按优先级) @@ -60,15 +61,15 @@ 弃图率 / 每轮刷新率随试玩日志采集;预警阈值须有基线读数才能定(2026-09-18 拍板:现在定是拍脑袋)——随验收读数重测批出基线,数字与调参联动复核。挖掘期当场刷新无结算发生,两读数均不影响对账。 -### ⑧ 旗行为基线监控(插旗提案评估前置采集,2026-09-19 拍板) +### ⑧ 旗行为基线监控(插旗不可逆批 2026-09-19 落地后的行为监控;原「提案评估前置采集」使命随提案转正终结) -纯日志侧采集、零玩法改动(与 ⑥⑦ 同形状)。**一个观测面服务两个待答问题**:① 现行拔旗 1 行动点定价是否合理(旧实测义务「拔旗定价复核」,2026-09-19 从 06 清单销项并入本项);② 「插旗不可逆提案(禁拔旗 + 挖旗格兑付改判——四情况与四开放问题,登记原文见 2026-09-18 台账 72 与 git 历史 06 清单条目,排期 Alpha)」的评估前置——四情况触发频率(尤其情况②④「插旗后又花行动点挖开」)由此数据定:若极低频,兑付改判的复杂度不值得、提案可被数据否掉;若高频,情况④语义就是承重墙。 +原口径(2026-09-19 上午拍板):本项为插旗不可逆提案的 Alpha 评估前置采集——同日用户在真人试玩中确认"不能挖旗格"违背设定,**提案全套提前落地**(禁拔旗 + 挖旗格兑付改判,战斗结算 v0.18 四情况表;登记原文见 2026-09-18 台账 72 与 git 历史 06 清单条目)。评估使命终结,本项转为一律机制的真人行为监控(与 ⑥⑦ 同形状,纯日志侧采集零新增埋点)。 -- **观测什么**(读数全部可由现役 JSONL 日志推导,零新增埋点——动作行含 flag_place/flag_pull/被拒 dig、结算步流含 flag_hit/flag_miss 判定行〔带格坐标〕、ux_reject 行含结构化拒绝原因):① 误旗率 = flag_miss 判定行 ÷ flag_place 动作行(判定时点口径细节随首批读数钉死);② 挖旗格尝试率与被拒后路径分布(拔了挖开 / 拔了重标 / 放弃不纠错);③ 同轮插了又撤率(同一对结算行之间的插撤对);④ 拔旗次数每局。 -- **语义锚(2026-09-19 实测锁定,smoke_all `_smoke_flag_misfire_dig_after_pull` 断言在案)**:误旗判定的负向惩罚与该格奖励**相互独立**——跨轮拔旗后挖开仍获正常正向奖励(惩罚并行、不吞奖励:判定走消费型授予、挖开走查询型身份快照,两通道互不吞并);同轮插撤被拔不判、零惩罚且奖励不受影响。观测②「拔了挖开」路径的收益语义以此为准。 +- **观测什么**(读数全部可由现役 JSONL 日志推导——动作行含 flag_place/被拒 dig、结算步流含 flag_hit/flag_miss 判定行〔带格坐标〕与 reward_ineffective 作废行;flag_pull 动作行与同轮插撤、拔旗次数两读数随拔旗退役作废删除):① 误旗率 = flag_miss 判定行 ÷ flag_place 动作行(判定时点口径细节随首批读数钉死);② 挖旗格直挖率 = 插旗格直接挖开数 ÷ flag_place 动作行(原「被拒后路径分布」三路径随挖旗格放行消失);③ 四情况触发分布(①标中留判 / ②雷格直挖 / ③标空留判 / ④安全格直挖零奖励——④占比是核心读数)。 +- **语义锚(2026-09-19 落地实测锁定,smoke_all `_smoke_flag_irreversible_quadrants` 断言在案)**:插旗安全格直挖 = 零惩罚零奖励(正向兑付作废、作废归因行恰一次、标空不判);插旗雷格直挖 = 只受挖雷一层惩罚(mine_hit 照常)、标中不发;不挖留判 = 标中/标空照常;flag_pull 出目录拒绝、重复插旗 flag_present 拒绝。 - **预期**:无基线,首跑即基线——随真人试玩批次 2 起采(模拟画像不拔旗,此面只有真人数据有效)。 -- **红灯信号**:误旗后放弃纠错占比异常高(1 点拔旗税劝退诚实纠错)或同轮插撤高频(误触摩擦过大)——两红灯仅作 Alpha 评估输入,**不在试玩现场改机制**。 -- **结论回流**:读数 → Alpha 阶段插旗不可逆提案评估(四情况是否立项、拔旗定价终判);切片期玩法结构冻结维持(验收读数重测批前不动旗机制)。 +- **红灯信号**:误旗后放弃纠错占比异常高(直挖免费但④零奖励仍可能劝退纠错——玩家改绕路不挖)或④触发后局部推进明显迟滞(零奖励力度过狠的体验证据)——红灯仅作曲线调参批输入,**不在试玩现场改机制**。 +- **结论回流**:读数 → 曲线调参批复核情况④零奖励力度与旗价目;提案已转正,无后续评估义务。 ## 验收读数重测批义务(给下一批的输入,非手工项) diff --git a/design/gdd/systems/战斗循环/地图战斗资源.md b/design/gdd/systems/战斗循环/地图战斗资源.md index d1e8e51e..9f2b14c7 100644 --- a/design/gdd/systems/战斗循环/地图战斗资源.md +++ b/design/gdd/systems/战斗循环/地图战斗资源.md @@ -95,9 +95,9 @@ |---|---|---| | 开格 | **+类型**:该格资源的正向兑付(奖励) | **−类型**:该格资源的负向兑付(惩罚) | | 标旗且留到轮末判定 | **−类型**:标错,该格资源的负向兑付(惩罚) | **+类型**:标中,该格资源的正向兑付(奖励) | -| 标旗后当轮撤旗 | 零兑付(行动点已耗 = 变相惩罚,行动经济承载) | 零兑付(同左) | +| 插旗格直接挖开(插旗不可逆批 2026-09-19) | 零兑付(四情况④:零惩罚零奖励——标空不判、正向作废) | **−类型**照常(四情况②:只受挖雷一层惩罚——标中不发) | -矩阵的定义归本文;把矩阵跑起来的路由(结算队列、抵消过滤、逐行兑付)归[战斗结算](战斗结算.md)的授予段。旧"踩雷→伏击"是本矩阵的退化特例(所有雷同一类型的世界);伏击保留为 blast 型的负向一行。 +矩阵的定义归本文;把矩阵跑起来的路由(结算队列、判定基线、逐行兑付)归[战斗结算](战斗结算.md)的授予段。旧"踩雷→伏击"是本矩阵的退化特例(所有雷同一类型的世界);伏击保留为 blast 型的负向一行。撤旗动作已随插旗不可逆批退役(2026-09-19)——旗无移除手段,矩阵删「撤旗」行、增「插旗格直接挖开」行。 举证:本节为枚举与路由定义,无计算。 @@ -237,3 +237,4 @@ | v0.10 | 2026-09-18 | **伤害道具批(用户指令,2026-09-18)**:① 五型→七型——增补 pierce(穿刺)/ magic(魔法)两型,各正负一行:正向 = 单目标克制直伤道具(pierce_strike 穿刺弹 / magic_bolt 魔法矢,attack_type 字段载攻击类型轴,查战斗结算克制矩阵、必中全额不掷点、目标 = 固定顺位最前存活敌);负向 = 主角直伤(pierce_snare 穿刺陷阱 / magic_recoil 魔法反噬,复用反噬通道不经增益状态);② 权重再平衡(用户指令"保爆裂粉尘不变、等比下调其余"):spark_aoe 16 不动,attack_up 20→14 · ac_up 16→11 · heal_minor 3→2 · weaken 12→8(等比基准 ×35/51≈0.686,整取微差登记已知偏差),新行各 8——类型频次合计仍 67;③ 结构冻结"五型封闭"按 owner 修订流程改"七型封闭";切片同批落地(reward.tres 四新行 + schema 七型枚举 + attack_type 校验 + 结算兑付分支 + 棋盘徽标 穿/魔 + smoke 295 全绿;断言"主角先手攻击行存在"放宽为"存在或授予段终局"——单弱敌可被挖掘期兑付单独击杀,战斗段整段跳过为合法结局) | | v0.9 | 2026-09-17 | **数值纪律收口批:正文与调参表 knob 现行值改指路存根(事实源 `data/tables/*.tres` 与 registry),3 处失同步值修正**——候选集合节"合计 = 67"字面量改指路 reward.tres cell 池正向行 weight 之和(现行值读表);候选集合表"类型频次合计"列两处 67 改"见 reward.tres";代码事实源清单五型 weight 明细(attack_up 20 · ac_up 16 · spark_aoe 16 · heal_minor 3 · weaken 12)改"5 行正向行 weight 现行值读 reward.tres" | | v0.11 | 2026-09-18 | **战斗数值结构修复批·极性矩阵同步(2026-09-18 盘问会话用户拍板,与战斗结算 v0.14 / 增益状态 v0.9 / registry / 切片代码同批)**:① defense 型负向 off_guard 行失同步修正——原残留"hero_defense 池,承伤加深"改"AC 掷点阈值轴负向、不入池"(off_guard 换装 AC 轴随结构修复批落地,effect_pool_map 删键);② weaken 型正向行补续时定案注记(同名重复授予只刷新剩余次数——2026-09-18,叠层措辞随批退役) | +| v0.12 | 2026-09-19 | **插旗不可逆批·极性矩阵同步(用户拍板全套落地,与战斗结算 v0.18 / 排雷玩法 v0.5 / registry / 切片代码同批)**:矩阵「标旗后当轮撤旗」行删除(撤旗动作退役)、「插旗格直接挖开」行新增——安全格零兑付(四情况④零惩罚零奖励)、雷格负向照常(四情况②只受挖雷一层惩罚、标中不发);路由描述「抵消过滤」随战斗结算改「判定基线」 | diff --git a/design/gdd/systems/战斗循环/战斗结算.md b/design/gdd/systems/战斗循环/战斗结算.md index 15b4f298..a0f70aa0 100644 --- a/design/gdd/systems/战斗循环/战斗结算.md +++ b/design/gdd/systems/战斗循环/战斗结算.md @@ -20,7 +20,7 @@ |---|---| | 轮次编排 | 战斗开始序列、每轮五段结算、五种出口分发(继续 / 换图 / 挖掘期刷新 / 战斗结束 / 关卡结束) | | 结算触发与段序 | 轮末自动触发一次整轮结算;五段顺序为冻结结构,改动走文档修订而非调参 | -| 结算队列与抵消过滤 | 挖掘期动作(开格、标旗、撤旗)入结算队列;结算先做抵消过滤再逐行兑付 | +| 结算队列与判定基线 | 挖掘期动作(开格、标旗)入结算队列;旗判定以轮末在场旗为基线,逐行兑付(插旗不可逆批 2026-09-19——撤旗动作退役) | | 明细流水协议 | 结算中的每件事逐行留档并标注来源,是呈现、回放对账、数据导出共读的唯一数据源 | | 收益对照表 | 一张效果档位对照表归一全部收益的折算口径(详见核心规则) | | 伤害与治疗边界 | 修正池合成与封顶、溢杀丢弃、治疗封顶、死亡判定时机 | @@ -42,7 +42,7 @@ ## 在核心循环中的位置 ``` -挖掘期:玩家开格 / 标旗 / 撤旗 / 停手 ──(事实流 + 账本扣费)──▶ 战斗结算(轮末五段兑现)──▶ 胜负 / 换图 / 冒险推进 +挖掘期:玩家开格 / 标旗 / 停手 ──(事实流 + 账本扣费)──▶ 战斗结算(轮末五段兑现)──▶ 胜负 / 换图 / 冒险推进 ``` 本系统站在"扫雷结束、结果揭晓"的那一段:挖掘期它只安静缓冲[排雷玩法](../排雷循环/排雷玩法.md)上报的事实流,不做任何兑付;轮末把这一轮攒下的因逐行兑现成战斗的果。再往下游,胜负去向交[冒险结构](../冒险循环/冒险结构.md)接手,明细交[HUD 框架](../表现与支撑/HUD框架.md)呈现。 @@ -119,13 +119,22 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × ### 授予段兑付:极性路由,四类动作各归各账 -授予段消费结算队列。**队列与抵消过滤**:挖掘期玩家对格子的每个动作——开格、标旗、撤旗——都进入结算队列;结算先做一遍遍历过滤再兑付,不走逐动作出栈直算——标旗后被撤旗的,该次标旗动作不结算(零兑付,行动点已耗即变相惩罚);开格事实不可撤(已开格无法回退)。过滤后按触发源分三批兑换,每格受理、每旗判定恰好一次。 +授予段消费结算队列。**队列与判定基线**:挖掘期玩家对格子的动作——开格、标旗——都进入结算队列;结算逐批兑付,不走逐动作出栈直算。旗判定以轮末在场旗位为基线:被直接挖开的旗已随挖掘消失、不在基线(零兑付——标中 / 标空让位于挖掘本体结局,见下文插旗不可逆四情况);开格事实不可撤(已开格无法回退)。过滤后按触发源分三批兑换,每格受理、每旗判定恰好一次。 兑付走**极性矩阵**(定义归[地图战斗资源](地图战斗资源.md)):开安全格 → 该格格面资源的正向;开雷格 → 负向;标中旗 → 该雷格资源的正向;标错旗 → 该安全格资源的负向。五型字典里每型各有一行正向、一行负向——伏击不再是踩雷的固定产物,只保留为 blast 型的负向一行。 +**插旗不可逆四情况(2026-09-19 用户拍板全套落地,2026-09-18 提案转正)**:旗是一次性承诺、拔旗动作退役、挖旗格放行——兑付按四情况改判: + +| 情况 | 兑付 | +|---|---| +| ① 插雷格、留到轮末判定 | 标中照常(该雷格资源正向) | +| ② 插雷格、再直接挖开 | 只受挖雷一层惩罚(该雷格资源负向照常兑付——惊动巢穴);标中不发(旗已随挖掘消失、不入判定基线) | +| ③ 插安全格、留到轮末判定 | 标空惩罚照常(该安全格资源负向) | +| ④ 插安全格、再直接挖开 | 零惩罚零奖励——正向兑付作废,记「奖励作废(插旗格直接挖开)」归因行(reward_ineffective 行型);标空判定同②自然缺席 | + | 批 | 兑付 | |---|---| -| 挖格批 | 开到的安全格逐格兑该格资源的正向;开到的雷格逐格兑该格资源的负向 | +| 挖格批 | 开到的安全格逐格兑该格资源的正向(插旗格直挖除外——四情况④零奖励);开到的雷格逐格兑该格资源的负向(插旗雷格照常——四情况②) | | 旗判定批 | 标中则兑该雷格资源的正向,每颗雷的资格只兑一次;标空则记"标旗未中"归因行,并兑该安全格资源的负向 | | 清图批 | 清图奖励对象恰好发一次;完美排雷追加第二份同身份兑付 | @@ -141,7 +150,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × 伏击有上限([敌人系统](敌人系统.md)的反灾难护栏):blast 负向兑付超出上限时只有上限内生效,超出的逐颗记"溢出未生效"行。伏击聚成一个池,本结算里多个敌人共享同一池值、互不消耗,且只活一轮、轮末清零。其余四型的负向不受伏击上限约束,各自受池 clamp 与叠层上限承载。 -标中资格认雷不认旗:兑付资格以雷格实例为键——拔旗重插不恢复、同一颗雷跨轮不重复兑付;换图后新雷是新实例,资格自然重置。同一面旗一直插在雷上,每轮都会重新上报标中,去重是本系统的职责。 +标中资格认雷不认旗:兑付资格以雷格实例为键——旗无任何移除手段(拔旗退役,重复插旗被"已有旗"拒绝)、同一颗雷跨轮不重复兑付;换图后新雷是新实例,资格自然重置。同一面旗一直插在雷上,每轮都会重新上报标中,去重是本系统的职责。 标错的惩罚不再是独立机制:标空旗(标了但格不是雷)= 该安全格格面资源的负向兑付(极性矩阵的"标错"分支),类型化、确定性、零额外派生——旧三选一哈希反噬(暴走 / 失衡 / 伏击+2 三池,坐标×板序号哈希派生)随格面资源批(2026-09-15)整段下线,三池内容收编为 attack / defense / blast 三型的负向行。负向兑付与"标旗未中"归因行同键去重,每雷格实例只触发一次;叠层已满授予被拒时记"奖励未生效"行,不静默吞掉。 @@ -262,9 +271,9 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 ## P0 最小闭环 1. 战斗开始序列跑完(关闸门、开容器、掷骰定敌数、生成敌人、装板并预分配资源、发开轮通知 + 开轮摇号),进入第一轮挖掘期。 -2. 挖掘期本系统只缓冲事实流(开格、标旗、撤旗入结算队列),不做任何兑付。 +2. 挖掘期本系统只缓冲事实流(开格、标旗入结算队列),不做任何兑付。 3. 轮末自动触发五段结算,触发与事件到达序无关。 -4. 授予段先做抵消过滤(标旗后被撤旗的不结算),再按极性矩阵三批兑现:开安全格 / 标中旗兑正向、开雷格 / 标错旗兑负向、清图发清图奖励对象(完美乘数读 settlement.tres p_perfect)。 +4. 授予段按判定基线过滤(被挖开的旗不入判定),再按极性矩阵三批兑现:开安全格兑正向(插旗安全格直挖 = 四情况④零奖励作废行)、开雷格 / 标错旗兑负向(插旗雷格直挖照常——四情况②)、清图发清图奖励对象(完美乘数读 settlement.tres p_perfect)。 5. 增益合成、主角先手加逐敌按摇定技能兑付(伤害技三乘法链 / 增益技施放行)、衰减恰好一次。 6. 判定段先主角后敌人:继续通知同图、清图换图续轮、胜负走战斗结束清理(魔法道具背包不清)。 7. 明细流水全程落行,供呈现、回放与导出。 @@ -301,7 +310,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 - 五段全序:任一轮结算的明细行按五段顺序排列。 - 触发与到达序无关:同一轮末的两条结束消息按任意先后驱动,结算步骤流逐行相同。 -- 抵消过滤:标旗后被撤旗的旗判定不进入兑付、零行零兑付;开格事实不可撤。 +- 挖旗格兑付改判(插旗不可逆批 2026-09-19):有旗格直接挖开被受理——插旗安全格直挖零奖励(作废归因行恰一次)、插旗雷格直挖只吃挖雷惩罚;被挖开的旗不入轮末判定(零标中 / 标空兑付);开格事实不可撤。 - 每事实恰一次:每格受理、每旗判定恰好一次;重复投递不改变结果。 - 极性路由正确:开安全格 / 标中旗只兑正向、开雷格 / 标错旗只兑负向;兑付效果与该格资源类型的字典行一一对应。 - 伏击守恒:blast 负向兑付超出上限时兑付数等于上限,超出部分逐颗记"溢出未生效"行;多敌共享同一伏击池值。 @@ -344,3 +353,4 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 | v0.16 | 2026-09-19 | **充能退役批(用户拍板,与切片代码 / registry / 07 清单 / README 同批)**:主角攻击三分量模型(普通 + 穿刺/魔法充能)整体退役,回归单分量——量 = 攻击基线、类型 = 主角表 attack_type;挖格批/旗判定批的充能计数条款删除、充能供给表与 charge_grants 调参行删除、乘区公式与粒度口径改"攻击合计";退役理由 = 克制策略归冒险前配置(营地换型/未来冒险前参数面),局内不再有挖掘攒弹药的第二伤害供给;保留面 = 攻击类型属性轴与棋盘奖励触发源的抽象分离、奖励直伤道具(穿刺弹/魔法矢)的克制直伤消费不变。切片同批:battle_orchestrator 攻击段单分量化 + _charge_points/_charge_counts/round_facts 充能键删除、DamageMath.charge_component → counter_component 改名(奖励直伤唯一消费方)、settlement.tres charge_grants 键与 schema 校验删除、冒烟充能断言组退役改形(乘区接线单分量基线 100、标中正向兑付行断言、恢复点探针换种子 42)、冒烟 293 项 0 失败 | | v0.16 | 2026-09-18 | **甲效矩阵加法核换装批(用户拍板数值语言——小整数加法核,与 01-核心循环 数值语言条款 / registry / 切片代码同批)**:量纲 percent→整数点数,三档(克制 +1/中性 0/被克 −1)九格改值〔normal 0/+1/−1、pierce −1/+1/0、magic 0/−1/+1〕,pierce×heavy = 0 中性为用户特别拍板;五档词表与百分比乘区退役(v0.15 的五档表随批短命退役,理由=个位数刻度假粒度);分量算术改述=折算取整后加修正点数;缺项回退 100→0;奖励直伤行 payload 键 effectiveness 保留、值语义改点数;池乘区/AC 折扣档/暴击保留百分比并成文粒度口径(数值语言条款:百分比仅此三处既有粗档+开发侧工具);切片同批:settlement.tres/settlement_table.gd/damage_math/battle_orchestrator/smoke/registry/01-核心循环同步 | | v0.17 | 2026-09-19 | **技能池新效应词汇批(用户拍板——设计权威 docs/proposal-status-vocabulary.md v1.0,与 敌人系统 / 增益状态 / registry / 切片代码同批)**:① 三乘法链尾部 +必中附加分量 rider(伤害技 rider_mult 字段:max(伤害下限, floor(攻击基线 × rider_mult))——后置加法、必中全额、不进乘区不出暴击、价位锚 0.75 = AC 折扣档最轻档;"固定值伤害"改形——固定值不随基线缩放与共享技能表前提冲突);② 增益/妨害技 = 同族施放行(0 伤害不掷不折不兑伏击),妨害状态按状态行 target_side 落主角——零新授予通道;③ 必中家族清单 +rider(掷点边界切口);④ 公式行/吃掷点行/验收标准三处同步 + 新效应词汇验收条目(rider 逐拍精确复算 + rider 键封闭 + 妨害落主角端到端);⑤ 实装面 = 仅 L2 Boss 卓尔(毒爆 +rider、新行蛛网束缚妨害→失衡),其余敌种不动;多段命中经用户拍板退役不入词汇表 | +| v0.18 | 2026-09-19 | **插旗不可逆批(用户拍板全套落地,2026-09-18 提案转正;与排雷玩法 v0.5 / 行动经济 v0.4 / 地图战斗资源 v0.12 / registry / 切片代码同批)**:① 授予段「抵消过滤」改述「判定基线」——撤旗动作退役,旗判定以轮末在场旗为基线,被挖开的旗不入基线;② 新增插旗不可逆四情况兑付表:①标中照常 / ②插旗雷格直挖只吃挖雷惩罚标中不发 / ③标空照常 / ④插旗安全格直挖零惩罚零奖励(正向作废 + reward_ineffective 归因行);③ 挖格批兑付表补四情况分支;④ 标中资格措辞改「旗无移除手段」(拔旗重插通道消失);⑤ P0 闭环第 2/4 步与验收标准同步(「抵消过滤」条改「挖旗格兑付改判」)。切片同批:minesweeper_rules 挖旗格放行 + flag_pull 出目录 + dug_under_flag 事件标记、battle_orchestrator 情况④零奖励抑制、action_economy 表 flag_pull_ap_cost 删除、冒烟 317→327 项 0 失败(四情况探针 `_smoke_flag_irreversible_quadrants`) | diff --git a/design/gdd/systems/排雷循环/排雷玩法.md b/design/gdd/systems/排雷循环/排雷玩法.md index 3203c2a7..b8ddf54b 100644 --- a/design/gdd/systems/排雷循环/排雷玩法.md +++ b/design/gdd/systems/排雷循环/排雷玩法.md @@ -44,22 +44,21 @@ ## 核心规则 -### 挖、插旗、拔旗、移动:每个动作怎么才算合法 +### 挖、插旗、移动:每个动作怎么才算合法 -玩家对棋盘只做四件事:挖未开格、插旗、拔旗,以及移动小人(玩家摆在棋盘上的探险者棋子)。前三个是付费动作,按标准动作价计费(价目见 `prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres`;每轮的决策预算,账本归[行动经济](行动经济.md));本文只管棋盘面这一半,总纲只有一条:付费动作只能作用于小人周围一圈 8 格(8 向相邻)之内。 +玩家对棋盘只做三件事:挖未开格、插旗,以及移动小人(玩家摆在棋盘上的探险者棋子)。前两个是付费动作,按标准动作价计费(价目见 `prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres`;每轮的决策预算,账本归[行动经济](行动经济.md));本文只管棋盘面这一半,总纲只有一条:付费动作只能作用于小人周围一圈 8 格(8 向相邻)之内。 移动不花行动点:点任何已开格,小人沿已探明区一次走到位,"已探明区"包括踩过的雷格残址。点击已开格的唯一含义就是移动——本作没有经典扫雷里"双击数字格一键翻开周围"的 chord(双击快开),每一格的兑现都必须花行动点。 -挖已插旗的格会被直接拒绝:想改主意,必须先按价计费拔旗、再按价计费挖。这条拒绝是故意的——旗是"别挖这里"的承诺,不让玩家零成本反悔。 +挖已插旗的格是合法动作(2026-09-19 插旗不可逆批用户拍板):旗是一次性承诺、没有撤旗动作,直接挖开就是对承诺的兑现——旗随挖掘消失、该旗轮末不再判定;挖开旗格的兑付改判(标中不发 / 安全格零奖励等四情况)归[战斗结算](../战斗循环/战斗结算.md)。 | 动作 | 合法对象 | 棋盘面拒绝情形 | |---|---|---| -| 挖 | 8 向相邻、未开、无旗 | 不相邻 / 已开过 / 有旗(须先拔旗) | +| 挖 | 8 向相邻、未开(有旗照挖) | 不相邻 / 已开过 | | 插旗 | 8 向相邻、未开、无旗 | 不相邻 / 已开过 / 已有旗 | -| 拔旗 | 8 向相邻、带旗 | 不相邻 / 无旗可拔 | | 移动 | 任意已开格,且能只经已开格连通到达 | 目标未开 / 无连通路径 | -每个被拒动作都带一个可辨的拒绝原因;原因只能取自一份固定清单,清单外没有别的来源。三个来源互不混淆:棋盘面非法(上表各情形,细因彼此数据可分——"须先拔旗"和"不相邻"必须是两个可区分的原因)、资源不足(判定权在行动经济)、相位不对(本轮已收尾,不收动作)。玩家看到的文案归[HUD 框架](../表现与支撑/HUD框架.md)。 +每个被拒动作都带一个可辨的拒绝原因;原因只能取自一份固定清单,清单外没有别的来源。三个来源互不混淆:棋盘面非法(上表各情形,细因彼此数据可分——"已有旗"和"不相邻"必须是两个可区分的原因)、资源不足(判定权在行动经济)、相位不对(本轮已收尾,不收动作)。玩家看到的文案归[HUD 框架](../表现与支撑/HUD框架.md)。 ``` 一个意图进来 @@ -79,7 +78,7 @@ 连锁遇到旗就停:旗格不被链开,也不给更远的格当传播跳板。旗按价计费,正是玩家亲手买下的"链阻断器"——对一片不敢确定的地,提前插一面旗,塌方到它为止。 -清图在每次挖完成后判定;插旗、拔旗、移动都不改已开格,永远不会触发清图。 +清图在每次挖完成后判定;插旗、移动都不改已开格,永远不会触发清图。 | 连锁展开的性质 | 含义 | |---|---| @@ -136,9 +135,9 @@ ### 旗立得住、也带不走:旗的生命周期与换图重置 -旗是棋盘状态的一部分,不是背包里的实物:插上的旗跨轮保留、随图存续,没有数量上限。每次轮次结束(行动点耗尽、玩家停手或清图时),本系统对当时在场的每一面旗如实上报一条判定——旗下是雷记标中(玩家看到巢穴被控制、化险为利),不是雷记标空;旗一直插在雷上,就每轮如实重复上报标中,去重是[战斗结算](../战斗循环/战斗结算.md)的事,本系统只报事实。插旗、拔旗本身不发事件,界面反馈走调用返回值。 +旗是棋盘状态的一部分,不是背包里的实物:插上的旗跨轮保留、随图存续,没有数量上限,也没有任何移除手段(拔旗已退役——旗只能被挖开,不能被撤下)。每次轮次结束(行动点耗尽、玩家停手或清图时),本系统对当时在场的每一面旗如实上报一条判定——旗下是雷记标中(玩家看到巢穴被控制、化险为利),不是雷记标空;旗一直插在雷上,就每轮如实重复上报标中,去重是[战斗结算](../战斗循环/战斗结算.md)的事,本系统只报事实。被直接挖开的旗随挖掘消失、不在轮末在场基线里,自然不报判定;插旗本身不发事件,界面反馈走调用返回值。 -标旗与撤旗都是动作,轮末结算时走[战斗结算](../战斗循环/战斗结算.md)的抵消过滤:标旗后当轮又被撤旗的,该次标旗不进入兑付(判定批以在场旗位为基线,不在场即不判)——零兑付、行动点不返还;开格事实不可撤。旗不参与清图判定,但带旗的格没挖开就照常挡着——全图标旗一格不挖,永远清不了图。 +插错旗的纠错路径只有一条:把它挖开(兑付改判四情况见[战斗结算](../战斗循环/战斗结算.md));不挖也可以留着它当链阻断器、当"别走这边"的地图注记。旗不参与清图判定,但带旗的格没挖开就照常挡着——全图标旗一格不挖,永远清不了图。 标空就是标错,玩家读到的是控制失败——惩罚兑付为该安全格格面资源的负向(极性矩阵,归[地图战斗资源](../战斗循环/地图战斗资源.md)与[战斗结算](../战斗循环/战斗结算.md)),本系统只报"标空"事实。 @@ -167,7 +166,7 @@ |---|---|---| | 最先 | 能清图的挖 | 执行后图即全开的挖——防止"耗尽优先"把清图高光结算成最差结局 | | 次之 | 停手 | 玩家主动收轮 | -| 其余 | 普通挖、插旗、拔旗、移动 | 同类内按提交顺序 | +| 其余 | 普通挖、插旗、移动 | 同类内按提交顺序 | 对外,本系统只发三种事件、不订阅任何东西;事件集合封闭,增删事件就是接口修订: @@ -205,7 +204,7 @@ 最小闭环只保留跑通"装板—挖—收轮—兑现—换图"必需的规则: 1. 战斗开始装上新图:校验结构前提,违例拒绝;初始展开免费翻开出生开阔地,小人落位。 -2. 玩家每帧提交一个意图:挖(判棋盘面,请账本按价扣费,开格加连锁并逐格上报)、插旗拔旗(按价计费,旗入棋盘,零事件)、移动(免费,已开区一次走到位)。 +2. 玩家每帧提交一个意图:挖(判棋盘面,请账本按价扣费,开格加连锁并逐格上报;有旗格照挖、旗随挖掘消失)、插旗(按价计费,旗入棋盘,零事件)、移动(免费,已开区一次走到位)。 3. 被拒动作带可辨原因、状态不变;批次内轮次结束首次成立处定赢家,其后意图丢弃。 4. 收轮:先报完在场旗的逐条判定,再报轮次结束(停手路径由行动经济直发)。 5. 战斗结算订阅事件流逐行兑现;清图或刷新换图时按它回发的指令装新图,旧板旗全量失效,回到第 1 步。 @@ -230,7 +229,7 @@ - 数字恒真:任意已开安全格的数字恒等于其 8 向相邻雷数;出生区无雷、数字恒真、初始展开不吞全图三条结构前提任一不满足时,装板被拒绝。 - 初始展开恰等:装板后翻开的格集合恰为"出生 3×3 加连通开阔地"——不止 3×3,也不外扩一圈;全程免费、恒无格面资源(封锁格)、计入清图进度;跨轮不重放。 - 连锁免费且遇旗即止:一次触发连锁翻开任意多格,扣费恒为首格按价计的一笔;旗格不被链开、不作传播跳板;连锁格计入清图进度、资源资格照常。 -- 挖有旗格被拒:拒绝原因可辨("须先拔旗"与"不相邻"数据可分),棋盘与行动点余额不变。 +- 挖旗格放行(插旗不可逆批 2026-09-19):有旗格直接挖开被受理——旗随挖掘消失、该旗轮末不判;重复插旗仍被"已有旗"拒绝;兑付改判四情况(标中不发 / 安全格零奖励)归战斗结算。 - 已引爆雷格可通行:挖雷后轮次不结束,小人可站、可经已开区穿行续挖;雷格不计清图、不可再挖再插旗。 - 移动合法且预览同源:目标未开或无已开区连通路径时拒绝;路径预览与实际执行逐格相同;移动免费、零事件。 - 批次仲裁与到达序无关:同一批意图按任意顺序提交,结束路径与结算结果一致;赢家定出后其余意图丢弃——零棋盘效果、零扣费、零事件,且丢弃区别于拒绝。 @@ -251,3 +250,4 @@ | v0.2 | 2026-09-15 | 雾格残留清理:关闭「待拍板」三条雾失义登记(连锁开雾格与雾内旗恒不中、开格事件的变异标记透传、四格态中的两个雾态)——现行连锁与旗判定口径已按"全部安全格"写在正文,开格事件仍携带随格只读的变异标记(通用变异框架保留),格态呈现收窄为已开 / 未开两态。规则本体不变 | | v0.4 | 2026-09-17 | 数值纪律收口批:正文与调参表 knob 现行值改指路存根(事实源 data/tables/*.tres 与 registry),0 处失同步修正——动作价面值改「按标准动作价计费(价目见 action_economy.tres)」口径,连锁免费、移动免费等结构语义保留 | | v0.3 | 2026-09-15 | **格面资源批(用户拍板四点)**:① 初始展开格定名**封锁格**(已开、计入清图进度、恒无格面资源、不再有任何加成),铁律表"无奖励"改"无格面资源";② 挖开格事件"奖励资格"字段改"格面资源资格"——安全格与雷格恒有(雷格不再恒无),封锁格恒无;③ 旗生命周期补结算队列口径:标旗 / 撤旗都是动作,轮末经战斗结算抵消过滤(标旗后被撤旗不兑付、行动点不返;开格不可撤);④ 标错惩罚条目与 P1 后置删除——惩罚已定为该安全格格面资源的负向兑付(极性矩阵,归奖励道具经济 / 战斗结算),本系统只报"标空"事实;⑤ 清图呈现口径注记:剩余安全格计数即"剩余未探索安全区域"。玩家说法"失控反噬"随三选一反噬下线改"控制失败" | +| v0.5 | 2026-09-19 | **插旗不可逆批(用户拍板全套落地,2026-09-18 提案转正)**:① 挖旗格放行——挖的合法对象去掉"无旗"前提,旗格直挖被受理、旗随挖掘消失、该旗轮末不再上报判定;② 拔旗动作退役——意图目录删 flag_pull,动作表与拒绝原因清单删对应项(flag_absent);③ 旗生命周期改写:旗无任何移除手段、纠错路径 = 挖开(兑付改判四情况归战斗结算 v0.18);④ 验收标准"挖有旗格被拒"改"挖旗格放行"。冒烟 327 项 0 失败(四情况探针 `_smoke_flag_irreversible_quadrants` 在案) | diff --git a/design/gdd/systems/排雷循环/行动经济.md b/design/gdd/systems/排雷循环/行动经济.md index f86707ae..d47175ed 100644 --- a/design/gdd/systems/排雷循环/行动经济.md +++ b/design/gdd/systems/排雷循环/行动经济.md @@ -6,7 +6,7 @@ ## 设计目的 -每场战斗里,玩家真正在花的钱只有一种:行动点(AP)。行动经济就是这本账——每轮发给你固定的行动点,挖格、插旗、拔旗按统一价目计费(价目见 `prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres`),花完这一轮就结束了。没有这本账,"挖这一格值不值"就没有统一的价格语言。 +每场战斗里,玩家真正在花的钱只有一种:行动点(AP)。行动经济就是这本账——每轮发给你固定的行动点,挖格、插旗按统一价目计费(价目见 `prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres`),花完这一轮就结束了。没有这本账,"挖这一格值不值"就没有统一的价格语言。 它还要给"我不想赌了"一个体面的出口:主动停手能把剩余行动点 1:1 存进储备池,攒够了可以当场换掉整张雷图。推理本身不收钱——连锁展开免费——花钱的只是"兑现"这个动作。 @@ -19,7 +19,7 @@ | 负责 | 说明 | |---|---| | 行动点账本 | 全项目唯一扣减入口:余量检查、扣费、结束判定在同一个原子事务里完成,不允许双重折算 | -| 统一价目 | 挖 / 插旗 / 拔旗同一价目(表值见 action_economy.tres);移动与连锁展开免费(冻结项,不参与调参) | +| 统一价目 | 挖 / 插旗同一价目(表值见 action_economy.tres;拔旗动作已随插旗不可逆批 2026-09-19 退役);移动与连锁展开免费(冻结项,不参与调参) | | 轮次结束仲裁 | 清图、停手、耗尽三种结束方式同时成立时,按固定优先级只裁出一个赢家 | | 储备行动点池 | 停手进账、刷新支出、战斗结束清零(详见核心规则) | | 对下游的经济契约 | 五条契约约束收益兑现侧,由战斗结算负责对账验收 | @@ -54,8 +54,7 @@ | 动作 | 花费 | 说明 | |---|---|---| | 挖开格 | 标准价(见 action_economy.tres) | 只能挖小人 8 向相邻的格子;若触发连锁展开(flood fill 式整片翻开),连锁翻开的部分免费,且计入清图进度 | -| 插旗 | 标准价(见 action_economy.tres) | 同样限相邻格。旗只是棋盘上的标记,不是背包里的实物,想用多少有多少——它的稀缺性完全由"要花一次标准价行动点"承载 | -| 拔旗 | 标准价(见 action_economy.tres) | 与插旗对称:移除标记,无任何返还 | +| 插旗 | 标准价(见 action_economy.tres) | 同样限相邻格。旗只是棋盘上的标记,不是背包里的实物,想用多少有多少——它的稀缺性完全由"要花一次标准价行动点"承载。旗是一次性承诺(插旗不可逆批 2026-09-19):插上就没有移除动作,纠错 = 直接挖开该格 | 不花钱的有两件事:移动小人(点已翻开的格子走过去,仅限已探明区域)和连锁展开。这两项免费是冻结设计,服务"边缘探索不心疼"的氛围,不是经济杠杆。 @@ -149,12 +148,12 @@ | 下游 | [HUD 框架](../表现与支撑/HUD框架.md) | 行动点与储备池常显;刷新入口;清零反馈;动作被拒原因的双源呈现(棋盘非法 vs 资源不足) | | 下游 | [教学引导](../表现与支撑/教学引导.md) | 价目与"储备池战斗结束清零"的教学口径来源 | -账本对外是一组封闭的布尔入口(开轮 / 挖 / 插旗 / 拔旗 / 停手 / 刷新 / 结算完成通知 / 战斗结束通知):受理返回真、拒绝返回假且状态不变。相位非法的拒绝附带调试诊断,但不致命中止——进程致命只留给数据配置通道。 +账本对外是一组封闭的布尔入口(开轮 / 挖 / 插旗 / 停手 / 刷新 / 结算完成通知 / 战斗结束通知):受理返回真、拒绝返回假且状态不变。相位非法的拒绝附带调试诊断,但不致命中止——进程致命只留给数据配置通道。 ## P0 最小闭环 1. 战斗结算发开轮通知:行动点硬重置为表值,储备池不动,进入可操作状态。 -2. 玩家挖 / 插旗 / 拔旗,每个动作过账本按价扣费;连锁展开免费且计入清图进度。 +2. 玩家挖 / 插旗,每个动作过账本按价扣费;连锁展开免费且计入清图进度。 3. 玩家可随时停手(剩余行动点 1:1 入库),或储备够门槛时刷新换图(每轮次数上限读 `data/tables/action_economy.tres`)。 4. 结束路径在账本事务内仲裁,只裁一个赢家。 5. 轮末两字段消息发给战斗结算,后者把收益逐行兑现进结算明细。 @@ -178,7 +177,6 @@ | ap_per_round | int [1, 32] | 表值 | 每轮行动点;兼作停手转存的规模旋钮 | | dig_ap_cost | int [1, 8] | 表值 | 挖单格成本(下限 1:为 0 则预算系统失效) | | flag_place_ap_cost | int [0, 8] | 表值 | 插旗成本 | -| flag_pull_ap_cost | int [0, 8] | 表值 | 拔旗成本 | | rap_refresh_cost | int [1, 64] | 表值 | 刷新门槛;默认等于每轮预算,构成"满预算停手 = 恰好一刷"的基线节拍 | | move_ap_cost | int | 表值(冻结 0) | 移动免费——冻结,非调参项 | | chain_ap_cost | int | 表值(冻结 0) | 连锁免费——冻结,非调参项 | @@ -212,3 +210,4 @@ | v0.1 | 2026-09-14 | 自归档 GDD 压缩改写 | | v0.3 | 2026-09-17 | 数值纪律收口批:正文与调参表 knob 现行值改指路存根(事实源 data/tables/*.tres 与 registry),0 处失同步修正——价目改「统一价目 / 标准价(见 action_economy.tres)」口径,每轮点数与刷新门槛改表值指路;调参表「现行值」列整列指路化;注记 refresh_cap_per_round 与表字段 refresh_max_per_round 命名不一致(字段名对齐待批)。顺带:P0 闭环「扣 1 点」改「按价扣费」;终扫补两处漏网——巢穴约束例句「每轮花 2 点重插」改「重复计费重插」、P0 闭环「每轮至多 2 次」改刷新上限读表 | | v0.2 | 2026-09-16 | AP 压速批文档面值同步(用户拍板 8→6,削标旗 rush 单轮兑付吞吐为无伤通关根因的主阀门):ap_per_round 与 rap_refresh_cost 现行值 8→6,随动保持"满预算停手 = 恰好一刷"耦合;数据表与 registry 已同批改,本批补文档面正文两处与调参表两行 | +| v0.4 | 2026-09-19 | 插旗不可逆批(用户拍板全套落地):拔旗动作退役——设计目的/统一价目/价目表/布尔入口清单/P0 闭环/调参表六处删"拔旗",flag_pull_ap_cost 字段随 schema 与 data 表同批删除;插旗行补"旗 = 一次性承诺、纠错 = 直接挖开"口径 | diff --git a/design/gdd/systems/表现与支撑/教学引导.md b/design/gdd/systems/表现与支撑/教学引导.md index 675ed10d..728c9477 100644 --- a/design/gdd/systems/表现与支撑/教学引导.md +++ b/design/gdd/systems/表现与支撑/教学引导.md @@ -54,8 +54,9 @@ HUD 框架定稿时已为教学留好两个承载位,本系统的内容将来 | 条目 | 来源 | 要教的事 | |---|---|---| -| 行动点价目 | [行动经济](../排雷循环/行动经济.md) | 挖 / 插旗 / 拔旗各花多少、移动与连锁免费 | +| 行动点价目 | [行动经济](../排雷循环/行动经济.md) | 挖 / 插旗各花多少、移动与连锁免费 | | 储备池清零提示 | [行动经济](../排雷循环/行动经济.md) | 必须说清"储备行动点在战斗结束时清零",防止"攒的钱凭空没了"的误读 | +| 旗是一次性承诺的首遇提示 | [排雷玩法](../排雷循环/排雷玩法.md)(插旗不可逆批 2026-09-19) | 经典扫雷玩家会习惯性右键拔旗——本作旗不能拔,插上就是承诺;想改主意就左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)。每场冒险提示一次,非模态 | | chord 不存在的首遇提示 | [HUD 框架](HUD框架.md)(承接排雷玩法的呈现义务) | 经典扫雷玩家会习惯性双击数字格——本作没有 chord,点数字格 = 移动小人。每场冒险提示一次,非模态 | | 挖非相邻格的首次提示 | [HUD 框架](HUD框架.md) | 挖与旗都限小人 8 向相邻的格子,第一次越界操作时提示 | | 增益叠层节奏 | [增益状态](../战斗循环/增益状态.md) | "同一轮拿满优于跨轮慢慢叠"——增益有结算次数衰减,教学要传达这个节奏 | diff --git a/design/registry/entities.yaml b/design/registry/entities.yaml index 0dc7776d..8b1bd8bb 100644 --- a/design/registry/entities.yaml +++ b/design/registry/entities.yaml @@ -72,17 +72,17 @@ entities: table: ActionEconomyTable(数据配置 承载,单行全局;行动经济 为回填 owner;2026-09-07 三次回填(RAP 结构修订)——四字段删除 + rap_refresh_cost 新增,见 行动经济 Schema 定稿节) ap_per_round: 6(2026-09-16 AP 压速批 8→6,用户拍板——无伤通关根因=标旗 rush 单轮兑付吞吐,AP 削均值为主阀门;原型 v2 定稿 8,12 点已否决;双重身份:节奏 knob 兼停手 RAP 转存规模;不够再降 4 待观察) dig_ap_cost: 1(连锁展开免费,计入清图进度) - flag_place_ap_cost: 1(旗无实物资源/无数量上限——2026-09-07 用户修订,稀缺性由 AP 承载;限小人 8 向相邻) - flag_pull_ap_cost: 1(移除棋盘标记、无返还物——OQ2 待原型补丁复核,knob 落点) + flag_place_ap_cost: 1(旗无实物资源/无数量上限——2026-09-07 用户修订,稀缺性由 AP 承载;限小人 8 向相邻;旗 = 一次性承诺——纠错 = 直接挖开该格,插旗不可逆批 2026-09-19) + flag_pull_ap_cost: 【已删除】拔旗动作随插旗不可逆批退役(2026-09-19 用户拍板全套落地)——字段随 schema 与 data 表同批移除,本行留痕指路 rap_refresh_cost: 6(2026-09-16 随 ap_per_round 8→6 同批随动,保持"满停一轮 = 恰好一刷基线节拍"耦合;解耦须单独立批) move_ap_cost: 0(冻结:P4 探索氛围承载,非经济杠杆) chain_ap_cost: 0(冻结:v2 定稿) rap_rules: RAP = 停手剩余 AP 1:1 转存(结构,非 knob);战斗域生命周期(battle_over 清零,跨轮跨图保留);来源唯一(停手)、支出唯一(刷新);刷新决策主体 = 玩家发起、挖掘期(Active 相位)当场换图——HUD框架 经组合根/战斗结算 refresh_board 编排调用(2026-09-12 音频 修订:原"结算编排期 Waiting 代理调用"退役);每轮上限 2 次(refresh_cap_per_round 结构位默认 2——HUD框架 提案采纳,RoundStart 重置;原"同一 Waiting 相位多次刷新合法/无结构上限"作废);旧板旗随旧板作废(零兑付、旗 AP 不返还——排雷玩法 EC11 承载);挖掘期当场刷新无结算发生(原"敌方时间税"自惩失效——行动经济 OQ3 阈值联动复核);cleared 轮恒 new_board、stopped/ap_exhausted 轮恒 same_board(战斗结算 回发违约 fail-fast 不变);刷新图不产生清图奖励;无战斗收益——原 ×2/×1 折算与 stop/clear_multiplier 已作废(2026-09-07 用户拍板) - flag_rules: 旗 = 排雷玩法 棋盘状态单态(原口袋旗双态作废);随图存续,清图/刷新消失;标中旗 = 怪物 debuff(三向效果定稿),兑付一次性、资格以雷格实例为键(每雷本次战斗至多兑付一次;放置实例仅为当轮结算载体;拔旗重插不恢复已消费资格——2026-09-07 三轮评审改定,OQ5/F1 条款 2) + flag_rules: 旗 = 排雷玩法 棋盘状态单态(原口袋旗双态作废);随图存续,清图/刷新消失;标中旗 = 怪物 debuff(三向效果定稿),兑付一次性、资格以雷格实例为键(每雷本次战斗至多兑付一次;放置实例仅为当轮结算载体);旗无移除手段(拔旗退役 2026-09-19——原「拔旗重插不恢复已消费资格」OQ5/F1 条款 2 通道结构性消失,重插被 flag_present 拒绝);挖旗格放行、兑付四情况改判归 战斗结算 v0.18(插旗不可逆批 2026-09-19) round_end_carrier: 双发射源(2026-09-07 修订)——棋盘路径(ap_exhausted/cleared)由 排雷玩法 外发(systems-index 原裁决不变),停手路径(stopped)由 行动经济 直发;payload 同构 (path_tag, continuation_hint) arbitration_priority: cleared > stopped > ap_exhausted(同帧/同批次结束路径总仲裁;stop-first 批次腿 deferral 至 排雷玩法 集成测试,义务清单见 systems-index 门禁记录) downstream_contract: 行动经济 F1 五条款(① 效果当量对照单一线性换算 · ② 标中旗锚严格 > 最低档 + 连锁放大边际基准 + 总吞吐对账 · ③ 挖掘风险不等式 P(安全)×buff > P(雷)×敌方增益,战斗结算 单一闭合 owner · ④ 清图奖励 > 0 且 地图战斗资源 差异化插槽(图类型战斗域恒定) · ⑤ 清图 vs 弃图刷新支配性,战斗结算 闭合强制检查点)——2026-09-07 三轮评审定稿 - cross_field_validation: 任一动作成本(dig/flag_place/flag_pull)≤ ap_per_round——ActionEconomyTable schema validate() 跨字段类别,违规加载期致命(2026-09-07 三轮评审增补;条件性夹具登记 数据配置 校验双通道) + cross_field_validation: 任一动作成本(dig/flag_place;flag_pull 随拔旗退役 2026-09-19 移出)≤ ap_per_round——ActionEconomyTable schema validate() 跨字段类别,违规加载期致命(2026-09-07 三轮评审增补;条件性夹具登记 数据配置 校验双通道) referenced_by: [棋盘生成, 排雷玩法, 地形变异, 战斗结算, 增益状态, 地图战斗资源, HUD框架, 教学引导] - name: variation_table @@ -172,7 +172,7 @@ entities: round_end: "{path_tag ∈ {ap_exhausted, cleared}, continuation_hint}——棋盘路径由 排雷玩法 外发、核心字段逐字段取自 行动经济.last_round_end(读取权 = 接口契约),与 行动经济 stopped 路径 payload 同构;stopped 由 行动经济 直发、排雷玩法 禁双发(action_economy.round_end_carrier 双发射源)" event_order: 同一轮次结束事务内 flag_judged ×N 先于 round_end(全路径统一——stop 腿 排雷玩法 先发旗判定再委托 request_stop,停手不改旗故基线等价);战斗结算 结算触发 = Waiting 相位编排点、不依赖事件到达序(战斗结算×3 强制项之一,systems-index 排雷玩法 门禁块) zero_random: 排雷玩法 零 RNG 流消费——同板同意图序列 ⇒ 事件流逐位确定(AC-19);cell_revealed 发射序 = 规范序(初始/连锁 = 触发格 BFS、8 邻行主序) - api_surface: install_board / settle_batch(HUD框架 唯一提交入口;stop 经此委托 行动经济.request_stop——HUD框架×6① 强制项:不直调 行动经济,行动经济 Rule 9 调用方细化、行动经济 GDD 不改以本条为准)/ try_dig·try_flag_place·try_flag_pull·try_move / compute_move_path(HUD框架 路径预览数据源)/ retire_board;ActionOutcome{accepted, executed|voided|rejected, reject_reason ∈ {board_face, resource_face, phase, board_invalid}, sub_reason【仅 board_face 携带:not_adjacent/target_revealed/target_concealed/flag_present/flag_absent/no_path/out_of_catalog——2026-09-09 评审 UX-1 定稿,玩家可辨呈现数据源;批后修改 = 接口漂移】}——provisional,事件载体归实现期 ADR(排雷玩法 OQ2) + api_surface: install_board / settle_batch(HUD框架 唯一提交入口;stop 经此委托 行动经济.request_stop——HUD框架×6① 强制项:不直调 行动经济,行动经济 Rule 9 调用方细化、行动经济 GDD 不改以本条为准)/ try_dig·try_flag_place·try_move / compute_move_path(HUD框架 路径预览数据源)/ retire_board;ActionOutcome{accepted, executed|voided|rejected, reject_reason ∈ {board_face, resource_face, phase, board_invalid}, sub_reason【仅 board_face 携带:not_adjacent/target_revealed/target_concealed/flag_present/no_path/out_of_catalog——2026-09-09 评审 UX-1 定稿,玩家可辨呈现数据源;批后修改 = 接口漂移;flag_absent 随拔旗退役 2026-09-19 移出,挖旗格放行同批】}——provisional,事件载体归实现期 ADR(排雷玩法 OQ2) referenced_by: [战斗结算, 地图战斗资源, HUD框架, 音频, 教学引导] - name: minesweeper_initial_expansion @@ -234,7 +234,7 @@ entities: 叠加层(overlay): 魔法道具 自持的板级派生信息集(显影/真视/预读三集)——只改"玩家能看见什么"不改棋盘事实,零回写 排雷玩法/地图战斗资源 状态、随图消亡(魔法道具 首次定义) 真视窄域护栏: 替代推理的道具效果(雷位真视)目标域 ≤3×3、全图级仅限恢复推理原料类(显影/预读)——信息层级护栏,防"扫雷退化为路径优化"(魔法道具 Core Rule 3 首次定义,schema 校验强制) 渗透率: aux 道具期望件数占奖励池的份额——E[N_aux/板] = |U|×Σw_aux/W_total(魔法道具 F1 首次定义;供给面天然限流,P1 护栏的量化形态) - 行动: 轮次内挖掘期的原子决策单位——同一事件的三个视角:玩家决策层叫行动、进 排雷玩法 的数据形态叫意图(单意图帧整批提交)、行动经济 计价类别叫动作(action_kind 统一价目);有价行动(挖/插旗/拔旗)1 AP/次、受每轮 AP 预算约束(现行表值 8),移动/停手免费不计 AP(game-concept 术语层级 2026-09-14 收录) + 行动: 轮次内挖掘期的原子决策单位——同一事件的三个视角:玩家决策层叫行动、进 排雷玩法 的数据形态叫意图(单意图帧整批提交)、行动经济 计价类别叫动作(action_kind 统一价目);有价行动(挖/插旗;拔旗随插旗不可逆批 2026-09-19 退役)1 AP/次、受每轮 AP 预算约束(现行表值 8),移动/停手免费不计 AP(game-concept 术语层级 2026-09-14 收录) 节点: 关卡节点地图上的一个格——关卡内移动的步进单位;层名定稿(2026-09-14 用户裁决:节点 胜 遭遇——成员中立 + 关卡节点地图 注册名/切片 node_* 接口零改动 + 遭遇既有语义 = 战斗不挪用);类型分类学与图形态已展开定稿(2026-09-17 关卡节点地图 v1.0:四类封闭枚举 {battle, reward, rest, boss} + 分支节点图〔连线只朝前进方向、唯一入口、Boss 恒唯一终点、无回头路〕;节点编号 = 关卡内唯一定位地址,数据键 node_depth 沿用) 遭遇: 一次完整战斗事件的同义词——战斗节点触发的那一场仗(既有用法:战斗结算"两场遭遇" / HUD框架"双敌种遭遇""Boss 遭遇变体");不作地图层名(2026-09-14 裁决) 节点奖励: 奖励点节点给的奖励(关卡节点地图 域)——与安全格奖励(地图战斗资源,格内开出)区分;机制定稿(2026-09-17 关卡节点地图 v1.0:节点奖励 = **功能道具的正式渠道**,兑付 = 获得一件功能道具入持有域;内容/持有/使用语义归 魔法道具 内容重定批,切片期零兑付直通为合法空态);节点奖励覆盖层 = 兑付反馈覆盖层(与行进确认同面,视觉规格归 HUD框架;spec design/ux/node-reward.md 未来件〔battle.md OQ9〕)——三个"reward"各归各位(2026-09-14 拆名注记) @@ -320,7 +320,7 @@ entities: pool_tag: enum {cell, clear_default}(cell = 格面资源单一池——安全格与雷格一视同仁同池同分布;clear_ 命名空间预留、校验拒绝——棋盘生成 elite_ 同款) # 2026-09-15 safe_cell→cell 更名 + active_inject 删除 tier: int [1, 8](knob)——强度档位序数(与当量零耦合) cross_field_validation: 加载期致命族——池空(cell 池 ≥1 正向行 weight>0)· 七型正向行齐备 · 七型负向行齐备(字典完整性——缺任一极性行致命)· clear_default 恒正当量(≥1 活行 ∧ power≥1 ∨ effect_id 非空——行动经济 条款④"清图奖励>0"的行级兑现口径)· percent 行 effect_id 必填/flat 行恒空 · flat 行 power≥1 # 2026-09-15 重写——旧 floor_supply_missing / no_damage_row 随可选类删除失义退役 - mechanics: 格面资源身份 install 期预分配(roll_board 恰一次、行主序、消费 reward_roll 封闭流恰一 scope 批〔格批 + 清图批尾一号〕;全部非封锁格逐一摇定类型——安全格与雷格同池同分布、封锁格不入摇号清单;Ready 后只读、无重掷 API)· **极性不在资源内**——「动作 × 格型」矩阵(开安全格/标中雷 = 正向、开雷格/标错安全格 = 负向、标旗后撤旗 = 零兑付)字典定义归 地图战斗资源、路由执行归 战斗结算 授予段(结算队列 + 抵消过滤——标旗后被撤旗不结算)· 授予期零随机(身份恒定跨轮;换图映射消亡)· 负向恒即时(不入库、无库存语义——库存已随可选类删除下线)· 满层零兑付 = 合法例外 + "奖励未生效"行 · 重复受理幂等(同格重复 grant / 同批次重复 grant_clear → 同一快照/身份 + 诊断;受理/兑付恰一次归 战斗结算×②③) + mechanics: 格面资源身份 install 期预分配(roll_board 恰一次、行主序、消费 reward_roll 封闭流恰一 scope 批〔格批 + 清图批尾一号〕;全部非封锁格逐一摇定类型——安全格与雷格同池同分布、封锁格不入摇号清单;Ready 后只读、无重掷 API)· **极性不在资源内**——「动作 × 格型」矩阵(开安全格/标中雷 = 正向、开雷格/标错安全格 = 负向、插旗安全格直挖 = 零兑付〔四情况④〕;撤旗动作随插旗不可逆批 2026-09-19 退役)字典定义归 地图战斗资源、路由执行归 战斗结算 授予段(结算队列 + 轮末在场旗判定基线——被挖开的旗不入基线)· 授予期零随机(身份恒定跨轮;换图映射消亡)· 负向恒即时(不入库、无库存语义——库存已随可选类删除下线)· 满层零兑付 = 合法例外 + "奖励未生效"行 · 重复受理幂等(同格重复 grant / 同批次重复 grant_clear → 同一快照/身份 + 诊断;受理/兑付恰一次归 战斗结算×②③) api: 封闭枚举入口(provisional,实现期 ADR 定名)——roll_board / query_identity(返回 {reward_id, cell_type, tier} 引用级投影;未开格恒可查——魔法道具 桥接结构前提;清单外坐标 → null + 诊断)/ grant(同格重复受理幂等 + 诊断)/ grant_clear(同批次重调幂等 + 诊断)/ retire_mapping / equivalence_supply(全活行投影 + tier_min_buff)+ weighted_pick 纯函数面;新增入口 = 接口漂移,新增入口须随附全输入域语义表 # 2026-09-15 删 stock/use/query_inventory/clear_inventory(库存轴下线) mvp_content: 十四行(2026-09-15 格面资源批十行;2026-09-18 伤害道具批增补四行)——格面池正向七行(attack_up / ac_up〔2026-09-16 护甲等级掷点批换装,原 defense_up 减伤 −30% 退役;cell_type defense/权重/档位零改动〕/ spark_aoe / heal_minor / weaken / pierce_strike / magic_bolt〔attack_type 字段载攻击类型轴,查战斗结算克制矩阵、单目标必中全额〕)+ 负向七行纯字典(enrage / off_guard / heal_backlash 直伤〔不经增益状态〕/ ambush_double blast〔stacks=2〕/ sapped / pierce_snare / magic_recoil〔均主角直伤,不经增益状态〕)+ clear_blast(clear_default);类型频次 = 正向行权重合计;tier_min_buff = 2。**2026-09-17 数值压缩批·文档同步——具体数值列(power/weight/power_min/power_max)改指路存根**:现行行值读 `data/tables/reward.tres`(压缩后锚行:spark_aoe power=1 / heal_minor power=[1,1] 定值(原 [2,6] 区间压缩)/ heal_backlash power=1 / clear_blast power_min=8 power_max=18 power=13 中点 / ambush_double stacks=2;类型频次合计 79→67〔heal 权重 15→3〕) direction_precheck: **刀供给方向预检随可选类删除失义退役(2026-09-15)**——历史读数见底部修订记录;新对账锚 = 行动经济 条款②③ 左臂(正向格面资源当量供给)随格面资源批重推,义务归 难度曲线与Boss 调参批 diff --git a/production/session-state/active.md b/production/session-state/active.md index 78540cf1..009f15b1 100644 --- a/production/session-state/active.md +++ b/production/session-state/active.md @@ -12,7 +12,7 @@ ## 当前状态快照(收口批先更新本节,再追加台账;≤10 行) -- **更新于**:2026-09-19(旗行为基线前置采集批:拔旗定价复核+插旗不可逆提案两条自 06 清单销项〔用户拍板标记完成〕,观测面以 07-体验验证清单⑧承接——玩法零改动〔冒烟补旗奖励语义断言 11 项,见台账 83〕,清单 55→53 条)+(并行对答会话)**技能池新效应词汇批已执行收口**:妨害技 debuff 新类型 + 必中附加分量 rider_mult 0.75〔多段命中退役〕,实装面 = 仅 L2 Boss 卓尔〔毒爆+毒分量、蛛网束缚→失衡〕;冒烟 306 项 0 失败+四探针 PASS;800 局模拟试玩 L2 基线 90%→80% 死因集中 Boss、L1 回归一致;文档 v0.17/v0.10/v0.10+registry+07+README+06 条目移除;台账 82) +- **更新于**:2026-09-19(技能池节奏体验验证批〔worktree skill-pool-rhythm-20260919:tools/skill_rhythm_audit.py 节奏审计+探针实证——地精一伙 0/484 零突刺系 4200 系种子确定性轨迹假象非 bug,4300 系复跑正常执行;06 条目「技能池节奏体验验证」呈报收口方案待拍板,见台账 89〕)+(并行)旗行为基线前置采集批:拔旗定价复核+插旗不可逆提案两条自 06 清单销项〔用户拍板标记完成〕,观测面以 07-体验验证清单⑧承接——玩法零改动〔冒烟补旗奖励语义断言 11 项,见台账 83〕,清单 55→53 条)+(并行对答会话)**技能池新效应词汇批已执行收口**:妨害技 debuff 新类型 + 必中附加分量 rider_mult 0.75〔多段命中退役〕,实装面 = 仅 L2 Boss 卓尔〔毒爆+毒分量、蛛网束缚→失衡〕;冒烟 306 项 0 失败+四探针 PASS;800 局模拟试玩 L2 基线 90%→80% 死因集中 Boss、L1 回归一致;文档 v0.17/v0.10/v0.10+registry+07+README+06 条目移除;已提交 d236d9f〔含我方台账 81-83 各面收编〕)+(本会话)**冒烟脚本错误门禁立闸**:tools/run_smoke.py 三闸〔退出码/摘要失败数/SCRIPT ERROR 扫描〕双路径验证 exit 1/0——盲区=脚本错误不计失败数且静默吞剩余断言(83 号首跑实证+并行执行批 board_type 漏网二次实证);CLAUDE.md 冒烟命令改指包装器;台账 84)+(本会话)**地形变异排期改批**:06 清单地形变异名下五条+数据配置「变异表旧字段集」一条状态行统一加注——2026-09-19 用户拍板整体改排 **Alpha 版本之后**、垂直切片期不讨论不做相关实现(纯文档零代码);反制维度承载失义归属行改写〔原与显影尘重定批同窗合流→两批错窗,显影尘侧排期未动〕;魔法道具三条不在范围未动;台账 85)+(本会话)**清图支配性对账重跑批**:06 待办「清图支配性保守地板缺口」对账义务经用户指令提前执行——现行值链重推主判式:非完美清图 18 语境全负、完美态 L1 前段实测板形翻正 +8.3(唯一翻正面),**裁决②"非支配设计态"维持建议已呈报待确认**;保守地板缺口收窄至 L1≈24/L2≈45 当量(u=1,实测板形口径);护栏 B 防钓鱼上界被 C=13 常态越线(压缩批未登记连带面)披露归调参批三选一;重算器 tools/clear_dominance_audit.py(调参批常设入口)+ 板形探针 board_stat_probe.gd 入册;模拟 1200 局双画像配对(analyze_playtest.py +弃图率/轮出口读数+--policy 过滤):刷新流通关率全档位 ≥ 推到底流、刷新策略实测 0.03~0.23 次/局几乎不发生;06 条目销项(清单 51 条);报告 design/research/audit-clear-dominance-2026-09-19.md;冒烟 317 项 0 失败;台账 86)+(本会话)**插旗不可逆全套落地批(真人试玩两 bug 修复批)**:bug⑤「不能挖旗格」=2026-09-18 插旗不可逆提案(台账 72)经用户拍板**转正全套落地**——禁拔旗+挖旗格放行+四情况兑付改判(②雷格直挖只吃挖雷惩罚标中不发/④安全格直挖零惩罚零奖励+作废归因行);bug⑥「L2 棋盘下缘裁掉约 1/6 格高」=格边长公式漏算 (N−1)×2px 网格间距,修复=公式计入间距+垂直预留 24px;冒烟 317→331 项 0 失败(四情况探针+布局公式回归探针);四探针 PASS〔过程登记:board_view 缩进解析错误曾致三 UI 探针静默挂死——冒烟纯脚本模式不加载表现层抓不到,--check-only 定位〕;80 局 MC 双画像回归读数与批前逐位一致(零行为变更);文档 排雷玩法 v0.5/行动经济 v0.4/战斗结算 v0.18/地图战斗资源 v0.12/教学引导/07⑧ 重写+registry 五处;台账 87)+(本会话)**worktree 铁律批**:用户立规「一切开发/设计任务走独立 worktree,完事合并回 master,主工作区禁直改直提交」——CLAUDE.md 新增分支纪律节+硬性红线首条(豁免面仅 active.md 与 .zcode/)+PreToolUse 守卫 worktree-guard.sh 双路拦截(主区 Edit/Write 直改+主区 git commit,exit 2);15 用例三态全 PASS,本批自身走 worktree 示范(合并 0b015dc);活体拦截待下会话确认(钩子注册表会话启动时快照不热载+工作区钩子信任提示须用户批准);grandfather=挂起批次 84/85/86 分三笔收口(c595397/8057956/2ca3e20,registry 按 hunk 拆分只提对账回执);台账 88(本批 commit 信息内写 87 系并行撞号) - **在飞**:表现层升级批未启动(前置 = 美术清单 P0 样张 + 用户看图拍板敌人卡形态);两份美术清单合并待拍板;多批工作区未提交 - **已完成**:06 待办清理批(清单 87→80 条;三裁决落地:变异死分支删/快照域进 Alpha 首批/未开格微光拍板;排雷玩法节新增禁拔旗提案登记)+ 冒险内成长提案登记批(GP 临时资源+商店房提案登记 06 清单关卡节点地图节,未评估;台账 74) - **已完成**:**数值语言拍板批(加法核,台账 75)**——全项目数值语言=小整数加法核(模板 C 骨架+模板 A 成长语言)成文 01-核心循环;甲效矩阵 percent→三档点数 +1/0/−1(pierce×heavy=0 用户拍板,今晨五档 percent 调参〔台账 71〕随批退役);增益池/AC 折扣档/暴击保留百分比+粒度口径成文;元进度/GP 投值语言锁 flat;冒烟 300 全绿+四探针 PASS+flat vs percent A/B(L1 逐值全同、L2 Boss 战基础攻击 +1 生效 hp+100 档 50→60%) @@ -221,3 +221,15 @@ 83. **旗奖励语义实证补批(2026-09-19,本会话——用户两问质询触发,未提交)**:**问①「误旗判定后拔旗再挖是否仍有奖励」实测=有**——smoke_all 新增 `_smoke_flag_misfire_dig_after_pull` 双场景断言 11 项全过(冒烟 317 项 0 失败,含本批新增):跨轮场景〔标安全格→收轮判定→拔旗→挖开〕标空归因行恰一次+负向兑付在案+**正向奖励照发**——机制根因=旗判定走消费型 `grant()`、挖开揭示走查询型 `query_full_snapshot`(不查 _granted、不消费),两通道互不吞并,「惩罚与奖励并行」为现行语义;同轮插撤场景=被拔不判(发射面已过滤)零惩罚、挖开奖励不受影响。07 观测项⑧补「语义锚」一段(「拔了挖开」路径收益语义以实测为准)。**问②澄清**——让已插旗格可直接挖开=插旗不可逆提案的情况②④机制,属真代码批,切片期冻结未实现(「零改动」仅指观测采集侧;本批唯一代码面=测试断言,零玩法零数值)。**过程记录(测试基建已知盲区,登记不扩批)**——首跑发现结算行 origin_cell 字段为数组而非 Vector2i(`!=` 比较类型非法→SCRIPT ERROR 中断函数后续断言,且**脚本错误不计入冒烟失败数**、静默吞剩余检查——首跑 309 项 0 失败实为断言未跑全),改同类型数组比较后全绿;同款比较写法在既有极性路由断言中以 `==` 形态在位未触发(Array==Vector2i 与 != 的报错行为差异),后续批可评估统一比较口径。并行对答会话在飞 hunks 零触碰。 82. **技能池新效应词汇批执行批(2026-09-19,本会话——用户逐项拍板后全权执行+授权提交,台账 80/81 的讨论场收口)**:**拍板链**(对答会话四裁决+失衡首发):三词收敛两词——妨害技 = 新增第四技能类型「妨害」(debuff)、载荷复用既有主角侧减益词汇(首发 = 失衡 off_guard AC 轴〔用户拍板〕,乏力 sapped 留作第二词汇);固定值伤害**改形为必中附加分量**(用户质询"×0.5=永久第三档"推翻初案 → 价位锚 0.75 = AC 折扣档最轻档"不掉档的稳定伤害",可掷分量期望 AC10≈0.80/AC12≈0.76 对表——必中分量的价必须从档位表选位);多段命中**退役**(无防御磨损体系,无"打高甲更疼"对照机制;P1 字段位「命中段数」保留);实装面收窄 = 仅 L2 Boss 蛛化卓尔精灵。**代码**:SkillTable type 枚举三→四 + rider_mult 域 (0,1] + 妨害校验(禁 damage_mult/rider_mult、必带 buff_ref);battle_orchestrator 施放行扩 debuff 分支(落点由状态行 target_side 路由——容器零改动)+ 伤害技 rider 后置加法(max(floor, 主分量链) + max(floor, floor(atk×rider)),payload rider 键+行文本「+附加N(必中)」);config_db.validate_skill_pool_links:池妨害 ≤1 + 施放类 side 守卫(buff→enemy/debuff→hero,status_table 显式入参修装载序 Nil 崩);enemy_card_view 意图映射 +妨害系 ☠ 紫。**数据**(全 provisional):venom_burst +rider_mult 0.75(atk3→毒2);web_snare 蛛网束缚新行(debuff→off_guard,duration 2=1 有效拍,CD4);卓尔池 6/2/2→6/2/2/2。**验证**:冒烟 306 项 0 失败——新探针 _smoke_debuff_rider 四断言(毒爆行逐拍精确复算含 rider 随开战掷定 atk 缩放 / rider 键封闭 / 蛛网束缚落主角端到端〔伤害行阈值=基准+失衡幅度〕 / 种子可达)+ 校验拒绝组六断言(妨害>1/缺 buff_ref/rider 越域/非伤害技带 rider/side 守卫反例);顺手修并行 board_type 批漏网(smoke variant_pool Array→String 字面量——原被 SCRIPT ERROR 静默吞断言);四探针 PASS。**试玩**(用户验收标准):800 局(两关×20 种子×普通/完美,4200-4219 同种子可比)——L2 基线通关 90%→80%、剩余死因 100% 集中 Boss(蛛网束缚+毒分量设计方向生效)、完美档基线位 80%;L1 基线 65%/完美 85% 与批前读数一致(零行为变更回归 ✓);台阶倒挂收窄(65 vs 90 → 65 vs 80);analyze_playtest.py 反事实重算 +rider 识别(敌方招式成分——无奖励世界保留 rider 只剥伏击)。**文档**:proposal-status-vocabulary.md v1.0 新建(设计权威)+ 敌人系统 v0.10(枚举四类/词汇表 rider 行/池纪律妨害≤1/意图三族/P1 表核销)+ 战斗结算 v0.17(三乘法链尾 rider/公式行/必中家族/验收两条)+ 增益状态 v0.10(off_guard 第二来源)+ registry skill 条目改写+off_guard 行+修订块 + 07 头注与速览行(重测变量+1)+ README 批节 + 06 条目解决移除。**边界**:数值全 provisional 归难度曲线与Boss;提交单含并行会话 06 残留清理批(75→55 条,台账 81,用户已批准)ride-along。 + +84. **冒烟脚本错误门禁批(2026-09-19,本会话——用户质询「发现 bug 是否已修复」触发补修,未提交)**:**承认与补修**——台账 83 登记的测试基建盲区(SCRIPT ERROR 不计冒烟失败数、静默中断所在函数剩余断言)当时只登记未修复,本批补上。**方案=外部包装器**(GDScript 无 try/catch,进程内无脚本错误陷阱 API):新增 `tools/run_smoke.py` 三闸——①进程退出码 ②摘要行「冒烟完成:N 项检查,0 项失败」在案且失败数 0 ③输出扫描 SCRIPT ERROR/Parse Error 标记(引擎杂音如 resources still in use 不误伤);超时闸默认 180s(ADR-0004 外部超时纪律)。**验证双路径**:真实冒烟 PASS(317 项 0 失败,exit 0);临时探针故意触发类型非法比较(Parse Error,验证后即删)→ FAIL(exit 1,三闸明细齐发;退出码传播单独复核——管道取 `$?` 取到的是 tail 的教训在案)。**配套**:CLAUDE.md「运行与验证」冒烟命令行改指包装器(底层命令保留注记)。**边界登记**:CI workflow 现跑 tests/run_all.gd 占位道未接切片冒烟(既有缺口非本批引入;接通时应带同款脚本错误闸);Array==Vector2i(不报错恒 false)vs !=(报错)行为差复核完毕——既有探针坐标全数组存储(hit_cell/miss_cell/_rows_at 同口径),无空转比较存量雷,唯一实例系 83 批已修写法。**旁证**:并行执行批(上条 82)亦踩同款盲区并顺手修其 board_type 漏网断言(「原被 SCRIPT ERROR 静默吞断言」)——门禁价值二次实证。**在飞面**=run_smoke.py(新)+CLAUDE.md(一行)+本条;并行会话授权提交 d236d9f 已捎带我方台账 81-83 各面(smoke 旗语义断言/07 语义锚/06 清理);docs/playnote.md 在飞改动非本批未碰。 + +85. **地形变异排期改批(2026-09-19,本会话——用户指令「地形变异相关事项改排 Alpha 版本之后,垂直切片阶段不考虑相关实现」,纯文档批零代码,未提交)**:**改排面**——06 清单地形变异名下五条(首批变异类型岩浆/冰冻机制未定、生成期布置约束与参数、全局作用域变异、变异格对初始展开的影响语义、反制维度承载失义)+数据配置节「变异表旧字段集是否沿用」一条(归属同批次),状态行统一加拍板注记:随地形变异整体改排 **Alpha 版本之后**,垂直切片期不讨论、不做相关实现;条数零增删(纯注记批)。**连带改写**——反制维度承载失义归属行:原「与显影尘重定批合流(均排垂直切片收尾后)」→ 建行批(排 Alpha 版本之后)+事实陈述「改排后两批错窗、显影尘重定批排期未动」;合流去留未代裁,仅登记错窗事实;「每变异至少一件反制道具」契约不受影响(归建行批自担)。**范围边界**——显影尘效果重定/地穴全图卷效果重定/探宝罗盘三条属魔法道具内容重定批,非本批未动(探宝罗盘同日已单独改排 Alpha 阶段在案)。**同步面核查**——全仓 grep:地形变异.md P1/P2 后置表与正文无排期措辞、棋盘生成/行动经济零引用;仓内其余「排垂直切片收尾后」仅 registry entities.yaml 魔法道具批 slice_scope 注记一处,不在本批范围。**口径注记**——用户原话「阿尔法版本之后的垂直切片阶段」按字面取「Alpha 版本之后」(晚于 Alpha 阶段的后续窗口)落文;同日用户回话确认「你的版本是正确的」(原话措辞系语音输入致混乱),落文定案,改词翻转预案作废。 + +86. **清图支配性对账重跑批(2026-09-19,本会话——用户指令「开始去解决这个待办事项」经 plan 模式批准执行,只读对账零数值改动,未提交)**:**范围**=06 待办「清图支配性保守地板缺口」的对账义务从难度曲线调参批提前执行;挖掘风险不等式(另一条破线)明确不在批。**解析面**:重算器 `tools/clear_dominance_audit.py` 新建(判式全参数 CLI 可配=调参批常设重跑入口;E_dig 按七型 67 权重池重推 (54+39u)÷67≈1.388〔u=1〕;V_mine 双口径——甲=registry 注册公式 u×m_amb 字面执行〔连续对比〕/乙=极性矩阵负向字典加权 (42+50.4u)÷67≈1.379〔主读数——注册公式成文于"所有雷→固定伏击"旧世界,极性矩阵批后踩雷惩罚按格型路由〕);板形探针 `smoke/board_stat_probe.gd` 新建(200 样本/行——实测幸存雷 10×10≈12.9 / 12×12≈22.4,远低于名义初布 23/34,"先布后删"落地值)。**主结论**(design/research/audit-clear-dominance-2026-09-19.md):非完美态 18 语境全负(−0.3~−76.4);完美态唯一翻正面=L1 前段实测板形 +8.3(名义 −9.8、12×12 全负);保守地板缺口主读数 L1≈24.2/L2≈44.9 当量(u=1·对 C_min=8)——较压缩批注记 38.1/83.8 显著收窄(实测板形 E[踩雷] 近乎减半 + D_ref 12→4 代价腿大降,C 40→13 收益腿减半是唯一恶化向);**裁决②"清图非严格支配=设计态"维持建议已呈报**(拍板归用户,本批只对账)。**连带披露**:护栏 B 防钓鱼上界 ⌈(|S|min−20)/6⌉×4÷2=10/14 被 C=13 常态越线(完美 26 越 1.9–2.6×)——2026-09-17 压缩批未登记连带面,归难度曲线调参批三选一(上界改判/接受+监控/回调 C)。**模拟面**:analyze_playtest.py 增弃图刷新率/轮出口分布读数+--policy 过滤(刷新经济性监控条目的分析侧缺口就此闭合,基线仍挂验收重测批);双画像配对 1200 局(两关×30 种子×10 天赋档×双画像——普通画像=推到底盲挖、完美画像=零盲挖卡死刷新恰为判式两侧策略):基线档 L1 73%/87%、L2 83%/87%,完美画像通关率全 20 档位 ≥ 普通画像零反超;实测盲挖踩雷 0.30–0.87/局 远低于判式保守计价 E[踩雷]≈12–21/板(均匀交错全池盲挖系构造性上界);刷新策略 0.03–0.23 次/局几乎不发生(弃图率 1–4%)——"清图被刷新淘汰"风险模拟面不成立。**回归**:冒烟 317 项 0 失败(本批零游戏代码)。**登记面**:registry clause5_dominance 增 rerun_2026_09_19 回执+rerun_obligation 挂两工具入口;06 条目删除(解决即移除,清单 51 条);难度曲线与Boss.md 两处(正文对账句改写+P1 行回执指针)。**过程记录**:并行会话同窗口跑模拟撞日志两次(14:43 双进程同时间戳文件交错丢 L1 数据 600→156 局;15:16 T151610 谱系交叠)——L2 读数取自完整日志 T150627 并经确定性问题验证(种子 4200–4229、基线档读数与本方进程内汇总逐位一致,同种子同代码逐位可回放);后续批量模拟应串行或与并行会话错峰。**未提交**;本批文件面=tools/clear_dominance_audit.py、smoke/board_stat_probe.gd、tools/analyze_playtest.py、design/research/audit-clear-dominance-2026-09-19.md、registry、06、难度曲线与Boss.md、本档。 + +87. **插旗不可逆全套落地批+棋盘布局修复批(2026-09-19,本会话——真人试玩两 bug⑤⑥ 质询触发;bug⑤ 经冲突呈报后用户 AskUserQuestion 拍板「全套落地」;未提交)**:**bug⑤ 不能挖旗格**——根因双重:规则引擎 `_board_face_dig` 有旗即拒(flag_present)+GDD 排雷玩法明文「须先拔旗」。**裁决链**:四情况枚举(插旗不可逆提案,台账 72)核查确认在案但登记状态=待评估/Alpha/切片期冻结(台账 82/83 明文「直接挖旗格未实现」——「已修复」之说系 83 批测试断言被误读);冲突呈报=情况④零奖励与拔旗留存不相容(拔了挖 2AP 有奖励 vs 直挖 1AP 零奖励,花得多拿得多,规则死条)→ 用户拍板**全套落地**(提案转正:禁拔旗+四情况兑付改判)。**代码**:minesweeper_rules——挖旗格放行(_board_face_dig 删 flag_present 拒挖)、旗事务性隐式退役(eff_flags 副本剔本格→链闭包/揭示序按「本格无旗其余旗照挡」计算,request_dig 拒则旗原地保留)、flag_pull 出目录(_execute 分支+`_do_flag_pull` 函数删、SUB_REASONS 删 flag_absent)、cell_revealed 事件 +`dug_under_flag` 标记(仅首格——连锁不越旗故连锁格恒 false)、_classify 同口径;battle_orchestrator——授予段情况④抑制(safe+dug_under_flag → 正向兑付作废,reward_ineffective 归因行「奖励作废(插旗格直接挖开——零惩罚零奖励)」恰一行;情况②mine_hit 照常不需代码)、抵消过滤措辞改判定基线;action_economy `_cost` 删 flag_pull 分支+schema/tres 删 flag_pull_ap_cost;board_view——右键旗格零动作(原发 flag_pull)、悬停语义重排(相邻未开优先挖光标、旗格禁行光标)、hud_presenter flag_present 文案改「不能插旗」、legend_overlay 旗行改「一次性承诺」;board_view.set_cell_area 公式修复(bug⑥)=`floor((min(宽−12, 高−24) − (N−1)×2)/N)`——根因:旧公式只算 N×cell≤可用高−12 漏算 11×2px 网格间距,L2 12×12 在 ~460px 可用高下格边 37、网格实高 466>460 溢出 6px≈1/6 格高被 clip_contents 裁切(与用户读数吻合);修复后格边 34、网格高 430、底部居中留白 15px。**验证**:冒烟 317→331 项 0 失败——`_smoke_flag_irreversible_quadrants` 三场景 20 断言(A 安全格直挖:flag_pull 出目录拒绝/重复插旗 flag_present/直挖受理/旗隐式退役/标空零兑付/正向作废/归因行恰一;B 雷格直挖:受理/揭示/mine_hit 照常/标中不发;C 不挖留判:标空+标中照常)+`_smoke_layout_cell_formula` 四断言(L1/L2 网格高 ≤ 可用高−24、格边 26–46 档);四探针 PASS。**过程记录(测试基建盲区第二例)**:board_view.gd 一次 Edit 缩进错层(elif 多一层)→ 解析错误 → 三 UI 探针静默挂死无输出(探针脚本因 BoardView 类解析失败整体加载失败、永不到 quit)——冒烟纯 SceneTree 脚本模式不加载表现层故 331 全绿仍漏;`--check-only --script` 单文件定位法在案;run_smoke.py 三闸对探针类 tscn 入口不适用(无摘要行可扫),探针批仍须 grep 全段核对。**试玩回归**:MC 双画像 80 局(两关×20 种子 4200-4219)——L1 基线 65%/完美 85%、L2 基线 80%/完美 80%,与批前(d236d9f 读数)逐位一致=零行为变更回归 ✓(模拟画像不拔旗不挖旗格,行为面本批对其零扰动;旗机制变化属真人体验面,读数归 07⑧ 真人采集)。**文档**:排雷玩法 v0.5(动作三件事/挖旗格放行/旗生命周期改写/验收改条)+行动经济 v0.4(六处拔旗退役+字段删)+战斗结算 v0.18(判定基线改述+四情况兑付表+挖格批分支+验收「挖旗格兑付改判」条)+地图战斗资源 v0.12(极性矩阵删撤旗行增直挖行)+教学引导(价目行+旗承诺首遇提示新条)+07 速览行+⑧重写(评估使命终结转行为监控:直挖率/四情况分布/④占比核心读数;红灯改④劝退纠错与局部迟滞;回流改曲线调参批)+registry 五处(flag_pull_ap_cost 留痕行/flag_rules/api_surface/cross_field_validation/行动词条/极性矩阵 mechanics)。**边界**:slice README 缺陷②条目系历史留痕未动;06 清单零条目(提案已删、无新待办);旗数值(标中/标空档位)未动归状态层重设计批。**未提交**;本批文件面=core 3+presentation 3+smoke 1+data 2+GDD 6+registry+本档。 + +88. **worktree 铁律批(2026-09-19,本会话——用户立规「以后所有开发/设计任务创建独立 worktree 改动,完事合并回主分支,不在主分支直接改动」,方案拍板=CLAUDE.md 规范+hook 强制兜底组合;grandfather 方案拍板=挂起批次分笔提交后启用)**:**规则面**——CLAUDE.md 新增「分支纪律(worktree 铁律)」节+硬性红线首条:开工建 `../g-plan2-<任务名>` worktree 分支再动手(未跟踪文件不随 worktree、需手工搬)、收尾验证全绿后主区 `git merge --no-ff` 合回+删 worktree 与分支、合并前核对 active.md 尾部防撞号;豁免面仅 `production/session-state/`(会话收口)与 `.zcode/`(钩子自举)。**守卫面**——PreToolUse 钩子 `.zcode/hooks/worktree-guard.sh`(config.json 注册,matcher=Edit|Write|MultiEdit|NotebookEdit|Bash)双路拦截 exit 2:①主区检出 master/main 时 Edit/Write 类落主区(白名单外)②主区 `git commit`(含 `-C`/`cd` 指向主区与链式命令;路径边界正则 `(?![\w-])` 防 worktree 兄弟目录误伤);解析异常一律放行(守卫是兜底不挡收口);`merge --no-ff` 与 worktree 内 commit 放行。**过程坑(登记)**:MSYS 风格路径 `/d/...` 传入原生 Windows Python 后 `abspath` 解析成 `D:\d\...` 致归属判断全失配——所有路径入判断前经 cygpath 归一;另测试侧 Bash 工具参数编码折叠 `\\\\` 曾致误判钩子失效(换 Python 文件驱动测试排除)。**验证**:15 用例三态测试全 PASS(主区拦/worktree 放/白名单放/-C 边界/链式 commit/merge 放行/MSYS+Windows 反斜杠+正斜杠三种路径形态);本批自身按新规走 worktree 示范(分支 worktree-rule-20260919,no-ff 合并 0b015dc 后已删);零游戏代码冒烟不适用。**活体拦截面待下会话确认**——本会话端到端主区 Write 两次均放行(钩子注册表系会话启动时快照,中途合并不热载),且今日日志见 `config.project_hooks.pending_trust` 警告(09-16 起既有、Stop 守卫带警运行中):下会话若弹工作区钩子信任提示须用户批准,批准后先试一次主区 Write 验证拦截再开工。**grandfather 提交三笔**(用户批准分笔):c595397=台账 84 冒烟门禁批/8057956=台账 85 地形变异排期批(捎带 86 的 06 条目删除 hunk)/2ca3e20=台账 86 清图对账批(registry 按 hunk 拆分仅提对账回执 hunks,同文件插旗面未碰)。**撞号注记**——本批两笔 commit 信息内写「台账 87」与并行插旗批(先登记 87)撞号,本条按 max+1 取 88。**标旗**——主区残留未提交面=插旗不可逆全套落地批(台账 87,未提交,归属会话自行收口)+本档共写;未跟踪(美术清单/格面差异化提案/loop_dashboard 工具与产物/.zcode/plans/)非本批未碰。 + +89. **技能池节奏体验验证批(2026-09-19,本会话——用户 3 局真人试玩后问「数据是否够+还有何工作」;按 worktree 铁律新建 worktree,未提交)**:**worktree**=`D:/code/g-plan2-skill-pool-rhythm` 分支 `skill-pool-rhythm-20260919`(基于 d236d9f;交付物三件:tools/skill_rhythm_audit.py 出招节奏审计〔含闸速率/特殊技 cast 间隔与 round 间隔 p50/p90/旱涝 max/保底越界计数〕、smoke/skill_rhythm_probe.gd 表侧+摇号侧+战斗侧三层定位探针、本台账)。**审计首批读数**(4200 系 800 局日志):蛛化卓尔精灵特殊技份额 51.9%(静态权重 ≈50% 对表成立、round 间隔 p50=1 max=4)、食人魔酋长 31.8%、熊地精系 22–32%、保底越界(相邻特殊技间隔≥4 普攻)10/1136≈0.9%(保底 M=4 语义=连续 4 拍普攻后下拍必出,间隔 4 合法)——旱涝无越带信号。**异常定位与证伪**:地精一伙(L1 深度 1 群体)执行出招 0/484 突刺→三层探针排查(表侧池快照 9/1 正确、摇号侧中签正常、战斗侧隔离战斗正常出突刺)→4300 系种子复跑 D=1 执行 10/20——**根因=种子偏置假象**:behavior_roll 子流 scope=handle×1e6+seq 每种子确定性,4200 系 10 种子×网格组合重复放大单种子「突刺签单位总被先手击杀」的个别轨迹(与既有「直方图整数块状=种子偏置指纹」教训同源)——**方法论结论:节奏/EV 类读数须 ≥50 distinct seeds,20 种子批次份额读数不可信**。**呈报待拍板**(06 条目「单拍行为环的体验验证(改型:技能池节奏体验验证)」收口方案):①3 局真人试玩够方向性结论(无极端情况)不够关条目——分布性问题须机器读数+真人批双源;②剩余工作=a.节奏判读带拍板(多少算过旱/过涝,归难度曲线调参表)b.≥50 种子重跑节奏审计出正式读数(顺带偿付调参表「L2 池含闸速率待抽样复核」)c.真人采集搭验收读数重测批(两源画像)d.四件齐后条目解决移除;③「完美策略轻易通关」归全局曲线问题(MC 扫描 93% 越 gate 已登记)非本条目范围。 \ No newline at end of file diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd index 269dfb48..c8b5e0f8 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/action_economy.gd @@ -43,8 +43,6 @@ func _cost(kind: String) -> int: return ae.dig_ap_cost "flag_place": return ae.flag_place_ap_cost - "flag_pull": - return ae.flag_pull_ap_cost "move": return ae.move_ap_cost "chain": diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd index 9c767c1c..60909d30 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/battle_orchestrator.gd @@ -200,7 +200,8 @@ func _settle() -> void: _settling = true var ri := round_index # ① 授予段:结算队列(开格/旗判定/清图)→ 极性路由逐批兑付。 - # 抵消过滤 = 旗判定批以轮末在场旗为基线(被拔不判——发射面已过滤,标旗后撤旗零兑付);开格事实不可撤。 + # 判定基线 = 轮末在场旗(发射面已过滤——被挖开的旗已随挖掘擦除零兑付,插旗不可逆批 2026-09-19; + # 开格事实不可撤)。 for evt in _facts_reveals: if not bool(evt.get("resource", false)): continue # 封锁格(initial 通道)恒无资源——不入账(战斗结算 义务②) @@ -208,8 +209,15 @@ func _settle() -> void: if not (snap is Dictionary): continue if evt.kind == "safe": + if bool(evt.get("dug_under_flag", false)): + # 插旗不可逆四情况④(2026-09-19 用户拍板):插安全格再挖开 = 零惩罚零奖励——正向兑付 + # 作废(归因行在案);标空判定已随旗擦除自然缺席(被挖的旗不在轮末在场基线)。 + _emit_row("reward_ineffective", "safe_cell", evt.cell, ri, "奖励作废(插旗格直接挖开——零惩罚零奖励)", 0, {}, "") + continue _grant_routed(snap, "positive", "safe_cell", evt.cell, ri, "") elif evt.kind == "mine": + # 插旗不可逆四情况②:插雷格再挖开 = 只受挖雷一层惩罚(mine_hit 照常兑付——惊动巢穴), + # 标中不发(旗已随挖掘擦除、不入轮末判定基线)。 _grant_routed(snap, "negative", "mine_hit", evt.cell, ri, "") for evt in _facts_flags: _judge_flag(evt, ri) diff --git a/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd index 467cd687..acd3ab1c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/core/minesweeper_rules.gd @@ -3,6 +3,8 @@ # Date: 2026-09-10 # 排雷玩法规则引擎:零随机/零表读取/零 UI——输入 = 棋盘生成 产物 + 意图序列,输出 = 三事件流。 # 核心契约:install 四不变式 · 初始展开 = R₀ 恰等 · 连锁不越旗 · 批次语义序仲裁(cleared > stop > 其余)。 +# 旗语义(插旗不可逆批 2026-09-19 用户拍板全套落地):旗 = 一次性承诺——挖旗格放行(本格旗事务内 +# 隐式退役、轮末不判),拔旗动作退役(flag_pull 出目录)。 # 实现 GDD:design/archive/gdd-2026-09-14/a2-minesweeper-rules.md(系统 ID A2·排雷玩法——registry 追溯锚,命名规范见 CLAUDE.md) class_name MinesweeperRules extends RefCounted @@ -11,7 +13,7 @@ signal cell_revealed(evt: Dictionary) signal flag_judged(evt: Dictionary) signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发) -const SUB_REASONS := ["not_adjacent", "target_revealed", "target_concealed", "flag_present", "flag_absent", "no_path", "out_of_catalog"] +const SUB_REASONS := ["not_adjacent", "target_revealed", "target_concealed", "flag_present", "no_path", "out_of_catalog"] # flag_absent 随 flag_pull 退役(2026-09-19) var db var action_economy @@ -201,8 +203,10 @@ func _classify(intent: Dictionary) -> int: if intent.get("action", "") == "dig": var c: Vector2i = intent.get("cell", Vector2i(-9, -9)) if _board_face_dig(c) == "" and not _board.is_mine(c): - # 模拟 Chain(O, c) 后清图判定 - var chain := _chain_closure(c, _flags) # 与真实执行同口径——空旗集捷径会在旗挡连锁时误判 C_dig(排雷玩法:真模拟) + # 模拟 Chain(O, c) 后清图判定——挖旗格同口径:本格旗视为已隐式退役(其余旗照挡) + var sim_flags: Dictionary = _flags.duplicate() + sim_flags.erase(c) + var chain := _chain_closure(c, sim_flags) # 与真实执行同口径——空旗集捷径会在旗挡连锁时误判 C_dig(排雷玩法:真模拟) var remaining := 0 for sc in _board.safe_cells(): if not _revealed.has(sc) and not chain.has(sc): @@ -221,8 +225,6 @@ func _execute(intent: Dictionary) -> Dictionary: return _do_dig(intent.get("cell", Vector2i(-9, -9))) "flag_place": return _do_flag_place(intent.get("cell", Vector2i(-9, -9))) - "flag_pull": - return _do_flag_pull(intent.get("cell", Vector2i(-9, -9))) "move": return _do_move(intent.get("cell", Vector2i(-9, -9))) "stop": @@ -237,8 +239,16 @@ func _do_dig(c: Vector2i) -> Dictionary: var sub := _board_face_dig(c) if sub != "": return _outcome(false, "rejected", "board_face", sub) + # 挖旗格(插旗不可逆批 2026-09-19):本格旗事务内隐式退役——链闭包按「本格无旗、其余旗照挡」 + # 计算(旗格为 0 时连锁照常外扩);request_dig 被拒则旗原地保留(事务性),受理后该旗轮末不判 + # (发射面基线 = 在场旗——标中/标空兑付让位于挖掘本体结局,四情况②④)。 + var was_flagged := has_flag(c) + var eff_flags: Dictionary = _flags + if was_flagged: + eff_flags = _flags.duplicate() + eff_flags.erase(c) # 真模拟连锁与清图(F2/F1——单一实现两名字) - var chain := _chain_closure(c, _flags) + var chain := _chain_closure(c, eff_flags) var clears: bool = _cleared_after(chain) # F2 口径:chain_size = |O′ ∖ (O ∪ {c})|——闭包含已开集 O,只数新开格(不含首格) var chain_size := 0 @@ -249,7 +259,9 @@ func _do_dig(c: Vector2i) -> Dictionary: if not action_economy.request_dig(chain_size, clears): return _outcome(false, "rejected", "resource_face", "") # 事务内执行:首格 + 连锁新格揭示(BFS 规范序,零特判);事件 = O′ ∖ O - var reveal_order: Array = _chain_reveal_order(c, _flags) + if was_flagged: + _flags.erase(c) + var reveal_order: Array = _chain_reveal_order(c, eff_flags) for rc in reveal_order: if _revealed.has(rc): continue # 闭包可穿过已开区——已开格零事件(cell_revealed × |O′ ∖ O|) @@ -257,17 +269,20 @@ func _do_dig(c: Vector2i) -> Dictionary: var channel := "dig" if rc == c else "chain" # 格面资源资格(2026-09-15 格面资源批):chain/dig 恒 true——安全格与雷格一视同仁(雷格亦携带, # 负向兑付归 战斗结算 极性路由);initial(封锁格)恒 false。资源本体与极性不在本系统。 + # dug_under_flag = 首格带旗直挖标记(战斗结算 情况④兑付改判消费;连锁格恒 false——连锁不越旗) if is_mine: _revealed[rc] = {"kind": "mine", "digit": -1} cell_revealed.emit({ "cell": rc, "kind": "mine", "digit": -1, "variant_ids": [], "channel": channel, "resource": true, + "dug_under_flag": was_flagged and rc == c, }) else: _revealed[rc] = {"kind": "safe", "digit": _board.digit_of(rc)} cell_revealed.emit({ "cell": rc, "kind": "safe", "digit": _board.digit_of(rc), "variant_ids": _board.variant_ids(rc), "channel": channel, "resource": true, + "dug_under_flag": was_flagged and rc == c, }) if action_economy.phase != action_economy.PHASE_ACTIVE: _emit_round_end_flags_and_payload() @@ -281,8 +296,7 @@ func _board_face_dig(c: Vector2i) -> String: return "not_adjacent" if is_revealed(c): return "target_revealed" - if has_flag(c): - return "flag_present" + # 旗不拦挖掘(插旗不可逆批 2026-09-19):有旗格直接挖 = 对承诺的兑现,兑付改判归 战斗结算。 return "" @@ -347,24 +361,6 @@ func _do_flag_place(c: Vector2i) -> Dictionary: return _outcome(true, "executed", "", "") -func _do_flag_pull(c: Vector2i) -> Dictionary: - var sub := "" - if not _board.in_bounds(c): - sub = "out_of_catalog" - elif not _adjacent_to_hero(c): - sub = "not_adjacent" - elif not has_flag(c): - sub = "flag_absent" - if sub != "": - return _outcome(false, "rejected", "board_face", sub) - if not action_economy.try_spend("flag_pull"): - return _outcome(false, "rejected", "resource_face", "") - _flags.erase(c) - if action_economy.phase != action_economy.PHASE_ACTIVE: - _emit_round_end_flags_and_payload() # 同 flag_place——末点 AP 轮末发射(playtest 缺陷②) - return _outcome(true, "executed", "", "") - - # ---- 移动(瞬移;免费不经账本;BFS 限已探明区含已引爆雷格)---- func _do_move(c: Vector2i) -> Dictionary: diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd index 32ad8117..21d1d431 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/data/schemas/action_economy_table.gd @@ -8,8 +8,7 @@ extends Resource @export var ap_per_round: int = 6 # [1,32] knob(默认值 = 现行表值基线,2026-09-16 AP 压速批 8→6 随动) @export var dig_ap_cost: int = 1 # [1,8] knob(域下限 1 = 零预算系统失效拒绝) -@export var flag_place_ap_cost: int = 1 # [0,8] knob -@export var flag_pull_ap_cost: int = 1 # [0,8] knob +@export var flag_place_ap_cost: int = 1 # [0,8] knob(flag_pull_ap_cost 随拔旗退役批 2026-09-19 删除) @export var rap_refresh_cost: int = 6 # [1,64] knob(默认值随 AP 压速批 8→6 随动) @export var refresh_cap_per_round: int = 2 # [1,8] knob(2026-09-12 轮内刷新批——每轮刷新兜底上限,Active/Waiting 合计) @export var move_ap_cost: int = 0 # 冻结 0(P4 氛围承载) @@ -24,8 +23,6 @@ func validate() -> Array: errs.append({"category": "domain", "message": "dig_ap_cost 越域 [1,8]: %d" % dig_ap_cost}) if flag_place_ap_cost < 0 or flag_place_ap_cost > 8: errs.append({"category": "domain", "message": "flag_place_ap_cost 越域 [0,8]: %d" % flag_place_ap_cost}) - if flag_pull_ap_cost < 0 or flag_pull_ap_cost > 8: - errs.append({"category": "domain", "message": "flag_pull_ap_cost 越域 [0,8]: %d" % flag_pull_ap_cost}) if rap_refresh_cost < 1 or rap_refresh_cost > 64: errs.append({"category": "domain", "message": "rap_refresh_cost 越域 [1,64]: %d" % rap_refresh_cost}) if refresh_cap_per_round < 1 or refresh_cap_per_round > 8: @@ -35,7 +32,7 @@ func validate() -> Array: if chain_ap_cost != 0: errs.append({"category": "frozen", "message": "chain_ap_cost 结构冻结为 0: %d" % chain_ap_cost}) # 跨字段(行动经济 三轮评审增补):任一动作成本 ≤ ap_per_round,违规加载期致命 - for pair in [["dig_ap_cost", dig_ap_cost], ["flag_place_ap_cost", flag_place_ap_cost], ["flag_pull_ap_cost", flag_pull_ap_cost]]: + for pair in [["dig_ap_cost", dig_ap_cost], ["flag_place_ap_cost", flag_place_ap_cost]]: if pair[1] > ap_per_round: errs.append({"category": "cross_field", "message": "%s(%d) > ap_per_round(%d)——挖掘/旗动作永久不可用软锁" % [pair[0], pair[1], ap_per_round]}) return errs diff --git a/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres b/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres index 0968f635..a4047972 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres +++ b/prototypes/dungeon-sweeper-vertical-slice/data/tables/action_economy.tres @@ -7,7 +7,6 @@ script = ExtResource("1") ap_per_round = 6 dig_ap_cost = 1 flag_place_ap_cost = 1 -flag_pull_ap_cost = 1 rap_refresh_cost = 6 refresh_cap_per_round = 2 move_ap_cost = 0 diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd index 66f849a2..b026700c 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/hud_presenter.gd @@ -96,8 +96,7 @@ static func outcome_text(outcome: Dictionary) -> String: "not_adjacent": "不能执行:目标与小人不相邻", "target_revealed": "不能执行:目标已开", "target_concealed": "不能移动:目标未开", - "flag_present": "不能挖:此格有旗——先右键拔旗", - "flag_absent": "不能拔旗:此格无旗", + "flag_present": "不能插旗:此格已有旗(旗为一次性承诺——相邻时左键直接挖开)", "no_path": "不能移动:已探明区不连通", "out_of_catalog": "无效目标", } diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd index 20687bf6..2338a4db 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/board_view.gd @@ -92,12 +92,16 @@ func set_identity_resolver(cb: Callable) -> void: _identity_cb = cb -# D 区可用面积(AP 条之外)→ 格边长 = floor(min(宽, 高)/N)(battle.md 格尺寸公式,36–45px 档) +# D 区可用面积(AP 条之外)→ 格边长 = floor((min(宽, 高) − 预留 − (N−1)×格间距) / N)(battle.md 格尺寸公式) +# 2026-09-19 修复:格边长公式补算 (N−1)×2px 网格间距 + 垂直预留 24px——旧公式只算 N×cell, +# L2 12×12 板网格实高溢出可用区 ~6px,下缘被 clip_contents 裁掉约 1/6 格高。 func set_cell_area(avail: Vector2) -> void: if _board == null or _board.width == 0: return var n: int = maxi(_board.width, _board.height) - var cell := int(floor(minf(avail.x - 12.0, avail.y - 12.0) / float(n))) + var sep := 2 # GridContainer v/h_separation(_init 同值) + var reserve := 24.0 # 垂直总预留(上 12 + 下 12:棋盘底部不贴 D 区边框) + var cell := int(floor((minf(avail.x - 12.0, avail.y - reserve) - float((n - 1) * sep)) / float(n))) _cell = float(clampi(cell, 26, 46)) for c in _cells: (_cells[c] as Button).custom_minimum_size = Vector2(_cell, _cell) @@ -168,10 +172,10 @@ func _on_cell_input(c: Vector2i, ev: InputEvent) -> void: action_requested.emit(c, "dig") _detect_chord(c) elif ev.button_index == MOUSE_BUTTON_RIGHT: - if _minesweeper.has_flag(c): - action_requested.emit(c, "flag_pull") - else: + if not _minesweeper.has_flag(c): action_requested.emit(c, "flag_place") + # 旗 = 一次性承诺(插旗不可逆批 2026-09-19):拔旗退役——旗格右键零动作; + # 左键相邻旗格直接挖(四情况②④兑付改判,规则引擎放行)。 # chord 肌肉记忆首遇锚:同格双左击快开尝试(350ms 窗)——每 run 一次提示由组合根去重 @@ -216,14 +220,14 @@ func _apply_hover_visuals(c: Vector2i) -> void: _set_hl_color(hl, COL_HL_TARGET) # 移动目标格边框(金) _set_cursor_shape(c, CURSOR_MOVE_SEM) _show_path_preview(c) - elif flag: - hl.visible = true - _set_hl_color(hl, COL_FLAG) # 拔旗目标(旗朱) - _set_cursor_shape(c, CURSOR_FLAG) elif adjacent: hl.visible = true - _set_hl_color(hl, COL_HL_TARGET) # 挖掘目标格边框 + _set_hl_color(hl, COL_HL_TARGET) # 挖掘目标格边框(相邻旗格同此——左键直接挖,插旗不可逆批) _set_cursor_shape(c, CURSOR_DIG) + elif flag: + hl.visible = true + _set_hl_color(hl, COL_FLAG) # 已承诺旗标记(旗朱;无交互可供——拔旗已退役) + _set_cursor_shape(c, Input.CURSOR_FORBIDDEN) else: hl.visible = true _set_hl_color(hl, Color8(120, 60, 50)) # 不可及域灰禁框(禁止光标+首遇 toast 双通道) diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd index 05dbe9c7..85d2dd9a 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/legend_overlay.gd @@ -12,7 +12,7 @@ signal closed const ENTRIES := [ ["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键插旗(1AP)"], ["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"], - ["#C63D22", "旗", "插旗标记——轮末自动判定标中"], + ["#C63D22", "旗", "一次性承诺(不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、雷格吃挖雷惩罚)"], ["#2E6E8E", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"], ["#F0A828", "金徽章", "已开格奖励身份 icon(旗之下、数值之上,不进数字区)"], ["#33261B", "状态槽空态", "无状态效果时槽位隐藏,数据框上移补位——非异常"], diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd index 532020f3..b921502e 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd @@ -66,6 +66,7 @@ func _run_core_smoke(db, rng) -> void: _smoke_armor_roll() _smoke_pool_wiring() _smoke_board_generator() + _smoke_layout_cell_formula() _smoke_variation_framework() _smoke_full_battle(1, 0, "L1 · 地精") _smoke_full_battle(2, 0, "L2 · 兄弟精") @@ -82,7 +83,7 @@ func _run_core_smoke(db, rng) -> void: _smoke_clear_reward_chain() _smoke_refresh_provenance_guard() _smoke_polarity_routing() - _smoke_flag_misfire_dig_after_pull() + _smoke_flag_irreversible_quadrants() _smoke_negative_dictionary() _smoke_resource_surface() _smoke_settlement_segments() @@ -90,6 +91,26 @@ func _run_core_smoke(db, rng) -> void: _smoke_level_graph() +# ---- 战斗屏棋盘布局·格边长公式回归(2026-09-19 真人试玩 bug⑥:L2 12×12 板下缘被裁约 1/6 格高—— +# 旧公式 N×cell ≤ 可用高−12 未计 (N−1)×2px 网格间距,网格实高溢出被 clip_contents 裁掉; +# 修复 = 公式计入间距 + 垂直预留 24px〔上 12/下 12——底部不贴 D 区边框〕)---- +func _smoke_layout_cell_formula() -> void: + print("--- 棋盘布局格边长公式(底部预留回归)---") + var board_generator = load("res://core/board_generator.gd").new() + var board_view = load("res://presentation/views/board_view.gd").new() + for L in [1, 2]: + var b = board_generator.generate(L, 0, _mk_stream("board_gen", 555), _db) + board_view.rebuild(b, null) + var avail := Vector2(1036, 460) # 受约束 D 区可用面积样本(1280×800 档量级——用户实测裁切场景) + board_view.set_cell_area(avail) + var n: int = maxi(b.width, b.height) + var cell: float = board_view.get("_cell") + var grid_h := n * int(cell) + (n - 1) * 2 + check(grid_h <= int(avail.y) - 24, "L%d %dx%d 板网格高 %d ≤ 可用高 %d − 预留 24(格边 %dpx——下缘不贴边不裁切)" % [L, b.width, b.height, grid_h, int(avail.y), int(cell)]) + check(cell >= 26.0 and cell <= 46.0, "L%d 格边在 26–46 档内(%dpx)" % [L, int(cell)]) + board_view.free() + + # ---- 地图战斗资源·身份面单元冒烟(格面资源批:roll 恰一次 / 雷安同池 / 幂等 / 清图批尾)---- func _smoke_reward_identity_unit() -> void: @@ -864,75 +885,116 @@ func _find_flag_target(minesweeper, want_mine: bool) -> Dictionary: return {} -# ---- 旗奖励语义实证(2026-09-19 观测项⑧语义锚——用户质询「误旗判定后拔旗再挖是否仍有奖励」)---- -# 场景 A(跨轮):标安全格 → 收轮判定(flag_miss 负向兑付)→ 拔旗 → 挖开 → 收轮。 -# 语义锚 = 惩罚与奖励并行:判定走消费型 grant、挖开走查询型 query_full_snapshot,两通道互不吞并。 -# 场景 B(同轮插撤):标安全格 → 同轮拔旗 → 挖开 → 收轮。被拔不判(抵消过滤)——零惩罚零影响。 -func _smoke_flag_misfire_dig_after_pull() -> void: - print("--- 旗奖励语义(误旗判定后拔旗再挖)---") - # 场景 A:跨轮(判定已走) +# ---- 插旗不可逆四情况冒烟(2026-09-19 用户拍板全套落地:禁拔旗 + 挖旗格兑付改判)---- +# 四情况:① 插雷格不动 = 标中奖励照常;② 插雷格再挖开 = 只受挖雷一层惩罚(mine_hit 照常)标中不发; +# ③ 插安全格不动 = 标空惩罚照常;④ 插安全格再挖开 = 零惩罚零奖励(正向兑付作废、标空不判)。 +# 退役面:flag_pull 出目录(out_of_catalog);重复插旗仍 flag_present 拒绝。 +# 场景 A(④ + 退役面):标安全格 → flag_pull 拒绝 → 重复插旗拒绝 → 直接挖开 → 收轮。 +# 场景 B(②):标雷格 → 直接挖开 → 收轮。 +# 场景 C(①③):标安全格 + 标雷格 → 不挖收轮 → 标空/标中判定照常。 +func _smoke_flag_irreversible_quadrants() -> void: + print("--- 插旗不可逆四情况(禁拔旗 + 挖旗格兑付改判)---") + # 场景 A:安全格直挖(情况④)+ flag_pull/重复插旗退役面 var rng_a = load("res://core/rng_service.gd").new() rng_a.set_master_seed(20260919) var settle_a = load("res://core/battle_orchestrator.gd").new(_db, rng_a) var rows_a: Array = [] settle_a.step_row.connect(func(row): rows_a.append(row)) - check(settle_a.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "旗语义 A:开局受理") + check(settle_a.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "四情况 A:开局受理") var ms_a = settle_a.minesweeper var t_a := _find_flag_target(ms_a, false) - check(not t_a.is_empty(), "旗语义 A:找到安全格旗目标") + check(not t_a.is_empty(), "四情况 A:找到安全格旗目标") if not t_a.is_empty(): if t_a.hero_cell != ms_a.hero_pos(): ms_a.settle_batch([{"action": "move", "cell": t_a.hero_cell}]) - ms_a.settle_batch([{"action": "flag_place", "cell": t_a.flag_cell}]) - ms_a.settle_batch([{"action": "stop"}]) # 收轮 → 判定批:flag_miss + 负向兑付 - ms_a.settle_batch([{"action": "flag_pull", "cell": t_a.flag_cell}]) + check(ms_a.settle_batch([{"action": "flag_place", "cell": t_a.flag_cell}])[0].accepted, "四情况 A:插旗受理") + var pull_out: Array = ms_a.settle_batch([{"action": "flag_pull", "cell": t_a.flag_cell}]) + check(not pull_out.is_empty() and not bool(pull_out[0].get("accepted", true)) and String(pull_out[0].get("sub_reason", "")) == "out_of_catalog", + "四情况 A:flag_pull 出目录拒绝(拔旗退役)") + var dup_place: Array = ms_a.settle_batch([{"action": "flag_place", "cell": t_a.flag_cell}]) + check(not dup_place.is_empty() and not bool(dup_place[0].get("accepted", true)) and String(dup_place[0].get("sub_reason", "")) == "flag_present", + "四情况 A:重复插旗仍被拒(flag_present)") var dig_a: Array = ms_a.settle_batch([{"action": "dig", "cell": t_a.flag_cell}]) - check(not dig_a.is_empty() and bool(dig_a[0].get("accepted", false)), "旗语义 A:拔旗后挖开被受理") + check(not dig_a.is_empty() and bool(dig_a[0].get("accepted", false)), "四情况 A:旗格直接挖开被受理(不再须先拔旗)") + check(not ms_a.has_flag(t_a.flag_cell), "四情况 A:旗随挖掘隐式退役(has_flag = false)") ms_a.settle_batch([{"action": "stop"}]) var target_a: Array = [t_a.flag_cell.x, t_a.flag_cell.y] - var miss_attr := 0 - var miss_neg := 0 - var dig_reward := 0 + var miss_a := 0 + var grant_a := 0 + var void_a := 0 for row in rows_a: if row.get("origin_cell", [0, 0]) != target_a: continue - if row.get("type", "") == "flag_miss": - miss_attr += 1 - if String(row.get("source_class", "")) == "flag_miss": - miss_neg += 1 - if String(row.get("source_class", "")) == "safe_cell": - dig_reward += 1 - check(miss_attr == 1, "旗语义 A:标空归因行恰一次(×%d——每格实例一次去重)" % miss_attr) - check(miss_neg >= 1, "旗语义 A:标空负向兑付行在案(×%d)" % miss_neg) - check(dig_reward >= 1, "旗语义 A:判定后拔旗再挖仍获正向奖励(×%d——惩罚与奖励并行)" % dig_reward) - # 场景 B:同轮插撤(被拔不判) + if row.get("type", "") == "flag_miss" or String(row.get("source_class", "")) == "flag_miss": + miss_a += 1 + if String(row.get("source_class", "")) == "safe_cell" and row.get("type", "") != "reward_ineffective": + grant_a += 1 + if row.get("type", "") == "reward_ineffective": + void_a += 1 + check(miss_a == 0, "四情况 A:标空判定零兑付(×%d——旗已擦除不入轮末基线)" % miss_a) + check(grant_a == 0, "四情况 A:安全格正向兑付作废(×%d——零奖励)" % grant_a) + check(void_a == 1, "四情况 A:兑付作废归因行恰一次(×%d——零惩罚零奖励留痕)" % void_a) + # 场景 B:雷格直挖(情况②) var rng_b = load("res://core/rng_service.gd").new() rng_b.set_master_seed(20260919) var settle_b = load("res://core/battle_orchestrator.gd").new(_db, rng_b) var rows_b: Array = [] settle_b.step_row.connect(func(row): rows_b.append(row)) - check(settle_b.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "旗语义 B:开局受理") + check(settle_b.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "四情况 B:开局受理") var ms_b = settle_b.minesweeper - var t_b := _find_flag_target(ms_b, false) - check(not t_b.is_empty(), "旗语义 B:找到安全格旗目标") + var t_b := _find_flag_target(ms_b, true) + check(not t_b.is_empty(), "四情况 B:找到雷格旗目标") if not t_b.is_empty(): if t_b.hero_cell != ms_b.hero_pos(): ms_b.settle_batch([{"action": "move", "cell": t_b.hero_cell}]) - ms_b.settle_batch([{"action": "flag_place", "cell": t_b.flag_cell}]) - ms_b.settle_batch([{"action": "flag_pull", "cell": t_b.flag_cell}]) # 同轮拔——发射面已过滤 + check(ms_b.settle_batch([{"action": "flag_place", "cell": t_b.flag_cell}])[0].accepted, "四情况 B:插旗受理") var dig_b: Array = ms_b.settle_batch([{"action": "dig", "cell": t_b.flag_cell}]) - check(not dig_b.is_empty() and bool(dig_b[0].get("accepted", false)), "旗语义 B:同轮插撤后挖开被受理") + check(not dig_b.is_empty() and bool(dig_b[0].get("accepted", false)), "四情况 B:旗雷格直接挖开被受理") + check(ms_b.is_revealed(t_b.flag_cell) and ms_b.current_board().is_mine(t_b.flag_cell), "四情况 B:雷格已揭示(挖雷本体照常)") ms_b.settle_batch([{"action": "stop"}]) var target_b: Array = [t_b.flag_cell.x, t_b.flag_cell.y] - var miss_b := 0 - var reward_b := 0 + var mine_hit_b := 0 + var hit_b := 0 for row in rows_b: - if row.get("type", "") == "flag_miss" or String(row.get("source_class", "")) == "flag_miss": - miss_b += 1 - if String(row.get("source_class", "")) == "safe_cell" and row.get("origin_cell", [0, 0]) == target_b: - reward_b += 1 - check(miss_b == 0, "旗语义 B:同轮插撤零判定零惩罚(×%d——被拔不判)" % miss_b) - check(reward_b >= 1, "旗语义 B:同轮插撤后挖开奖励不受影响(×%d)" % reward_b) + if row.get("origin_cell", [0, 0]) != target_b: + continue + if String(row.get("source_class", "")) == "mine_hit": + mine_hit_b += 1 + if row.get("type", "") == "flag_hit" or String(row.get("source_class", "")) == "flag_hit": + hit_b += 1 + check(mine_hit_b >= 1, "四情况 B:挖雷惩罚照常兑付(×%d——只受挖雷一层惩罚)" % mine_hit_b) + check(hit_b == 0, "四情况 B:标中不发(×%d——旗已擦除不判)" % hit_b) + # 场景 C:不挖收轮(①③判定照常) + var rng_c = load("res://core/rng_service.gd").new() + rng_c.set_master_seed(20260919) + var settle_c = load("res://core/battle_orchestrator.gd").new(_db, rng_c) + var rows_c: Array = [] + settle_c.step_row.connect(func(row): rows_c.append(row)) + check(settle_c.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "四情况 C:开局受理") + var ms_c = settle_c.minesweeper + var t_safe := _find_flag_target(ms_c, false) + var t_mine := _find_flag_target(ms_c, true) + check(not t_safe.is_empty() and not t_mine.is_empty(), "四情况 C:安全格+雷格旗目标齐备") + if not t_safe.is_empty() and not t_mine.is_empty(): + for t in [t_safe, t_mine]: + if t.hero_cell != ms_c.hero_pos(): + ms_c.settle_batch([{"action": "move", "cell": t.hero_cell}]) + ms_c.settle_batch([{"action": "flag_place", "cell": t.flag_cell}]) + ms_c.settle_batch([{"action": "stop"}]) # 不挖收轮——两旗均在场基线 + var safe_c: Array = [t_safe.flag_cell.x, t_safe.flag_cell.y] + var mine_c: Array = [t_mine.flag_cell.x, t_mine.flag_cell.y] + var miss_c := 0 + var miss_neg_c := 0 + var hit_c := 0 + for row in rows_c: + if row.get("origin_cell", [0, 0]) == safe_c and row.get("type", "") == "flag_miss": + miss_c += 1 + if row.get("origin_cell", [0, 0]) == safe_c and String(row.get("source_class", "")) == "flag_miss" and row.get("type", "") != "flag_miss": + miss_neg_c += 1 + if row.get("origin_cell", [0, 0]) == mine_c and (row.get("type", "") == "flag_hit" or String(row.get("source_class", "")) == "flag_hit"): + hit_c += 1 + check(miss_c >= 1 and miss_neg_c >= 1, "四情况 C:标空惩罚照常(归因×%d 负向兑付×%d)" % [miss_c, miss_neg_c]) + check(hit_c >= 1, "四情况 C:标中奖励照常(×%d)" % hit_c) # ---- 试玩日志器冒烟(结算快照 JSONL + 诊断转储)----