From 58dfb56272f5a2d0ea7e14edb4b3397a5f76bcbf Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 6 Oct 2026 09:14:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(battle):=20=E5=9C=B0=E5=9B=BE=E4=BC=A4?= =?UTF-8?q?=E5=AE=B3=E8=B4=A6=E6=AE=BF=E5=90=8E=E6=89=B9=EF=BC=88=E6=A1=88?= =?UTF-8?q?C=202026-10-06=20=E7=94=A8=E6=88=B7=E8=A3=81=E2=80=94=E2=80=94?= =?UTF-8?q?=E3=80=8C=E4=B8=BB=E8=A7=92=E5=85=88=E6=89=8B=E6=94=BB=E5=87=BB?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=E3=80=8D=E6=AD=A7=E4=B9=89=E8=B4=A8=E8=AF=A2?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=EF=BC=89=E2=80=94=E2=80=94=E2=91=A0=E7=BB=93?= =?UTF-8?q?=E7=AE=97=E9=A1=BA=E5=BA=8F=E5=AE=9A=E6=AD=BB=3D=E2=91=A0?= =?UTF-8?q?=E6=8E=88=E4=BA=88=E6=AE=B5=E5=85=91=E9=9D=9E=E4=BC=A4=E5=AE=B3?= =?UTF-8?q?=E8=B4=A6=EF=BC=88=E8=B5=84=E6=BA=90/=E5=A2=9E=E7=9B=8A/?= =?UTF-8?q?=E6=B2=BB=E7=96=97/=E8=A1=8C=E5=8A=A8=E7=82=B9/GP/=E8=AD=A6?= =?UTF-8?q?=E8=A7=89=EF=BC=8C=E4=BC=A4=E5=AE=B3=E5=BD=A2=E6=80=81=E5=8D=95?= =?UTF-8?q?=E6=8D=AE=E6=9A=82=E5=AD=98=E4=B8=8D=E5=85=91=EF=BC=89=E2=86=92?= =?UTF-8?q?=E2=91=A2=E6=88=98=E6=96=97=E6=AE=B5=E3=80=8C=E4=B8=BB=E8=A7=92?= =?UTF-8?q?=E5=85=88=E5=9F=BA=E7=A1=80=E6=94=BB=E5=87=BB=E2=86=92=E6=95=8C?= =?UTF-8?q?=E4=BA=BA=E6=8C=89=E7=94=9F=E6=88=90=E5=BA=8F=E9=80=90=E4=B8=AA?= =?UTF-8?q?=E8=BF=98=E6=89=8B=E2=86=92=E5=9C=B0=E5=9B=BE=E4=BC=A4=E5=AE=B3?= =?UTF-8?q?=E8=B4=A6=E6=AE=BF=E5=90=8E=E3=80=8D=E2=86=92=E2=91=A3=E8=A1=B0?= =?UTF-8?q?=E5=87=8F=E2=86=92=E2=91=A4=E5=88=A4=E5=AE=9A=EF=BC=9B=E2=91=A1?= =?UTF-8?q?=E5=88=87=E7=89=87=3Dbattle=5Forchestrator=20=E6=9A=82=E5=AD=98?= =?UTF-8?q?=E9=97=B8=20=5Fdefer=5Fboard=5Fdamage/=5Fdeferred=5Fboard+=5Fsu?= =?UTF-8?q?bmit=20=E6=A1=88C=E5=88=86=E6=B5=81=EF=BC=88sync=E5=90=8C?= =?UTF-8?q?=E9=97=B8=E2=80=94=E2=80=94=E6=B8=85=E5=9B=BE=E6=89=B9=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E5=BB=B6=E5=90=8E=EF=BC=89+=5Foutcome=5Fof=EF=BC=88?= =?UTF-8?q?=E6=9A=82=E5=AD=98=E5=8D=95=E6=8C=89=E3=80=8C=E5=B0=86=E5=85=91?= =?UTF-8?q?=E4=BB=98=E3=80=8D=E5=9B=9E=E6=8A=A5=E2=80=94=E2=80=94=E5=AE=8C?= =?UTF-8?q?=E7=BE=8E=E7=AC=AC=E4=BA=8C=E4=BB=BD=E4=BE=9D=E6=8D=AE=E5=BC=80?= =?UTF-8?q?=E5=8D=95=E6=97=B6=E7=82=B9=E4=BA=8B=E5=AE=9E=EF=BC=89+?= =?UTF-8?q?=E6=8E=88=E4=BA=88=E6=AE=B5=E5=9B=9B=E5=A4=84=E7=9B=B4=E6=8F=90?= =?UTF-8?q?=E6=94=B9=E8=B5=B0=E9=97=B8+=E6=88=98=E6=96=97=E6=AE=B5?= =?UTF-8?q?=E6=9C=AB=E8=A1=A5=E6=8F=90=E4=BA=A4=E5=9D=97=EF=BC=88=E4=B8=BB?= =?UTF-8?q?=E8=A7=92=E6=AD=BB=E4=BA=8E=E6=80=AA=E7=89=A9=E2=86=92=E6=9A=82?= =?UTF-8?q?=E5=AD=98=E5=8D=95=E6=95=B4=E4=BD=93=E4=B8=8D=E5=85=91=E9=80=90?= =?UTF-8?q?=E5=8D=95=E7=95=99=E3=80=8C=E6=9C=AA=E7=94=9F=E6=95=88=EF=BC=88?= =?UTF-8?q?=E4=B8=BB=E8=A7=92=E5=B7=B2=E5=80=92=E4=B8=8B=EF=BC=89=E3=80=8D?= =?UTF-8?q?=E8=A1=8C=20forfeit=3Dhero=5Fdown=EF=BC=89=EF=BC=9B=E2=91=A2?= =?UTF-8?q?=E6=96=87=E6=A1=A3=3D=E6=88=98=E6=96=97=E7=BB=93=E7=AE=97=20v0.?= =?UTF-8?q?26=EF=BC=88=E4=BA=94=E6=AE=B5=E8=A1=A8=E2=91=A0=E2=91=A2?= =?UTF-8?q?=E8=A1=8C/=E6=8E=88=E4=BA=88=E6=AE=B5=E8=8A=82/=E5=85=91?= =?UTF-8?q?=E4=BB=98=E5=BD=A2=E6=80=81=E8=A1=A8/=E7=BB=93=E8=B4=A6?= =?UTF-8?q?=E6=97=B6=E6=9C=BA=E5=8F=8C=E6=A8=A1=E5=BC=8F/=E6=88=98?= =?UTF-8?q?=E6=96=97=E6=AE=B5=E8=8A=82/P0=E9=97=AD=E7=8E=AF/=E4=B8=89?= =?UTF-8?q?=E5=88=86=E9=87=8F=E9=81=97=E7=95=99=E9=A1=BA=E6=89=8B=E6=B8=85?= =?UTF-8?q?+=E4=B8=89=E5=A4=84=E6=99=AE=E9=80=9A=E5=88=86=E9=87=8F?= =?UTF-8?q?=E6=94=B9=E5=9F=BA=E7=A1=80=E5=88=86=E9=87=8F=EF=BC=89+?= =?UTF-8?q?=E6=95=8C=E4=BA=BA=E7=B3=BB=E7=BB=9F=EF=BC=88P0=E7=AC=AC4?= =?UTF-8?q?=E6=AD=A5=E6=88=98=E6=96=97=E6=AE=B5=E9=99=90=E5=AE=9A+?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E8=B4=A6+=E6=AD=BB=E4=BA=A1=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E6=94=B9=E8=BF=B0=EF=BC=89+=E5=9C=B0=E5=9B=BE?= =?UTF-8?q?=E6=88=98=E6=96=97=E8=B5=84=E6=BA=90=EF=BC=88=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=9B=BE/=E8=B4=9F=E5=90=91=E6=81=92=E5=8D=B3=E6=97=B6?= =?UTF-8?q?=E8=8A=82/=E4=BA=94=E6=AD=A5=E6=B8=85=E5=8D=95=EF=BC=89+01-?= =?UTF-8?q?=E6=A0=B8=E5=BF=83=E5=BE=AA=E7=8E=AF=EF=BC=88=E3=80=8C=E6=80=AA?= =?UTF-8?q?=E7=89=A9=E7=9A=84=E5=85=88=E6=89=8B=E3=80=8D=E7=94=A8=E8=AF=8D?= =?UTF-8?q?=E7=9B=B8=E6=92=9E=E6=94=B9=E8=AD=A6=E8=A7=89=EF=BC=89+README?= =?UTF-8?q?=E6=A1=88C=E6=89=B9=E8=8A=82+registry=E4=B8=A4=E5=A4=84?= =?UTF-8?q?=E6=A1=88C=E6=B3=A8=E8=AE=B0+=E7=BC=96=E6=8E=92=E5=99=A8?= =?UTF-8?q?=E5=A4=B4=E9=83=A8=E6=B3=A8=E9=87=8A=EF=BC=9B=E2=91=A3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=3D=E8=A1=8C=E7=9B=B8=E4=BD=8D=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E5=99=A8=E6=8D=A2=E4=BB=A3=EF=BC=88damage=E6=81=92=E6=88=98?= =?UTF-8?q?=E6=96=97=E6=AE=B5/reward=5Fineffective=E6=8C=89forfeit?= =?UTF-8?q?=E5=88=86=E6=B5=81=EF=BC=89+=E5=85=88=E6=89=8B=E6=96=AD?= =?UTF-8?q?=E8=A8=80=E6=94=B6=E7=B4=A7=EF=BC=88=E5=AD=98=E5=9C=A8=E6=88=96?= =?UTF-8?q?=E6=8E=88=E4=BA=88=E6=AE=B5=E7=BB=88=E5=B1=80=E2=86=92=E5=BF=85?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=EF=BC=89+=E6=96=B0=E5=A2=9E=E6=A1=88C?= =?UTF-8?q?=E6=AE=B5=E5=86=85=E5=BA=8F=E6=96=AD=E8=A8=80=EF=BC=88=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E8=B4=A6=E4=B8=8D=E5=BE=97=E5=85=88=E4=BA=8E=E5=85=88?= =?UTF-8?q?=E6=89=8B=E6=88=96=E6=8F=92=E5=85=A5=E6=95=8C=E6=96=B9=E8=BF=98?= =?UTF-8?q?=E6=89=8B=E4=B8=AD=E9=97=B4=EF=BC=89=E2=80=94=E2=80=94=E5=86=92?= =?UTF-8?q?=E7=83=9F783/0=E4=B8=89=E9=97=B8=E8=BF=87+run=5Fall=2017?= =?UTF-8?q?=E5=A5=97=E5=85=A8=E7=BB=BF+probe=5Fgate=20--click=E5=85=A8?= =?UTF-8?q?=E7=BB=BF+gdd=5Flint=E4=B8=8E=E5=9F=BA=E7=BA=BF=E4=B8=80?= =?UTF-8?q?=E8=87=B4=EF=BC=9B=E2=91=A4=E6=97=A7=E8=A1=8C=E4=B8=BAMC?= =?UTF-8?q?=E9=94=9A=E5=85=A8=E6=95=B0=E4=BD=9C=E5=BA=9F=EF=BC=88=E6=8E=88?= =?UTF-8?q?=E4=BA=88=E6=AE=B5=E4=BC=A4=E5=AE=B3=E8=87=B4=E6=AD=BB=E7=9F=AD?= =?UTF-8?q?=E8=B7=AF=E5=85=88=E6=89=8B=E8=AF=AD=E4=B9=89=E6=B6=88=E5=A4=B1?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E9=87=8D=E9=94=9A=E5=BD=92=E6=95=B0=E5=80=BC?= =?UTF-8?q?=E6=89=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design/gdd/01-核心循环.md | 2 +- design/gdd/systems/战斗循环/地图战斗资源.md | 6 +- design/gdd/systems/战斗循环/战斗结算.md | 29 ++++----- design/gdd/systems/战斗循环/敌人系统.md | 4 +- design/registry/entities.yaml | 4 +- game/README.md | 8 +++ game/core/battle_orchestrator.gd | 67 +++++++++++++++------ game/smoke/smoke_all.gd | 38 ++++++++++-- 8 files changed, 111 insertions(+), 47 deletions(-) diff --git a/design/gdd/01-核心循环.md b/design/gdd/01-核心循环.md index ebfdc5cf..78f9b96e 100644 --- a/design/gdd/01-核心循环.md +++ b/design/gdd/01-核心循环.md @@ -8,7 +8,7 @@ 1. 开局一张新雷图,每轮发给你若干行动点。 2. 用行动点做排雷决策:挖开安全格,拿增益和道具;控制(插旗)巢穴,削弱里面的怪物;点错惊动巢穴,怪物反而变强。 -3. 行动点用尽——或者你主动停手——这一轮结束,轮末自动结算:你这一轮扫雷攒下的因,逐行兑现成战斗的果。你拿的增益打了出去,你控制的巢穴变成怪物的虚弱,你惊动的巢穴变成怪物的先手。 +3. 行动点用尽——或者你主动停手——这一轮结束,轮末自动结算:你这一轮扫雷攒下的因,逐行兑现成战斗的果。你拿的增益打了出去,你控制的巢穴变成怪物的虚弱,你惊动的巢穴变成怪物的警觉(护甲变硬、伤害账轮末才落)。 4. 敌人没死光、你还活着,就进下一轮,同一张图接着挖;图开完了,发清图奖励,换一张新图接着打。 所以自动战斗不是过场,是阅卷:每一行结算都能指回你刚才的哪一步棋。 diff --git a/design/gdd/systems/战斗循环/地图战斗资源.md b/design/gdd/systems/战斗循环/地图战斗资源.md index 09dfe8b6..38c218b2 100644 --- a/design/gdd/systems/战斗循环/地图战斗资源.md +++ b/design/gdd/systems/战斗循环/地图战斗资源.md @@ -40,7 +40,7 @@ ## 在核心循环中的位置 ``` -装板 ──(一次摇号定全图资源身份 + 清图对象)──▶ 挖掘期(身份恒定可读,只揭晓不兑付)──▶ 轮末授予段(极性路由逐格兑付)──▶ 战斗段 +装板 ──(一次摇号定全图资源身份 + 清图对象)──▶ 挖掘期(身份恒定可读,只揭晓不兑付)──▶ 轮末授予段(极性路由逐格兑付;伤害形态单据暂存)──▶ 战斗段(段末地图伤害账统一落地——案C 2026-10-06 用户裁) ``` 本系统横跨挖掘期与轮末结算的头部:装板时全图资源身份一次摇定;挖掘期只揭晓身份,轮末授予段(结算里逐格兑付的段落)按极性矩阵把当轮的因兑现——正向当场生效,负向也当场生效(负向恒即时,无库存语义)。段序编排归[战斗结算](战斗结算.md)。 @@ -125,7 +125,7 @@ ### 负向恒即时:惩罚没有库存语义 -负向兑付全部在授予段当场执行,不入库、不出窗口——惩罚不设库存、不留存量。正向里的 percent 类(增益行)进[增益状态](增益状态.md)的容器;flat 类(群伤、治疗、直伤)当场结算;gp 正负两向(金币袋 / 破财)同样当场执行——记入战斗内 GP 缓冲,战斗完成随 gp_dropped 与钱包结算(账本归[冒险结构](../冒险循环/冒险结构.md)),同样无库存语义。叠层饱和由战斗结算既有池 clamp 承载;blast 负向(警觉)与 action 负向(迟滞)直授无上限。负向直伤使主角生命归零时,结算按战斗结算的致死短路规则处理。 +负向兑付的账户动作全部在授予段受理,不入库、不出窗口——惩罚不设库存、不留存量;其中**伤害形态(群伤/直伤类负向行)的扣血落地殿后到战斗段末**(2026-10-06 用户裁,单据暂存,见[战斗结算](战斗结算.md));主角在敌方还手中倒下则地图伤害账整体不兑(逐单留「未生效」行)。正向里的 percent 类(增益行)进[增益状态](增益状态.md)的容器;heal 正向当场结算;gp 正负两向(金币袋 / 破财)当场执行——记入战斗内 GP 缓冲,战斗完成随 gp_dropped 与钱包结算(账本归[冒险结构](../冒险循环/冒险结构.md)),同样无库存语义。叠层饱和由战斗结算既有池 clamp 承载;blast 负向(警觉)与 action 负向(迟滞)直授无上限。 举证:本节为兑付语义,无计算。 @@ -169,7 +169,7 @@ 1. 战斗结算装上新图后、玩家开挖前调用一次摇号:全部非封锁格的资源类型与清图对象身份摇定,映射进入只读。 2. 玩家挖掘:挖开的格子当场揭晓类型身份;已开格的身份随时可读。 -3. 轮末授予段按极性矩阵逐格兑付:正向 percent 挂状态、正向 flat 当场结算,负向全部当场兑付。 +3. 轮末授予段按极性矩阵逐格兑付:正向 percent 挂状态、heal 正向当场结算,非伤害负向当场兑付;伤害形态正负单据暂存,战斗段末统一落地。 4. 清图轮兑付清图奖励对象,恒非空;完美排雷追加第二份。 5. 战斗结束:板级映射退役。换图按新板重摇。 diff --git a/design/gdd/systems/战斗循环/战斗结算.md b/design/gdd/systems/战斗循环/战斗结算.md index 60eeb63d..258fdab8 100644 --- a/design/gdd/systems/战斗循环/战斗结算.md +++ b/design/gdd/systems/战斗循环/战斗结算.md @@ -51,7 +51,7 @@ ### 轮末五段结算:一轮从收场走到判定 -玩家行动点花完、主动停手,或开完整张图时,[行动经济](../排雷循环/行动经济.md)宣布本轮结束,本系统随即自动发起一次整轮结算。结算严格按五段固定顺序执行;核心相对次序"增益减益先于战斗"不变——先合成修正再动刀,任何结局都能归因。 +玩家行动点花完、主动停手,或开完整张图时,[行动经济](../排雷循环/行动经济.md)宣布本轮结束,本系统随即自动发起一次整轮结算。结算严格按五段固定顺序执行;核心相对次序"增益减益先于战斗"不变——先合成修正再动刀,任何结局都能归因。地图奖励与惩罚中的**伤害形态兑付一律殿后到战斗段末**(2026-10-06 用户裁):授予段只清资源、增益、治疗、行动点、GP 等非伤害账,刀落完后地图伤害账才统一落地。 ``` ① 授予段 → ② 增益/减益段 → ③ 战斗段 → ④ 衰减段 → ⑤ 判定段 @@ -59,9 +59,9 @@ | 段 | 干什么 | |---|---| -| ① 授予段 | 结算队列先过滤再分批兑换:开格、旗判定、清图,按极性矩阵路由(详见授予段一节) | +| ① 授予段 | 结算队列先过滤再分批兑换:开格、旗判定、清图,按极性矩阵路由(资源/增益/治疗/行动点/GP 当场兑付;伤害形态单据暂存不兑,详见授予段一节) | | ② 增益/减益段 | 把主角与敌人身上的活跃效果一次性合成本结算的修正池(同侧百分比修正的加总桶) | -| ③ 战斗段 | 主角先手攻击一次,随后敌人按行序逐个还手(详见战斗段一节) | +| ③ 战斗段 | 主角先基础攻击一次,随后敌人按行序逐个结算攻击,最后地图奖励与惩罚的伤害兑付在段末统一落地(详见战斗段一节) | | ④ 衰减段 | 移除到期的临时效果;每轮恰好一次,含末轮——清图奖励先兑付、效果后衰减 | | ⑤ 判定段 | 先判主角倒下(负),再判敌人清空(胜),都不满足则继续下一轮 | @@ -139,16 +139,16 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × | 旗判定批 | 标中则兑该雷格资源的正向,每颗雷的资格只兑一次;标空则记"标旗未中"归因行,并兑该安全格资源的负向 | | 清图批 | 清图奖励对象恰好发一次;完美排雷追加第二份同身份兑付 | -授予段内的伤害兑付(爆裂、直伤、反噬)以伤害单据入队,攒到授予段尾统一结账;清图批为段内同步结账点——开单前段内排队单先按开单序兑现(见[伤害单据与伤害控制器](#伤害单据与伤害控制器所有伤害走同一道门)一节)。 +授予段当场只兑非伤害形态(增益、治疗、行动点、GP 缓冲、警觉加层);**伤害形态兑付(爆裂、直伤、反噬)以伤害单据暂存,攒到战斗段末统一结账**(2026-10-06 用户裁——地图伤害账殿后,见[伤害单据与伤害控制器](#伤害单据与伤害控制器所有伤害走同一道门)与战斗段两节)。暂存单在授予段按「将兑付」回报——授予段内依赖兑付读数的流程决策(完美排雷第二份、高光份数)依据开单时点事实判定,该时点敌人存活有保证;战斗段末实际结账若目标已灭,控制器自记「奖励未生效」行。 **充能已退役(2026-09-19 用户拍板)**:原"挖格/标中旗给充能计数 → 战斗段折算穿刺/魔法分量"的弹药系统整体移除——主角攻击回归单分量,克制策略归冒险前配置(主角 attack_type 表值,营地换型 = 改字段;未来冒险前参数面承接策略位)。攻防两条抽象分离纪律保留:攻击类型是伤害的属性轴,棋盘事实只是奖励触发源。 | 兑付形态 | 覆盖 | 兑付方式 | 拒绝语义 | |---|---|---|---| | 增益(percent) | attack / defense / weaken 的正负两向 | 进入[增益状态](增益状态.md)的容器,本结算生效 | 叠层已满时拒收,记"奖励未生效"行 | -| 即时全体伤害(flat) | blast 正向 | 当场对全体存活敌各扣表值生命 | 场上无敌可打时零兑付,记"奖励未生效"行 | +| 即时全体伤害(flat) | blast 正向 | 战斗段末对全体存活敌各扣表值生命(暂存单殿后统一落地) | 场上无敌可打时零兑付,记"奖励未生效"行 | | 即时治疗(flat) | heal 正向 | 主角按表值回复并封顶上限 | 满血时零兑付,记"奖励未生效"行 | -| 直伤(flat) | heal 负向 | 主角当场扣表值生命,不经增益状态 | 生命归零即走致死短路 | +| 直伤(flat) | heal 负向 | 主角在战斗段末扣表值生命(暂存单殿后统一落地),不经增益状态 | 主角倒下则地图账整体不兑,逐单留"未生效"行 | | 警觉加层 | blast 负向 | 敌方全体护甲等级 +1/层(读 `reward.tres` enemy_alert 行 → `status_effects.tres` alert 行),入增益状态容器 | 直授无上限;战斗段由防守方护甲等级读数消费 | | GP 缓冲(flat) | gp 正 / 负向 | 金币袋 / 破财按 power 记入战斗内 GP 缓冲(+power / −power);缓冲随战斗完成折进 battle_finished 的 gp_dropped 统一到账(钱包账本归[冒险结构](../冒险循环/冒险结构.md)) | 无拒收态——缓冲只在战斗边界与钱包结算,战斗域零钱包状态 | | 行动点修正 | action 正/负向 | 下轮行动点 ±power(action_economy 排队,下一轮开始兑现后清零;下限 0) | 排队净值不跨轮保留;明细记效果行(effect_id=ap_delta) | @@ -181,13 +181,13 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × **死亡事件由单位自行宣布**:单位生命归零的那一刻自己发出死亡事件(不由流程代查、不由控制器推断)。怪死 → 队列中该敌为来源的未结单据作废(死亡即不动手——静默零行、不进冷却、不进保底计数,同既有口径);主角死 → 兑付终止、余单作废,Game Over 仍归判定段的生命读数路径。 -**结账时机双模式(结构位,非调参项)**:单据开出后何时兑现由控制器政策承载。现行 = **段界结账**:授予段的伤害单攒到授予段尾统一兑现;清图批是段内同步结账点——开单前先按序结清段内排队单、随后即开即结(完美第二份与高光份数的流程依赖需要同步读数);战斗段的单攒到战斗段尾统一兑现。保留位 = **开单即结**:提交当场兑现,逐位等价 2026-09-24 之前的行为,作回切结构位。两模式同种子同操作均逐位复现(兑现次序 = 开单次序);同一输入在两模式下结果可不同(兑现时机的语义差异),历史基线读数以段界结账口径为准。切换 = 改控制器政策常量,走代码修订不走调参。 +**结账时机双模式(结构位,非调参项)**:单据开出后何时兑现由控制器政策承载。现行 = **段界结账 + 地图账殿后**(2026-10-06 用户裁):授予段不开具任何当场兑现的伤害单——全部伤害形态单据暂存(含清图批),战斗段按「主角先手 → 逐敌还手 → 地图账」的顺序补提交,攒到战斗段尾统一兑现;主角在敌方还手中倒下 → 暂存单整体不兑,逐单留「未生效(主角已倒下)」行。清图批的完美第二份与高光份数决策依据开单时点事实(该时点敌人存活有保证),暂存单按「将兑付」回报。保留位 = **开单即结**:提交当场兑现,逐位等价 2026-09-24 之前的行为,作回切结构位。两模式同种子同操作均逐位复现(兑现次序 = 开单次序);同一输入在两模式下结果可不同(兑现时机的语义差异),历史基线读数以 2026-10-06 之前的授予段结账口径为准——该口径已随案C退役,新读数一律按地图账殿后重采。切换 = 改控制器政策常量,走代码修订不走调参。 举证:本节为管线编排约定,无新计算;掷点、折扣、克制与池乘区的算术口径见战斗段一节。 ### 战斗段与胜负判定:谁先动手、怎么扣血、怎么定胜负 -战斗段是兑现的最后一刀。主角的基础攻击恒存在——这是项目拍板:若基线为零,百分比增益乘上去永远是零,增益系统就空转了。攻击力域 [1, 999]、0 于加载期拒绝,行值读主角表。顺序固定:主角先手攻击顺位最前的存活敌人,随后按行序逐个存活敌人按本拍摇定技能还手;每个敌人依次走"按技能行兑现、结算伤害、执行上报(技能进冷却 + 保底计数)"三步且恰行动一次,主角不在其列。主角中途倒下,剩余敌人全部停手,直接进判定段。本段只开单——主角先手单与逐敌伤害技单攒到战斗段尾统一结账(见[伤害单据与伤害控制器](#伤害单据与伤害控制器所有伤害走同一道门)一节);施放技不涉伤害、照旧当场落行。 +战斗段是兑现的最后一刀。主角的基础攻击恒存在——这是项目拍板:若基线为零,百分比增益乘上去永远是零,增益系统就空转了。攻击力域 [1, 999]、0 于加载期拒绝,行值读主角表。顺序固定:主角先基础攻击顺位最前的存活敌人,随后按行序逐个存活敌人按本拍摇定技能还手;每个敌人依次走"按技能行兑现、结算伤害、执行上报(技能进冷却 + 保底计数)"三步且恰行动一次,主角不在其列;**最后兑地图伤害账**——授予段暂存的全部伤害单按授予段事件序补提交,与主角单、敌方单一起攒到战斗段尾统一结账(见[伤害单据与伤害控制器](#伤害单据与伤害控制器所有伤害走同一道门)一节)。主角中途倒下,剩余敌人全部停手,地图伤害账整体不兑(逐单留「未生效(主角已倒下)」行),直接进判定段。施放技不涉伤害、照旧当场落行。案C 后授予段不产出伤害,主角进入战斗段时恒存活、场上恒有敌——先手必兑。 **开轮摇号与敌方技能兑现**(2026-09-16 敌人技能池批):敌人每拍做什么由开轮摇号决定——每轮轮次开始时,本系统在换算层排除冷却中技能,按行序逐敌从 `behavior_roll` 封闭流的独立子流摇定本拍动作写入实例池(首轮同样摇号;每轮恰摇一次;轮内刷新换图不重摇),随后冷却递减。摇定后、兑现前死亡 → 本拍技能作废、不动手(死亡即不动手)。结算战斗段只按技能行兑现:伤害技走三乘法链(见下),增益技记一行施放行并把状态效果按技能行持续回合挂入[增益状态](增益状态.md)容器——0 伤害动作不掷不折。池模板、技能行、冷却与保底语义归[敌人系统](敌人系统.md)(引用不转述)。 @@ -201,7 +201,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × 增益池乘区(含敌方防御池)→ 向下取整 → 伤害下限 max(1,·) → 必中附加分量 rider(2026-09-19 新效应词汇批——伤害技 rider_mult 字段:max(伤害下限, floor(攻击基线 × rider_mult)),独立必中加项、不进乘区、不出暴击;无字段 = 无此步) ``` -主角先手攻击由三个分量逐项取整后相加:基础分量(攻击力经增益修正,可被池封顶压到零;**类型 = 主角 `attack_type` 表值**——2026-09-18 营地扩展前置批:基础攻击量与攻击类型抽象分离,基础分量不再恒属普通,缺省 normal 向后兼容)、穿刺分量、魔法分量。**攻击类型是伤害的属性轴**——它只决定这个分量查甲效表的哪一行,定义里不含任何棋盘来源。分量各自查表后取整相加;甲效表把攻击类型与敌方护甲类型交叉成一张 3×3 **克制修正矩阵(加法点数)**——2026-09-18 加法核换装:每个分量在掷点/暴击折算取整后 **+ 克制修正点数**(原百分比乘区退役): +主角先手攻击 = 单一基础分量(2026-09-19 充能退役批定稿;量 = 攻击力经增益修正,可被池封顶压到零;**类型 = 主角 `attack_type` 表值**——2026-09-18 营地扩展前置批:基础攻击量与攻击类型抽象分离,基础分量不再恒属普通,缺省 normal 向后兼容)。**攻击类型是伤害的属性轴**——它只决定这个分量查甲效表的哪一行,定义里不含任何棋盘来源。分量查表取整;甲效表把攻击类型与敌方护甲类型交叉成一张 3×3 **克制修正矩阵(加法点数)**——2026-09-18 加法核换装:每个分量在掷点/暴击折算取整后 **+ 克制修正点数**(原百分比乘区退役): | 攻击类型 \ 护甲类型 | 轻甲 | 魔法护甲 | 重甲 | 语义 | |---|---|---|---|---| @@ -213,7 +213,7 @@ percent 类(每结算、每层): E = |层数 × 行值| ÷ 100 × 主角攻击 = 单一基础分量(2026-09-19 充能退役批):量 = 攻击力基线(表值),类型 = 主角表 `attack_type`(营地永久强化换型 = 改字段;未来冒险前参数面承接"按关配型"的策略位)。原"普通 + 穿刺/魔法充能三分量"模型整体退役——挖掘期与战斗期之间不再有弹药供给线,克制应对从局内攒桶改为冒险前配型 + 棋盘火力(穿刺弹/魔法矢等直伤奖励道具,消费面不变)。 -**护甲等级掷点:必中、非归零,只吃基础攻击分量**(2026-09-16 定稿落地,判定式与全部数值已拍板;判定对象措辞随敌人技能池批改述)。防守方由此多了一条数值轴——护甲等级(数据键 `armor_class`):攻击永远命中,没有 miss、没有零收益白回合;只有吃掷点的分量在掷点低于防守方护甲等级时按减伤档位打折,档位再重也不归零。判定式:攻击结算时掷单骰(骰面读表),掷点 ≥ 防守方护甲等级 + 攻击加成 → 全额;掷点 < 护甲等级按缺口(护甲等级 − 掷点)查减伤档位表打折——边界与档值读 `settlement.tres` 减伤档位表,三档皆非零档。暴击:天然最大面(读表)使该次基础攻击分量 ×暴击倍率(读表),双方同规,且天然最大面恒全额(暴击与折扣互斥;天然最大面〔nat20〕暴击是掷点层概念,与技能重击〔池技能〕严格区分——两层不得混述)。伤害下限:**任何来源的伤害落地**至少 damage_floor(读表,全局 max(damage_floor, ·)——2026-09-20 用户拍板扩全来源:攻击双方、棋盘直伤与爆裂、地形变异、负向反噬;伤害取值域 [1, +∞),**不设上限**)——"被减伤归零 v=0"零值伤害行语义随批退役,重档折扣的攻击行在行文本记"被压制"。掷点范围:只有基础攻击分量吃掷点——主角先手攻击的普通分量,敌方技能的基础伤害分量(= 攻击基线 × 技能伤害倍率,普通攻击倍率 1 为特例);无伤害分量的技能(增益/妨害类)不掷不折;棋盘来源一律无视护甲等级——爆裂、清障炸药、奖励即时伤害、技能必中附加分量(rider,2026-09-19)全部必中全额不掷,这是本机制的边界切口,与"奖励/惩罚必中"同构。护甲等级(数值轴,掷点打折)与护甲类型(属性轴,克制修正)严格分离:护甲类型不做护甲等级映射,克制修正点数读表、消费面不变,两概念在文档、表字段与代码键中不得混用。数值载体:主角与敌方护甲等级行值读 hero / enemy 表的 armor_class 列(正式行值归怪物设定批,登记[待办清单](../../06-待办清单.md)),ac_up(护甲强化)在其上加算(幅度/层数读 `status_effects.tres`)。攻击加成(`attack_bonus`)为结构位预留字段,切片恒 0,未来归元进度 / 武器附魔投值。掷点时机在结算战斗段内、每次基础攻击伤害计算时判定,消费 `armor_roll` 封闭流(逐次掷逐次取号,可逐位回放)。 +**护甲等级掷点:必中、非归零,只吃基础攻击分量**(2026-09-16 定稿落地,判定式与全部数值已拍板;判定对象措辞随敌人技能池批改述)。防守方由此多了一条数值轴——护甲等级(数据键 `armor_class`):攻击永远命中,没有 miss、没有零收益白回合;只有吃掷点的分量在掷点低于防守方护甲等级时按减伤档位打折,档位再重也不归零。判定式:攻击结算时掷单骰(骰面读表),掷点 ≥ 防守方护甲等级 + 攻击加成 → 全额;掷点 < 护甲等级按缺口(护甲等级 − 掷点)查减伤档位表打折——边界与档值读 `settlement.tres` 减伤档位表,三档皆非零档。暴击:天然最大面(读表)使该次基础攻击分量 ×暴击倍率(读表),双方同规,且天然最大面恒全额(暴击与折扣互斥;天然最大面〔nat20〕暴击是掷点层概念,与技能重击〔池技能〕严格区分——两层不得混述)。伤害下限:**任何来源的伤害落地**至少 damage_floor(读表,全局 max(damage_floor, ·)——2026-09-20 用户拍板扩全来源:攻击双方、棋盘直伤与爆裂、地形变异、负向反噬;伤害取值域 [1, +∞),**不设上限**)——"被减伤归零 v=0"零值伤害行语义随批退役,重档折扣的攻击行在行文本记"被压制"。掷点范围:只有基础攻击分量吃掷点——主角先手攻击的基础分量,敌方技能的基础伤害分量(= 攻击基线 × 技能伤害倍率,普通攻击倍率 1 为特例);无伤害分量的技能(增益/妨害类)不掷不折;棋盘来源一律无视护甲等级——爆裂、清障炸药、奖励即时伤害、技能必中附加分量(rider,2026-09-19)全部必中全额不掷,这是本机制的边界切口,与"奖励/惩罚必中"同构。护甲等级(数值轴,掷点打折)与护甲类型(属性轴,克制修正)严格分离:护甲类型不做护甲等级映射,克制修正点数读表、消费面不变,两概念在文档、表字段与代码键中不得混用。数值载体:主角与敌方护甲等级行值读 hero / enemy 表的 armor_class 列(正式行值归怪物设定批,登记[待办清单](../../06-待办清单.md)),ac_up(护甲强化)在其上加算(幅度/层数读 `status_effects.tres`)。攻击加成(`attack_bonus`)为结构位预留字段,切片恒 0,未来归元进度 / 武器附魔投值。掷点时机在结算战斗段内、每次基础攻击伤害计算时判定,消费 `armor_roll` 封闭流(逐次掷逐次取号,可逐位回放)。 EV 参考数不入正文(派生读数,需要时按判定式与现行表值自行复算);旧 defense_up 的减伤墙已整体退役换装为 AC(无双轨,见版本历史)。微调旋钮 = 档位边界与档位值,不动护甲等级值。换装点锚定:基准 AC(读 hero 表)自带的方向性偏移与[难度曲线与Boss](../冒险循环/难度曲线与Boss.md)已登记的加难杠杆扫描合并处理;其生存链区间模型按掷点带宽口径复核(带宽含暴击档与折扣档),"零红档"验收保留。 @@ -227,7 +227,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 护甲等级掷点:掷单骰(骰面读表),掷点 ≥ 护甲等级 + 攻击加成 → 全额;低掷按缺口查减伤档位表(边界与档值读表,无零档) 暴击:天然最大面(读表)→ 该次基础攻击分量 ×暴击倍率(读表)且恒全额(双方同规;掷点层概念,与技能重击严格区分) 伤害下限:任何来源的伤害落地 ≥ damage_floor(读表,全局 max(damage_floor, ·)——折扣与池归零都不产生 v=0 伤害行;2026-09-20 扩全来源,取值域 [1, +∞)) -吃掷点分量 = 主角普通分量 / 敌方技能基础伤害分量;穿刺、魔法、增益/妨害施放、必中附加分量与棋盘来源伤害不掷不折 +吃掷点分量 = 主角基础分量 / 敌方技能基础伤害分量;穿刺、魔法、增益/妨害施放、必中附加分量与棋盘来源伤害不掷不折 治疗量 = min( 当前生命 + 表值, 生命上限 ) ``` @@ -235,7 +235,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 |---|---| | 修正池封顶 | 同侧百分比修正加算后每池独立收口 ±100 百分点;乘数可为 0——输出压制触池底时落地伤害由全局下限保底 damage_floor(读表)(叠满免伤归零的结构面已随护甲等级换装消失) | | 伤害下限 | 任何来源的伤害落地至少 damage_floor(读表,全局 max(damage_floor, ·)——2026-09-20 扩全来源:攻击双方/棋盘直伤/地形变异/负向反噬,取值域 [1, +∞) 不设上限);"被减伤归零 v=0"零值伤害行语义退役,重档折扣行文本记"被压制"——免伤与零输出仍逐行可见 | -| 护甲等级掷点 | 必中非归零:掷单骰(骰面读表)对防守方护甲等级 + 攻击加成(恒 0),低掷按缺口查减伤档位表(读表)三档折扣、档位不设零档;天然最大面暴击 ×暴击倍率(读表)恒全额;只吃基础攻击分量(主角普通分量、敌方技能基础伤害分量),棋盘来源伤害与增益施放不掷不折 | +| 护甲等级掷点 | 必中非归零:掷单骰(骰面读表)对防守方护甲等级 + 攻击加成(恒 0),低掷按缺口查减伤档位表(读表)三档折扣、档位不设零档;天然最大面暴击 ×暴击倍率(读表)恒全额;只吃基础攻击分量(主角基础分量、敌方技能基础伤害分量),棋盘来源伤害与增益施放不掷不折 | | 溢杀 | 一刀超出目标剩余生命的部分丢弃:不转移给下一个敌人、不存续、无负生命中间态 | | 治疗 | 生命不溢出上限;满血正向治疗零兑付,记"奖励未生效"行 | | 死亡判定 | 生命归 0 即倒下;敌人受击即死;主角倒下战斗段立即短路;判定段先判主角、再判敌人(防御性次序) | @@ -301,7 +301,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 2. 挖掘期本系统只缓冲事实流(开格、标旗入结算队列),不做任何兑付。 3. 轮末自动触发五段结算,触发与事件到达序无关。 4. 授予段按判定基线过滤(被挖开的旗不入判定),再按极性矩阵三批兑现:开安全格兑正向(插旗安全格直挖 = 四情况④零奖励作废行)、开雷格 / 标错旗兑负向(插旗雷格直挖照常——四情况②)、清图发清图奖励对象(完美乘数读 settlement.tres p_perfect)。 -5. 增益合成、主角先手加逐敌按摇定技能兑付(伤害技三乘法链 / 增益技施放行)、衰减恰好一次。 +5. 增益合成、主角先手加逐敌按摇定技能兑付(伤害技三乘法链 / 增益技施放行)加地图伤害账殿后(暂存单随战斗段末统一结账)、衰减恰好一次。 6. 判定段先主角后敌人:继续通知同图、清图换图续轮、胜负走战斗结束清理(秘宝背包不清)。 7. 明细流水全程落行,供呈现、回放与导出。 @@ -344,7 +344,7 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 - 完美排雷:零误标且至少一面标中时第二份兑付恰一次并发高光行;出现误标或零标中则无高光;计数换图重置。 - 开轮摇号:每轮恰摇一次(首轮同样;轮内刷新换图不重摇);摇定后兑现前死亡 → 本拍技能作废(零敌方行动行)、不进冷却不进保底计数;已兑现技能进冷却、保底计数按执行结果更新。 - 战斗段次序:主角先手后逐敌"按摇定技能行兑现、结算伤害、执行上报"各恰一次;主角倒下立即短路,剩余敌人不行动。 -- 护甲等级掷点:吃掷点分量(主角普通分量、敌方技能基础伤害分量)逐次掷单骰(骰面读表)——掷点 ≥ 防守方护甲等级 + 攻击加成全额兑付;低掷按缺口查减伤档位表(边界与档值读表),档位不设零档;天然最大面暴击该分量 ×暴击倍率(读表)且恒全额;穿刺/魔法分量与棋盘来源伤害(爆裂、清障炸药、奖励即时)、必中附加分量及增益/妨害施放不掷不折。 +- 护甲等级掷点:吃掷点分量(主角基础分量、敌方技能基础伤害分量)逐次掷单骰(骰面读表)——掷点 ≥ 防守方护甲等级 + 攻击加成全额兑付;低掷按缺口查减伤档位表(边界与档值读表),档位不设零档;天然最大面暴击该分量 ×暴击倍率(读表)且恒全额;穿刺/魔法分量与棋盘来源伤害(爆裂、清障炸药、奖励即时)、必中附加分量及增益/妨害施放不掷不折。 - 新效应词汇(2026-09-19):伤害技 rider_mult 行的每次攻击,伤害行携带 rider 值且终值 = max(伤害下限, 主分量链结果) + rider(停手轮零池下逐拍精确复算);rider 键只随带 rider_mult 的技能行出现;妨害技 = 施放行 + 状态按 target_side 落主角(投掷阈值随行可读下调),冷却/保底/死亡作废三闸无一例外。 - 伤害下限与留档:任何来源的伤害落地 ≥ damage_floor(读表,2026-09-20 扩全来源),全流水零 v=0 伤害行、零"被减伤归零"行;掷点读数、护甲等级与档位随伤害行留档,天然最大面(nat20)行带暴击标记。 - 数值边界:伤害下限保底后全程无零值/负值伤害;溢杀丢弃不转移;治疗封顶不溢出。 @@ -389,3 +389,4 @@ EV 参考数不入正文(派生读数,需要时按判定式与现行表值 | v0.23 | 2026-09-20 | **GP+商店房批(用户拍板——与 关卡节点地图 v1.1 / 冒险结构 v0.5 / 地图战斗资源 v0.15 / 难度曲线与Boss v0.18 / registry / 切片代码同批;版本号注记:v0.22 已被充能退役批占用,表尾顺延)**:① 呈现只读面表 battle_finished 载荷 +gp_dropped(掉落掷定 + 关级加值 + 战斗内缓冲的合计——字段增删走本文修订条款兑现);② 授予段兑付形态表新增 GP 缓冲行:gp 正/负向(金币袋 / 破财——九型批)兑付 = 战斗内 GP 缓冲 ±power,战斗完成统一上抛入钱包账本(账本归冒险结构),战斗域零钱包状态;③ 随机口径六处→七处:战斗结束掉落掷定消费 `gp_drop` 封闭流(出口随机、步骤流之外);九型字典措辞与验收标准可回放条目同步 | | v0.24 | 2026-09-24 | **伤害管线单据化批(用户三裁决——与切片代码同批,06 台账 148)**:新增「伤害单据与伤害控制器」一节——伤害对象抽象补全:五族伤害来源收口伤害控制器单点管线、兑付配方封闭五种;三裁决落文——①结账时机双模式(现行 = 段界结账:授予段尾 / 清图批同步点 / 战斗段尾;开单即结保留为回切结构位,切换走代码不走调参)②目标结账时刻照规则解析③死亡事件由单位自行宣布(怪死其未结单据作废、主角死兑付终止);授予段兑付与战斗段两节补结账时点句;验收标准 +伤害单据管线条目。切片同批:`core/damage_controller.gd` 新建(单据构造/开单/结账/死亡事件消费/管线算术收口)、battle_orchestrator 六调用点改开单、掷点/折扣/克制/池乘/攻击行文本助手迁出(`_defender_ac`/`_clamped_pool` 留薄委托)、unit_container 增 `unit_died` 信号;冒烟 557 项 0 失败双模式全绿 + A/B 对拍四配置全确定且真实分岔(行数恒等、纯次序位移);历史基线读数自本批起以段界结账口径为准(重测归曲线调参批) | | v0.25 | 2026-10-05 | **挖掘风险不等式判式换代(迭代14②号卡 T3/T4——与行动经济/难度曲线与Boss/registry 同批)**:①收益对照表补 gp/ap 当量口径注记(gp=flat power 代理 3/2 假设 A5 口径空白登记、ap=flat 表值 1 机会成本口径仅登记假设 A6,精确口径归商店批复核);②验收标准 +条款3 对账条(聚合健康式+β 带逐 u 检算 / 旗溢价兑付三形态覆盖·标空与挖格不乘·同池零触碰);③T4 机制载体 = settlement.tres 新键 flag_hit_premium=125(percent 域 provisional)——标中旗正向兑付 ×(1+δ),整数四舍五入取整与伤害管线 floor 分域(registry 随批登记);DamageController 零改、雷安同池冻结不动、直伤雷 6/8/8 与 p_perfect 不动 | +| v0.26 | 2026-10-06 | **地图伤害账殿后批(案C,用户裁——歧义质询触发)**:①五段总览①③行改写——授予段只兑非伤害形态,伤害形态兑付(爆裂/直伤/反噬/地形变异/战旗军规/熔火反噬等)暂存到战斗段末、按「主角先手→逐敌还手→地图账」统一落地;②战斗段节补地图账殿后与死亡守卫(主角死于怪物→暂存单整体不兑,逐单留「未生效(主角已倒下)」行);③结账时机双模式改述:现行=段界结账+地图账殿后,清图批同步读数改「将兑付」回报口径(完美第二份依据开单时点事实),历史基线读数以 2026-10-06 前授予段结账口径封存;④兑付形态表 blast 正向/heal 负向兑付时点改战斗段末;⑤P0 闭环第 5 步补地图账;⑥顺手清充能退役遗留:三分量句改单分量、三处「主角普通分量」改「基础分量」。切片同批:battle_orchestrator 暂存闸 `_defer_board_damage`/`_deferred_board` + `_submit` 案C 分流 + `_outcome_of` + 战斗段末补提交块 + 授予段四处直提改走闸;冒烟 783 项 0 失败(新增案C 段内序断言+行相位分类器换代)+ run_all 17 套全绿 + probe_gate --click 全绿;旧行为 MC 锚全数作废,重锚归数值批 | diff --git a/design/gdd/systems/战斗循环/敌人系统.md b/design/gdd/systems/战斗循环/敌人系统.md index 48632e52..eb5e25ec 100644 --- a/design/gdd/systems/战斗循环/敌人系统.md +++ b/design/gdd/systems/战斗循环/敌人系统.md @@ -141,7 +141,7 @@ 首轮同样摇号;每轮恰摇一次,轮内刷新换图不重摇。摇号随机走封闭随机流、每敌按生成序派生独立子流——单敌出招轨迹可独立复放、互不穿插;本系统自身语义零随机不变,单位容器收到的是确定结果(armor_roll 先例同构)。 -死亡即不动手(保留):摇定后、兑现前死亡(主角先手 / 授予段直伤),本拍技能作废——不动手、不进冷却、不进保底计数;其余存活敌人照常。拍结构保留:**每结算每存活敌恰行动一次、按行序、先作用后推进**——变的只是"轮到什么",由固定剧本改为摇号。同种敌人不再锁步:各自实例池独立摇号、独立冷却,怪物 1 的重击在冷却里不影响怪物 2,轨迹分歧是涌现,不设开战分配机制。 +死亡即不动手(保留):摇定后、兑现前死亡(主角先手 / 更早兑现的敌方伤害技),本拍技能作废——不动手、不进冷却、不进保底计数;其余存活敌人照常。拍结构保留:**每结算每存活敌恰行动一次、按行序、先作用后推进**——变的只是"轮到什么",由固定剧本改为摇号。同种敌人不再锁步:各自实例池独立摇号、独立冷却,怪物 1 的重击在冷却里不影响怪物 2,轨迹分歧是涌现,不设开战分配机制。 蓄力语义(2026-09-16 纠偏定稿):蓄力不是重击前奏——它是一行增益技:0 伤害动作,施加 empower 状态(幅度读 `status_effects.tres`),持续 1 个有效结算(provisional)。旧环"蓄力→猛击"的固定配对是哥布林弹珠的拷贝(给玩家输出窗口用的),随行为环一并退役;蓄力与重击之间**没有任何组合语义**——增益与技能倍率恰好同拍只是增益倍率 × 技能倍率的两层乘法共存(增益幅度 × 增益幅度 = 两层乘法读表可得),蓄力后摇中普通攻击则是增益倍率的加成平砍,两者同为平常事,不是设计的连招。技能冷却(几拍内不能再摇中,技能行属性)与增益持续(状态挂几回合,状态行属性)是两个计时器,互不相干。施放行 = 0 伤害动作,不掷不折(原"零兑付、不读伏击"的读怪奖励语义随伏击退役作废——2026-09-20 八型批)。 @@ -202,7 +202,7 @@ Boss 专属身份 = 专属池承载:独占技能 + 权重轮廓(**三代错 1. 战斗开始:战斗结算按遭遇坐标调本系统生成单位——读表快照实例化本战数量的敌人(池模板快照成实例池、冷却全零),主角以血量入参生成。 2. 开轮摇号:战斗结算换算层排除冷却中技能、按行序逐敌摇定本拍动作写入实例池,随后冷却递减(首轮同样摇;轮内刷新换图不重摇)。 3. 挖掘期踩雷:事实经战斗结算转化为警觉施加——每颗雷恰一次、逐层记账(无上限)。 -4. 每轮结算:主角先手攻击顺位最前的存活敌人;每个存活敌人按摇定技能行兑付(伤害技 = 基线 × 倍率,增益技 = 施放行 + 状态授予)。 +4. 每轮结算战斗段:主角先基础攻击顺位最前的存活敌人,随后每个存活敌人按摇定技能行兑付(伤害技 = 基线 × 倍率,增益技 = 施放行 + 状态授予);地图奖励与惩罚的伤害兑付殿后到战斗段末统一落地(案C 2026-10-06 用户裁,见战斗结算)。 5. 受击上报、血量递减;归 0 即移出顺位(摇定后兑现前死亡 → 本拍作废不进账)。 6. 执行上报:已兑现的技能进冷却、保底计数更新(普攻 +1 / 特殊技归零)。 7. 敌全灭或主角归零,回流战斗结算作胜负判定素材。 diff --git a/design/registry/entities.yaml b/design/registry/entities.yaml index 95014b70..f8e7ea46 100644 --- a/design/registry/entities.yaml +++ b/design/registry/entities.yaml @@ -339,7 +339,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)· 九型正向行齐备 · 九型负向行齐备(字典完整性——缺任一极性行致命——2026-09-20 八型批、同日 GP+商店房批随九型同步)· 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)· **极性不在资源内**——「动作 × 格型」矩阵(开安全格/标中雷 = 正向、开雷格/标错安全格 = 负向、插旗安全格直挖 = 零兑付〔四情况④〕;撤旗动作随插旗不可逆批 2026-09-19 退役)字典定义归 地图战斗资源、路由执行归 战斗结算 授予段(结算队列 + 轮末在场旗判定基线——被挖开的旗不入基线)· 授予期零随机(身份恒定跨轮;换图映射消亡)· 负向恒即时(不入库、无库存语义——库存已随可选类删除下线)· 满层零兑付 = 合法例外 + "奖励未生效"行 · 重复受理幂等(同格重复 grant / 同批次重复 grant_clear → 同一快照/身份 + 诊断;受理/兑付恰一次归 战斗结算×②③) + mechanics: 格面资源身份 install 期预分配(roll_board 恰一次、行主序、消费 reward_roll 封闭流恰一 scope 批〔格批 + 清图批尾一号〕;全部非封锁格逐一摇定类型——安全格与雷格同池同分布、封锁格不入摇号清单;Ready 后只读、无重掷 API)· **极性不在资源内**——「动作 × 格型」矩阵(开安全格/标中雷 = 正向、开雷格/标错安全格 = 负向、插旗安全格直挖 = 零兑付〔四情况④〕;撤旗动作随插旗不可逆批 2026-09-19 退役)字典定义归 地图战斗资源、路由执行归 战斗结算 授予段(结算队列 + 轮末在场旗判定基线——被挖开的旗不入基线)· 授予期零随机(身份恒定跨轮;换图映射消亡)· 负向恒即时(不入库、无库存语义——库存已随可选类删除下线)〔案C 2026-10-06 用户裁:伤害形态正负单据的扣血落地暂存至战斗段末统一执行——账户受理仍在授予段,兑付时点归 战斗结算〕· 满层零兑付 = 合法例外 + "奖励未生效"行 · 重复受理幂等(同格重复 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 伤害道具批增补四行;2026-09-20 格面资源八型批 ambush_double→enemy_alert 换装 + ap_up/ap_drain 两行新增;同日 GP+商店房批 +gp_gain/gp_loss 两行)——格面池正向九行(attack_up / ac_up〔2026-09-16 护甲等级掷点批换装,原 defense_up 减伤 −30% 退役;cell_type defense/权重/档位零改动〕/ spark_aoe / heal_minor / weaken / pierce_strike / magic_bolt〔attack_type 字段载攻击类型轴,查战斗结算克制矩阵、单目标必中全额〕/ ap_up action〔下轮行动点 +1——2026-09-20 八型批新增〕/ gp_gain gp 金币袋〔战斗内 GP 缓冲 +power,兑付编排归 战斗结算、钱包账本归 冒险结构——GP+商店房批新增〕)+ 负向九行纯字典(enrage / off_guard / heal_backlash 直伤〔不经增益状态〕/ enemy_alert blast〔effect_id=alert,敌方护甲等级 +1——2026-09-20 八型批 ambush_double 退役换装〕/ ap_drain action〔下轮行动点 −1〕/ sapped / pierce_snare / magic_recoil〔均主角直伤,不经增益状态〕/ gp_loss gp 破财〔战斗内 GP 缓冲 −power——GP+商店房批新增〕)+ 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〕;2026-09-20 八型批后 67→74——attack 14 / defense 11 / blast 16 / heal 2 / weaken 8 / pierce 8 / magic 8 / 清障炸药 1 / action 6;同日 GP+商店房批九型后 74→76——+gp_gain weight 2) direction_precheck: **刀供给方向预检随可选类删除失义退役(2026-09-15)**——历史读数见底部修订记录;新对账锚 = 行动经济 条款②③ 左臂(正向格面资源当量供给)随格面资源批重推,义务归 难度曲线与Boss 调参批 @@ -373,7 +373,7 @@ entities: ac_roll: 护甲等级掷点(**2026-09-16 护甲等级掷点批定稿,数字即规范**——骰面 roll_faces=20、nat 最大面 crit_roll=20、暴击倍率 crit_multiplier=200、攻击加成 attack_bonus=0〔结构位,元进度/武器附魔未来投值〕、伤害下限 damage_floor=1、流名 armor_roll_stream="armor_roll";判定式:掷 1d20,掷点 ≥ 防守方护甲等级 + 攻击加成 → 全额、低掷按缺口查档〔见 ac_discount_tiers〕,nat20 暴击该基础攻击分量 ×2 且恒全额〔双方同规〕;任何攻击落地伤害 ≥ damage_floor〔**2026-09-20 伤害下限全来源批(用户拍板)**:语义扩为任何来源的伤害落地——攻击双方/棋盘克制直伤/爆裂/地形变异/负向反噬全部收进闸内,伤害取值域 [1, +∞) 不设上限;全局 max(1,·)——零值伤害行语义退役,重档折扣行文本记"被压制"〕;吃掷点分量 = 主角先手攻击普通分量 / 敌方技能基础伤害分量〔= 攻击基线 × 技能伤害倍率——2026-09-16 敌人技能池批改述,原"敌方攻击拍与重击拍基线伤害"退役;无伤害分量的增益技不掷不折〕,棋盘来源伤害〔爆裂 / 清障炸药 / 奖励即时——2026-09-19 充能退役后无充能分量;伏击附加项随 2026-09-20 八型批伏击退役移除〕必中全额不掷;回合内随机、消费 armor_roll 封闭流逐次掷逐次取号,可逐位回放) ac_discount_tiers: Dictionary 缺口档边界→档位值 percent(**2026-09-16 定稿**——roll_tier_bounds=[3,7] · roll_tier_discounts=[75,50,25],档数 = 边界数 + 1;缺口 1-3 → ×0.75 · 4-7 → ×0.5 · 8+ → ×0.25,档位不设零档〔非归零〕,取整沿全局 floor;EV 参考数〔按判定式复算〕= AC10 期望减免 21.25% / AC11 25% / AC12 28.75%;微调旋钮 = 边界与档位值,不动 AC 值;难度曲线与Boss 生存链区间带宽补扫含暴击档与折扣档、"零红档"验收保留) validation: segment_order 违例致命 · effect_pool_map 悬空致命 · p_perfect 域 {100,200}(2026-09-10 载体域——100 = "完美必须 ≥ 不加倍"机制语义下界,200 = 双份交付)——战斗结算 冒烟三行致命 manifest(segorder_violation / poolmap_dangling / pperfect_oob) - mechanics: 结算段序骨架唯一权威载体(授予→增益/减益→战斗→衰减→判定——2026-09-15 格面资源批五段,item_window/item 两段随可选类删除下线)· **随机口径(2026-09-15 护甲等级批修订)**:结算换算层引入回合内随机(ac_roll 护甲等级掷点——必中非归零、低掷按 ac_discount_tiers 档位折扣),掷点流归属未定〔rng_service 流封闭清单 board_gen/reward_roll/encounter 不含结算内消费——新增流须改代码+P1 支柱评审、复用 encounter 则改"实例化期恰一 scope"绑定,登记 战斗结算「待办」,rng_service 条目随定稿批修订〕· 可回放契约 = **封闭随机流可回放**(同 master_seed 同意图序列 ⇒ 步骤流逐位复现——数据配置 AC-15 落点,原"结算路径零随机 / 步骤流逐行相同"口径随批作废;开战掷区间 / 装板摇号属初始化随机维持不变)· 调用序钉死 按摇定技能行兑现→apply_damage→commit_action(2026-09-16 技能池批改型——原 query_attack→apply_damage→advance 随行为环退役;行型 10 类同批改型:蓄力行 charge → 施放行 cast〔增益技 0 伤害施放、具名技能、不掷不折——原"不兑伏击"语义随 2026-09-20 八型批伏击退役改述〕)· 衰减恒执行含末轮 · 高光双落载体(perfect_sweep P_perfect——2026-09-10 修订为真实双份兑付载体 / 标中旗 highlight 标记)· 流水行协议字段分层(公共 7 字段 + 行类型特有 target/effect_id/pool_value/聚合计数——2026-09-10 评审批修订;行类型封闭枚举 9 类(2026-09-20 伏击上限删除批下线溢出未生效行 overflow)——2026-09-15 格面资源批由 15 类收拢:删道具行 / 已消耗未生效行 / 使用被拒行 / 窗口打开锚行 window_open / 窗口跳过锚行 window_skip 五类随可选类删除下线〔2026-09-15 窗口锚行批曾由 13 扩为 15,同日回撤〕;行来源断言两层豁免集合同批修订 = 行型豁免 {decay, verdict} × 来源类别豁免 {hero_attack, enemy_action, clear_reward},豁免不等于免检——豁免行仍受公共字段与各自语义约束,清单改动须同步 战斗结算 明细流水一节与 battle_log_view 格式化)· **类型化克制结算规格 = 战斗结算 GDD F6(W3 补全,2026-09-14 重算批;2026-09-15 命名统一批;2026-09-19 充能退役批——挖掘期充能/战斗段消费半边废止,加法核点数修正)**——逐分量 armor_effectiveness 查表(奖励直伤道具消费方保留) + mechanics: 结算段序骨架唯一权威载体(授予→增益/减益→战斗→衰减→判定——2026-09-15 格面资源批五段,item_window/item 两段随可选类删除下线)· **随机口径(2026-09-15 护甲等级批修订)**:结算换算层引入回合内随机(ac_roll 护甲等级掷点——必中非归零、低掷按 ac_discount_tiers 档位折扣),掷点流归属未定〔rng_service 流封闭清单 board_gen/reward_roll/encounter 不含结算内消费——新增流须改代码+P1 支柱评审、复用 encounter 则改"实例化期恰一 scope"绑定,登记 战斗结算「待办」,rng_service 条目随定稿批修订〕· 可回放契约 = **封闭随机流可回放**(同 master_seed 同意图序列 ⇒ 步骤流逐位复现——数据配置 AC-15 落点,原"结算路径零随机 / 步骤流逐行相同"口径随批作废;开战掷区间 / 装板摇号属初始化随机维持不变)· 段内伤害序(案C 2026-10-06 用户裁)= 主角先手→逐敌还手→地图伤害账殿后(授予段伤害形态单据暂存、战斗段末按事件序补提交统一结账;主角倒下暂存单整体不兑逐单留「未生效」行)· 调用序钉死 按摇定技能行兑现→apply_damage→commit_action(2026-09-16 技能池批改型——原 query_attack→apply_damage→advance 随行为环退役;行型 10 类同批改型:蓄力行 charge → 施放行 cast〔增益技 0 伤害施放、具名技能、不掷不折——原"不兑伏击"语义随 2026-09-20 八型批伏击退役改述〕)· 衰减恒执行含末轮 · 高光双落载体(perfect_sweep P_perfect——2026-09-10 修订为真实双份兑付载体 / 标中旗 highlight 标记)· 流水行协议字段分层(公共 7 字段 + 行类型特有 target/effect_id/pool_value/聚合计数——2026-09-10 评审批修订;行类型封闭枚举 9 类(2026-09-20 伏击上限删除批下线溢出未生效行 overflow)——2026-09-15 格面资源批由 15 类收拢:删道具行 / 已消耗未生效行 / 使用被拒行 / 窗口打开锚行 window_open / 窗口跳过锚行 window_skip 五类随可选类删除下线〔2026-09-15 窗口锚行批曾由 13 扩为 15,同日回撤〕;行来源断言两层豁免集合同批修订 = 行型豁免 {decay, verdict} × 来源类别豁免 {hero_attack, enemy_action, clear_reward},豁免不等于免检——豁免行仍受公共字段与各自语义约束,清单改动须同步 战斗结算 明细流水一节与 battle_log_view 格式化)· **类型化克制结算规格 = 战斗结算 GDD F6(W3 补全,2026-09-14 重算批;2026-09-15 命名统一批;2026-09-19 充能退役批——挖掘期充能/战斗段消费半边废止,加法核点数修正)**——逐分量 armor_effectiveness 查表(奖励直伤道具消费方保留) closure_receipt: 行动经济 F1 条款①②③⑤闭合 owner = 战斗结算(2026-09-10 F0–F5 首版验算完成——F0 瞬时当量口径 + m_w 值域裁决见 status_effect.anchor_warning;条款③ clause3_supply 闭合回执;条款⑤ clause5_dominance 条目;actuals p10 交付后强制重跑) referenced_by: [HUD框架, 战斗表现, 音频, 教学引导, 难度曲线与Boss] diff --git a/game/README.md b/game/README.md index d1d314aa..c4716911 100644 --- a/game/README.md +++ b/game/README.md @@ -548,3 +548,11 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/a **结账时机双模式(2026-09-24 用户三裁决之二三)**:目标 = 结账时刻照规则看现场解析(最前存活敌 / 主角 / 全体存活敌),开单不冻结对象;现行 = 段界结账(授予段尾 / 清图批同步点 / 战斗段尾统一兑现),开单即结保留为回切结构位(控制器 policy 常量一行切回=逐位还原迁移前行为)。同一输入两模式结果可不同(兑现时机语义差异),历史基线读数自本批以段界结账口径为准(重测归曲线调参批)。 **验证**:冒烟 557 项 0 失败双模式全绿三闸过;A/B 对拍探针四配置(L1/L2 × 双种子)两模式各自同种子逐位确定、四配置全部真实分岔(行数两两恒等、纯次序位移——分岔面 = 伤害行攒批位移与治疗满血判定时机,符合段界结账语义);三场景探针全 PASS。设计权威 = 战斗结算.md v0.24「伤害单据与伤害控制器」节。 + +## 2026-10-06 地图伤害账殿后批(案C,用户裁——「主角先手」歧义质询触发;设计权威 = 战斗结算.md v0.26) + +**裁决**:每轮结算内三者顺序定死 = ①授予段兑非伤害账(资源/增益/治疗/行动点/GP/警觉)→ ③战斗段「主角先基础攻击 → 敌人按生成序逐个还手 → 地图伤害账殿后」→ ④衰减 → ⑤判定。授予段不再当场兑付任何伤害——全部伤害形态单据(爆裂/直伤/反噬/地形变异/战旗军规/熔火反噬/惊巢反制/魔能共鸣/清障炸药等)暂存,战斗段怪物还手后按授予段事件序补提交统一结账;主角死于敌方还手 → 暂存单整体不兑,逐单留「未生效(主角已倒下)」行(载荷 forfeit=hero_down)。 + +**切片落点**:`battle_orchestrator.gd` 暂存闸 `_defer_board_damage`/`_deferred_board` + `_submit` 案C 分流(sync 同闸——清图批一致延后)+ `_outcome_of`(暂存单按「将兑付」回报——完美第二份/高光份数依据开单时点事实)+ 授予段四处直提改走闸 + 战斗段末补提交块(死亡守卫+留痕);授予段界 flush 保留(对空队 no-op 防御位);`smoke_all.gd` 行相位分类器换代(damage 恒战斗段/reward_ineffective 按 forfeit 分流)+ 先手断言收紧(「存在或授予段终局」→「必存在」)+ 新增案C 段内序断言(地图账不得先于主角先手或插入敌方还手中间)。 + +**验证**:冒烟 783 项 0 失败三闸过 + run_all 17 套全绿 + probe_gate(ui_input/ui_struct/ui_contrast/--click 行为走查)全绿、残留 0。**旧行为 MC 锚全数作废**(死亡短路语义变化:授予段伤害不再能致死短路先手;读数重采归数值批)。 diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index adc16beb..740830d4 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -14,7 +14,10 @@ # 伤害管线单据化批(2026-09-24 用户三裁决,06 台账 148):伤害兑付全部收口 伤害控制器(DamageController # 单点管线)——本类六调用点(主角先手/敌方伤害技/棋盘克制直伤/群伤/反噬/地形变异)退化为开单点 # (流程决定何时开单给谁开单;单据怎么算归控制器)。结账时机开关在控制器 policy:IMMEDIATE(A=开单 -# 即结,逐位等价迁移前行为——回切位)/ SEGMENT(B=攒批到段界 flush:授予段尾〔含清图批前〕+ 战斗段尾)。 +# 即结,逐位等价迁移前行为——回切位)/ SEGMENT(B=攒批到段界 flush:战斗段尾统一兑现)。 +# 地图伤害账殿后批(案C 2026-10-06 用户裁——「主角先手」歧义质询触发):授予段不再当场兑付任何伤害—— +# 全部伤害形态单据经 _submit 暂存闸入 _deferred_board(保授予段事件序),战斗段「主角先手→逐敌还手」后 +# 补提交统一结账;主角死于敌方还手 → 暂存单整体不兑,逐单留「未生效(主角已倒下)」行(forfeit=hero_down)。 # 死亡事件 = 单位容器 unit_died(单位自行宣布)→ 控制器作废该敌未结单据〔死亡即不动手〕/ 主角死终止兑付。 # 切片登记:① 事件总线载体 = 直连信号(实现期 ADR 项,provisional)② Waiting 相位刷新通道 2026-09-15 用户拍板整体 # 拆除(仅 Active 挖掘期受理)③ 效果行协议 = 授予段逐 grant 行 + 增益/减益段续存实例组合行。 @@ -63,6 +66,8 @@ var _facts_reveals: Array[Dictionary] = [] var _facts_flags: Array[Dictionary] = [] var _facts_path := "" var _settling := false +var _defer_board_damage := false # 案C 暂存闸(2026-10-06 用户裁:地图奖励伤害兑付迁战斗段末——怪物还手后) +var _deferred_board: Array[Dictionary] = [] # 授予段暂存伤害单据(保授予段事件序;战斗段怪物还手后按序补提交统一结账) var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_cap_per_round) var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用) var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励) @@ -449,8 +454,9 @@ func _maybe_settle() -> void: func _settle() -> void: _settling = true + _defer_board_damage = true # 案C:授予段伤害单据只暂存不结账(2026-10-06 用户裁——兑付时点=战斗段末) var ri := round_index - # ① 授予段:结算队列(开格/旗判定/清图)→ 极性路由逐批兑付。 + # ① 授予段:结算队列(开格/旗判定/清图)→ 极性路由逐批兑付(伤害形态单据暂存——见 _submit 案C 闸)。 # 判定基线 = 轮末在场旗(发射面已过滤——被挖开的旗已随挖掘擦除零兑付,插旗不可逆批 2026-09-19; # 开格事实不可撤)。 for evt in _facts_reveals: @@ -489,7 +495,7 @@ func _settle() -> void: var spell_t := _damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_first_dig_spell", "奥术矢", "board", int(_damage.feat_flags.get("spell_value", 0)), DamageController.TARGET_FRONT_ALIVE, ri, evt.cell, "magic") - _damage.submit(spell_t) + _submit(spell_t, false) # 掘进蓄能计数(迭代 11 同调批 T6——battle 域安全开格计数,每 charge_n 格积 1 点) if _attune_has("dig_charge"): _attune_safe_seen += 1 @@ -501,9 +507,10 @@ func _settle() -> void: # 标中不发(旗已随挖掘擦除、不入轮末判定基线)。 _grant_routed(_feat_yield_adjust(snap, false), "negative", "mine_hit", evt.cell, ri, "") _feat_combo = 0 # 矿脉直觉连击清零(踩雷断连——「推理密度直接奖励」的对侧) - # 地形变异 兑付段(授予段尾——触发枚举 on_open:挖开即伤、chain 通道同口径,轮末结算逐行兑现; - # 信息型 hide_number 不进结算——呈现层读取,见 board_view 数字遮蔽;雷格恒无变异——布置段排除, - # 双层叠罚不存在;插旗格直挖照吃变异伤害——地形后果同 mine_hit 口径,非奖励不作废) + # 地形变异 兑付段(授予段尾——触发枚举 on_open:挖开即伤、chain 通道同口径;伤害单据案C 暂存到 + # 战斗段末兑付〔2026-10-06 用户裁〕;信息型 hide_number 不进结算——呈现层读取,见 board_view 数字遮蔽; + # 雷格恒无变异——布置段排除,双层叠罚不存在;插旗格直挖照吃变异伤害——地形后果同 mine_hit 口径, + # 非奖励不作废;吞噬流资源兑付〔荆棘编织/地脉补偿〕中伤害形态行同样经案C闸暂存,非伤害形态照常当场) for evt in _facts_reveals: var vids: Array = evt.get("variant_ids", []) for vid in vids: @@ -513,7 +520,7 @@ func _settle() -> void: # 地形变异伤害单据(伤害管线单据化批 2026-09-24)——无类型必中,下限保底在控制器管线内 var vt := _damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, "地形变异", String(vrow.get("display_name", vid)), "board", \ int(vrow.get("effect_value", 0)), DamageController.TARGET_HERO, ri, evt.cell, "", 0, "", {"variant_id": String(vid)}) - _damage.submit(vt) + _submit(vt, false) # 吞噬流四面(迭代 12 T6——地形从纯税到第三资源带;伤害仍发生=乙稿判据 3 承载): # ①荆棘编织者=挖开伤害型地形格额外掉落一次格面资源(主动吃地形换资源) if int(_damage.feat_flags.get("terrain_dig_double", 0)) > 0: @@ -533,7 +540,7 @@ func _settle() -> void: # 战旗军规开单段(迭代 12 T3 标旗流——flag_judged 批后统一开单:轮末每面标中旗对最前存活敌 # 结算 board_typed 必中伤害〔复用零新配方;「旗所在巢穴敌」简化为 default_target 最前存活敌—— # 台账披露待追认〕;完美布防=本轮旗批全中(hit>0 且 miss==0)时旗伤 ×mult——全对推理奖励)。 - # 扫雷结算触发点=flag_strike/flag_all_hit/nest_aura 挂点承载红线①。 + # 扫雷结算触发点=flag_strike/flag_all_hit/nest_aura 挂点承载红线①;伤害单据案C 暂存战斗段末兑付。 if int(_damage.feat_flags.get("flag_strike_value", 0)) > 0: var hits := 0 var misses := 0 @@ -553,23 +560,23 @@ func _settle() -> void: var st_t := _damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_flag_strike", "战旗军规", "board", strike_value, DamageController.TARGET_FRONT_ALIVE, ri, fevt.cell, "normal") - _damage.submit(st_t) - # 熔火之躯轮末反打(迭代 12 T6 吞噬流——受地形伤积层,轮末每层对全体敌 ~1 必中反伤; - # 触发键=「受地形伤」通用不按地形种类特化——定稿红线承载;反打后清零) + _submit(st_t, false) + # 熔火之躯轮末反打(迭代 12 T6 吞噬流——受地形伤积层,对全体敌 ~1 必中反伤; + # 触发键=「受地形伤」通用不按地形种类特化——定稿红线承载;反打后清零;单据案C 暂存战斗段末) var magma := int(_damage.feat_flags.get("variant_counter_value", 0)) if magma > 0 and _feat_magma_layers > 0: var mg_t := _damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_magma_body", "熔火反噬 ×%d" % _feat_magma_layers, "board", magma * _feat_magma_layers, DamageController.TARGET_ALL_ALIVE, ri, minesweeper.hero_pos(), "", 0, "", {}) - _damage.submit(mg_t) + _submit(mg_t, false) _feat_magma_layers = 0 if _facts_path == "cleared": _grant_clear_reward(ri) - _damage.flush() # 授予段界结账(B 模式——段内开单攒批到此统一兑现;A 模式 no-op;清图批前另有同步结账点) + _damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界) # ② 增益/减益段(续存实例组合行;本结算新授予的已在授予段逐 grant 出行——增益状态 义务⑦) _emit_composition_effects("hero", ri) _emit_composition_effects("enemy", ri) - # ③ 战斗段(调用序:掷点判定 → apply_damage → commit_action;hero hp=0 短路) + # ③ 战斗段(调用序:主角先手 → 逐敌还手 → 地图伤害账殿后〔案C 2026-10-06 用户裁〕;hero hp=0 短路) _combat_segment(ri) # ④ 衰减段(恰一次恒执行含末轮——先兑付后衰减) var k: int = _status_effects.decay_round() @@ -775,7 +782,7 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri: if ctype == "blast": var t := _damage.open_ticket(DamageController.KIND_BOARD_BLAST, source_class, display, "board", power, \ DamageController.TARGET_ALL_ALIVE, ri, cell, "", 0, highlight, {"batch_key": String(row.reward_id)}) - return _damage.outcome(_submit(t, sync)) + return _outcome_of(_submit(t, sync)) if ctype == "pierce" or ctype == "magic": # 伤害道具批(2026-09-18)语义不变:正向 = 单目标克制直伤(必中全额不掷点——棋盘来源伤害既有口径); # 负向 = 主角直伤(复用反噬通道)。 @@ -784,7 +791,7 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri: var atk_type: String = String(row.get("attack_type", ctype)) var t2 := _damage.open_ticket(DamageController.KIND_BOARD_TYPED, source_class, display, "board", power, \ DamageController.TARGET_FRONT_ALIVE, ri, cell, atk_type, 0, highlight, {"batch_key": String(row.reward_id)}) - return _damage.outcome(_submit(t2, sync)) + return _outcome_of(_submit(t2, sync)) if ctype == "heal": if String(row.get("polarity", "positive")) == "negative": return _submit_flat_hero(source_class, display, power, cell, ri, highlight, sync) @@ -805,14 +812,23 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri: # 开单分流:sync(清图批)走控制器同步开单——先按序结清段内排队单据、再当场兑现本单; # 常规开单按结账时机政策(A 即结 / B 入队)。 func _submit(t: Dictionary, sync: bool) -> int: + if _defer_board_damage: + _deferred_board.append(t) # 案C:授予段伤害单据暂存——战斗段怪物还手后按事件序补提交(sync 同闸,清图批一致延后) + return -1 return _damage.flush_submit(t) if sync else _damage.submit(t) +# 案C 结账读数:暂存单(_submit 返回 -1)以「将兑付」回报——授予段兑付决策(清图完美第二份等)只能 +# 依据开单时点事实(该时点敌人存活有保证);战斗段末实际结账若目标已灭,控制器自记未生效行。 +func _outcome_of(ticket_id: int) -> String: + return "redeemed" if ticket_id < 0 else _damage.outcome(ticket_id) + + # 主角直伤单据(反噬/治疗包负向——无类型必中,下限保底在控制器管线内) func _submit_flat_hero(source_class: String, display: String, power: int, cell: Vector2i, ri: int, highlight: String, sync := false) -> String: var t := _damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, source_class, display, "board", power, \ DamageController.TARGET_HERO, ri, cell, "", 0, highlight, {}) - return _damage.outcome(_submit(t, sync)) + return _outcome_of(_submit(t, sync)) # 旗判定批:标中 = 该雷格资源的正向兑付(每雷实例一次;高光 = 强制检查点); @@ -942,6 +958,7 @@ func _roll_round_actions() -> void: # 停开(B 模式下若 flush 中主角倒下,由控制器死亡事件终止余单——两模式同一结果)。 func _combat_segment(ri: int) -> void: + _defer_board_damage = false # 案C 闸复位——战斗段起开单即入队(主角/敌方/补提交的地图单按开单序统一结账) # 本轮扫雷事实推导(迭代 12 T4——执拗 streak/连珠矢段数共用取数面):本轮 safe 事件数= # streak;踩雷即清零(mine_hit 监听同口——执拗「踩雷清零」承载)。攻击在战斗段=授予段后 # 同一次 _settle 内,facts 已定。 @@ -956,7 +973,8 @@ func _combat_segment(ri: int) -> void: # 蓄力层注入(迭代 12 T5——整备军备「每层下轮首段 +1」/厚积薄发满层 spike/铁壁整备减伤—— # 三件同读 action_economy.charge_layers;battle 域每轮刷新) _damage.feat_flags["charge_layers"] = action_economy.charge_layers - # ⓪ 主角先手攻击(基础攻击恒存在——2026-09-11 用户裁决;先于敌方行动;授予段负向直伤致死 → 致电短路不先手)。 + # ⓪ 主角先手攻击(基础攻击恒存在——2026-09-11 用户裁决;先于敌方行动。案C 后授予段不再产出伤害, + # 主角进入战斗段时恒存活——先手必兑,除非场上无敌)。 # 单分量伤害构成(2026-09-19 充能退役批·用户拍板):主角攻击 = 唯一基础分量(atk,吃掷点含暴击; # 类型 = 主角 attack_type 表值——2026-09-18 营地扩展前置批:量与类型抽象分离;查甲效矩阵对应行取克制修正)。 # 原"基础 + 穿刺/魔法充能三分量"模型随充能系统整体退役——克制策略归冒险前配置(营地换型 / 未来冒险前参数面), @@ -1010,7 +1028,18 @@ func _combat_segment(ri: int) -> void: et["source_unit"] = u et["context"] = {"skill_id": skill_id, "baseline": baseline} _damage.submit(et) - _damage.flush() # 战斗段界结账(B 模式——段内开单攒批到此统一兑现;A 模式 no-op) + # 地图伤害兑付(案C 2026-10-06 用户裁——战斗段末:怪物还手后按授予段事件序补提交统一结账; + # 主角死于怪物 → 地图账不兑,逐单留「未生效」行不作废静默——没生效也要留痕口径; + # 补提交后若地图直伤反杀主角,控制器 _halted 作废余单——战斗已败,胜负归判定段) + if int(_enemy_system.hero_unit().get("hp", 0)) <= 0: + for t in _deferred_board: + _emit_row("reward_ineffective", String(t.get("source_class", "")), t.get("origin_cell", Vector2i(-1, -1)), ri, + "%s 未生效(主角已倒下)" % String(t.get("display", "")), 0, {"forfeit": "hero_down"}, "") + else: + for t in _deferred_board: + _damage.submit(t) + _deferred_board.clear() + _damage.flush() # 战斗段界结账(B 模式——主角单+敌方单+地图单按开单序统一兑现;A 模式地图单补提交时即结) # ---- 算术读数(伤害管线单据化批 2026-09-24 迁移注记)---- diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index 25066713..0cfa24ab 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -1665,9 +1665,9 @@ func _smoke_full_battle(L: int, D: int, label: String) -> void: if not (row.has(atk_t) and row.has("roll") and row.has("ac") and row.has("tier")): hero_breakdown_ok = false check(has_verdict, "%s 步骤流含判定行" % label) - # 伤害道具批(2026-09-18):单弱敌遭遇可被挖掘期兑付(穿刺弹/魔法矢/爆裂粉尘)在授予段单独击杀—— - # 敌全灭后战斗段整段跳过、零先手行为合法结局;先手规则本身由强敌场景(L1/L2 深节点)继续守护。 - check(hero_attacks >= 1 or run1.terminated, "%s 主角先手攻击行存在或授予段终局(2026-09-11 拍板① + 伤害道具批)×%d" % [label, hero_attacks]) + # 案C(2026-10-06 用户裁):授予段不再产出伤害——进入战斗段时主角恒存活且场上有敌, + # 先手必兑;旧「挖掘期兑付可在授予段单独击杀、零先手合法」结局随地图账迁战斗段末整体消失。 + check(hero_attacks >= 1, "%s 主角先手攻击行必存在(2026-09-11 拍板① + 案C 地图账殿后)×%d" % [label, hero_attacks]) check(hero_breakdown_ok, "%s 主角攻击行携带类型键与掷点读数(单分量——充能退役批)" % label) @@ -2784,6 +2784,32 @@ func _smoke_settlement_segments() -> void: if decays != 1 or verdicts != 1: decay_verdict_ok = false # 衰减/判定每结算恰一次 check(order_ok, "每轮行相位单调(授予→增益/减益→战斗→衰减→判定)") + # 案C 段内序(2026-10-06 用户裁):段内伤害序 = 主角先手 →〔连珠矢随先手段〕→ 敌方逐个 → 地图账殿后。 + # 地图账 = 全部棋盘来源伤害行 + 主角倒下不兑留痕行;违序(先于先手或插进敌方还手中间)即失败。 + var mapdmg_ok := true + for ri_key in by_round.keys(): + var rows_r: Array = by_round[ri_key] + var first_hero := -1 + var last_enemy := -1 + for i in rows_r.size(): + var rw: Dictionary = rows_r[i] + if String(rw.get("type", "")) != "damage": + continue + if String(rw.get("source_class", "")) == "hero_attack" and first_hero < 0: + first_hero = i + elif String(rw.get("source_class", "")) == "enemy_action": + last_enemy = i + for i in rows_r.size(): + var rw: Dictionary = rows_r[i] + var sc := String(rw.get("source_class", "")) + var is_board_acct: bool = (String(rw.get("type", "")) == "damage" \ + and not ["hero_attack", "enemy_action", "feat_chain_arrows"].has(sc)) \ + or String(rw.get("forfeit", "")) == "hero_down" + if not is_board_acct: + continue + if (first_hero >= 0 and i < first_hero) or (last_enemy >= 0 and i < last_enemy): + mapdmg_ok = false + check(mapdmg_ok, "案C 段内序:地图伤害账殿后(主角先手→敌方逐个→地图账;连珠矢随先手段)") check(decay_verdict_ok, "衰减/判定段每结算恰一段(先兑付后衰减——含末轮)") @@ -2797,9 +2823,9 @@ func _row_phase(row: Dictionary, ri: int) -> int: "cast": return 2 "damage": - if String(row.get("source_class", "")) in ["hero_attack", "enemy_action"]: - return 2 - return 0 # 授予段 flat 兑付(爆裂/直伤) + return 2 # 案C(2026-10-06 用户裁):全部伤害兑付=战斗段(主角先手/敌方还手/地图账殿后统一结账) + "reward_ineffective": + return 2 if String(row.get("forfeit", "")) == "hero_down" else 0 # 战斗段末地图账不兑留痕 vs 授予段未生效 match String(row.get("source_class", "")): "safe_cell", "mine_hit", "flag_hit", "flag_miss", "clear_reward": if int(row.get("round_index", ri)) < ri: