fix(skill): 修复冰霜新星 (Frost Nova #44) 缺失 64 束原版环形扩散投射物动画与减速结算缺陷 (对齐新星 Nova #48 机制) #431

Closed
opened 2026-09-24 07:58:51 +00:00 by troytt · 0 comments
Owner

问题背景与缺陷表现 (Background & Defect Analysis)

在当前客户端实战(src/scene/act-scene.ts)中,法师 6 级冰系核心控制技能 冰霜新星 (Frost Nova, Skill ID: 44) 存在严重的机制降级与动画缺失缺陷:

  1. 完全缺失环形扩散投射物动画:施展冰霜新星时,屏幕上没有任何向外扩散的冰环动画,只有隐形的伤害判定。相比之下,12 级新星 (Nova, Skill ID: 48) 与死灵法师 30 级剧毒新星 (Poison Nova, Skill ID: 92) 均拥有完整、华丽且严格遵循 2:1 等轴测视角的 64 / 32 束环形扩散投射物动画。
  2. 硬编码早期短路拦截:在 src/scene/act-scene.ts 中,skillId === 44 被一段早期的几何距离占位代码(Math.hypot <= 240)直接拦截并 return true,导致施法逻辑根本无法进入标准投射物引擎管线。
  3. 数值与减速状态判定脱离 1.13c 原版标准:当前占位代码硬编码造成固定 20 点伤害,且未应用 1.13c ELen 迟缓减速持续时间(8.0 秒 + 1.0 秒/级)与暴风雪/冰封球协同伤害加成,同时强加了 10 ticks 冷却(原版 Frost Nova 冷却为 0)。

暴雪 1.13c 原版权威数据与底层机制基准 (Blizzard 1.13c Ground Truth Invariants)

根据 Diablo II v1.13c 官方数据表(Skills.txt、Missiles.txt)及逆向汇编规范:

1. Skills.txt (Row 44: Frost Nova vs Row 48: Nova)

属性项 冰霜新星 (Frost Nova #44) 新星 (Nova #48) 说明
Class / ReqLevel sor / Lv 6 sor / Lv 12 法师技能
Server / Client DoFunc srvdofunc = 22, cltdofunc = 25 srvdofunc = 22, cltdofunc = 25 两者完全共享同一套 1.13c Nova 环形生成函数指针!
Missiles srvmissilea/b/c = "frostnova" srvmissilea/b/c = "nova" 生成环形投射物
Cast Overlay ice_cast_2 light_cast_1 施法光晕
Cooldown / Delay 0 帧(无冷却延迟) 0 帧(无冷却延迟) 原版可连发
Element / HitShift cold / 7 (基数除以 2) ltng / 8 (基数除以 1) 伤害类型
Base EMin / EMax 4 / 8 (1 级为 2-4) 1 / 20 (1 级为 1-20) 基础元素伤害
EMin 5-Band Growth 4, 6, 8, 10, 12 6, 7, 8, 9, 10 5 档伤害成长
EMax 5-Band Growth 5, 7, 9, 11, 13 8, 9, 10, 11, 12 5 档伤害成长
Cold Duration (ELen) 200 帧 (8.0s) + 25 帧/级 (+1.0s/级) - 迟缓减速 (Chill) 时长
Synergy Formula (Blizzard.blvl + Frozen Orb.blvl) * 10% 0 (无协同) +10% 冰伤/级

2. Missiles.txt (Row 119: frostnova)

  • CelFile: FrostNova(已完全烘焙入图集 public/missiles/frostnova.png,16 方向,14 帧/方向,AnimLen 14)
  • Kinematics: Vel = 24 (速度 600 px/s), Range = 14 (扩散距离 336 像素)
  • Multi-hit Invariant: NextHit = 1, NextDelay = 4(确保单只怪物在 4 帧内不会被同一波环形投射物重复伤害)
  • Audio: TravelSound = "sorceress_frostnova"

缺陷根因深度剖析 (Root Cause Analysis)

根因 1:src/scene/act-scene.ts 早期分支粗暴拦截并短路 (Line 1339–1354)

在 act-scene.ts 的 castSkill 主逻辑中:

if (skillId === 44) {
  // Frost Nova: AoE radius 240 around player
  let hitCount = 0
  for (const monster of engine.world.monsters) {
    if (monster.state === 'dead' || monster.hp <= 0) continue
    if (Math.hypot(monster.x - player.x, monster.y - player.y) <= 240) {
      damageMonster(engine.world, monster.index, 20)
      engine.metrics.castHits += 1
      hitCount++
    }
  }
  player.cooldown = 10
  engine.metrics.casts += 1
  status.textContent = `施展霜之新星,命中 ${hitCount} 个目标`
  return true // 💥 致命短路:直接返回,彻底剥夺投射物生成与绘制管线!
}

这一硬编码分支直接跳过了投射物调度逻辑,导致游戏世界中根本没有生成任何投射物实体!

根因 2:未接入通用 Nova 环形投射物生成系统 (Line 2001–2012 与 2153–2173)

新星(Nova #48)与剧毒新星(Poison Nova #92)在投射物装配表中配置了:

missileType = 'electricnova' // 或 'poisonnova'
isNova = true
multiCount = 64 // 64 束射线

随后在 2153 行由环形发射器以 2:1 等轴测地面长宽比(ISO_GROUND_ASPECT_RATIO = 0.5)向 360 度发射 64 颗真实投射物:

if (isNova) {
  for (let i = 0; i < multiCount; i++) {
    const angle = (i / multiCount) * 2 * Math.PI
    const pVx = Math.cos(angle) * perTick
    const pVy = Math.sin(angle) * (perTick * ISO_GROUND_ASPECT_RATIO)
    engine.projectiles.push({
      skillId: String(skillId),
      x: player.x,
      y: player.y,
      vx: pVx,
      vy: pVy,
      damage,
      ttl,
      fromPlayer: true,
      missileType,
      statusEffect,
      statusDuration,
      nextHit: 1,
      nextDelay: 4,
    })
  }
}

而 skillId === 44 在投射物配置表中完全缺席!

根因 3:CANONICAL_113C_MISSILES 缺失 frostnova 规范记录

在 src/game/skills.ts 中,CANONICAL_113C_MISSILES 常量表未包含 frostnova,导致通过 getMissileTxtData('frostnova') 查询时回退到默认 20 速度的未定义占位。


修复方案与任务规划 (Remediation Plan)

  1. 移除 src/scene/act-scene.ts 中的占位短路逻辑 (Line 1339–1354):
    • 彻底删除 if (skillId === 44) 强行 return 的死代码,将调度流交给标准投射物生成器。
  2. 在投射物调度表中接入 frostnova (对齐 Nova #48):
    • 配置 missileType = 'frostnova';
    • 启用 isNova = true,multiCount = 64;
    • 速度与射程:speed = 600 px/s,range = 336 px(ttl = 14 ticks);
    • 接入 1.13c 伤害计算:damage = calculateSkillDamage(44, slvl, synergies);
    • 接入 1.13c 减速状态:statusEffect = 'chill',statusDuration = 200 + (slvl - 1) * 25 帧(8.0s + 1.0s/级);
    • 冷却归零:player.cooldown = 0(对齐 1.13c 0 延迟规范)。
  3. 在 src/game/skills.ts 补齐 CANONICAL_113C_MISSILES['frostnova']:
    • 准确配置 id: 119, vel: 24, range: 14, celFile: 'FrostNova', animLen: 14, animSpeed: 16, nextHit: 1, nextDelay: 4 等权威字段。
  4. 验证美术资产与动画绘制:
    • 验证 loadMissileArtMap 正常加载 frostnova 图集,并通过 drawMissileProjectile 在 2:1 等轴测展开时正确依方向播放 14 帧冰环波纹动画。
  5. 单元测试与全量回归守门:
    • 扩充 tests/skills/sor/skill-044-frost-nova.test.ts,增加对 64 束环形投射物发射、NextDelay=4 防重复判定、减速持续时间等级增长以及无 CD 连发的端到端单测。
## 问题背景与缺陷表现 (Background & Defect Analysis) 在当前客户端实战(`src/scene/act-scene.ts`)中,法师 6 级冰系核心控制技能 **冰霜新星 (Frost Nova, Skill ID: 44)** 存在严重的机制降级与动画缺失缺陷: 1. **完全缺失环形扩散投射物动画**:施展冰霜新星时,屏幕上没有任何向外扩散的冰环动画,只有隐形的伤害判定。相比之下,12 级**新星 (Nova, Skill ID: 48)** 与死灵法师 30 级**剧毒新星 (Poison Nova, Skill ID: 92)** 均拥有完整、华丽且严格遵循 2:1 等轴测视角的 64 / 32 束环形扩散投射物动画。 2. **硬编码早期短路拦截**:在 `src/scene/act-scene.ts` 中,`skillId === 44` 被一段早期的几何距离占位代码(`Math.hypot <= 240`)直接拦截并 `return true`,导致施法逻辑根本无法进入标准投射物引擎管线。 3. **数值与减速状态判定脱离 1.13c 原版标准**:当前占位代码硬编码造成固定 20 点伤害,且未应用 1.13c `ELen` 迟缓减速持续时间(8.0 秒 + 1.0 秒/级)与暴风雪/冰封球协同伤害加成,同时强加了 10 ticks 冷却(原版 Frost Nova 冷却为 0)。 --- ## 暴雪 1.13c 原版权威数据与底层机制基准 (Blizzard 1.13c Ground Truth Invariants) 根据 Diablo II v1.13c 官方数据表(`Skills.txt`、`Missiles.txt`)及逆向汇编规范: ### 1. `Skills.txt` (Row 44: Frost Nova vs Row 48: Nova) | 属性项 | 冰霜新星 (Frost Nova #44) | 新星 (Nova #48) | 说明 | |---|---|---|---| | **Class / ReqLevel** | `sor` / Lv 6 | `sor` / Lv 12 | 法师技能 | | **Server / Client DoFunc** | `srvdofunc = 22`, `cltdofunc = 25` | `srvdofunc = 22`, `cltdofunc = 25` | **两者完全共享同一套 1.13c Nova 环形生成函数指针!** | | **Missiles** | `srvmissilea/b/c = "frostnova"` | `srvmissilea/b/c = "nova"` | 生成环形投射物 | | **Cast Overlay** | `ice_cast_2` | `light_cast_1` | 施法光晕 | | **Cooldown / Delay** | `0` 帧(无冷却延迟) | `0` 帧(无冷却延迟) | 原版可连发 | | **Element / HitShift** | `cold` / `7` (基数除以 2) | `ltng` / `8` (基数除以 1) | 伤害类型 | | **Base EMin / EMax** | `4` / `8` (1 级为 2-4) | `1` / `20` (1 级为 1-20) | 基础元素伤害 | | **EMin 5-Band Growth** | `4, 6, 8, 10, 12` | `6, 7, 8, 9, 10` | 5 档伤害成长 | | **EMax 5-Band Growth** | `5, 7, 9, 11, 13` | `8, 9, 10, 11, 12` | 5 档伤害成长 | | **Cold Duration (`ELen`)** | **`200` 帧 (8.0s) + `25` 帧/级 (+1.0s/级)** | - | 迟缓减速 (Chill) 时长 | | **Synergy Formula** | `(Blizzard.blvl + Frozen Orb.blvl) * 10%` | 0 (无协同) | +10% 冰伤/级 | ### 2. `Missiles.txt` (Row 119: frostnova) - **CelFile**: `FrostNova`(已完全烘焙入图集 `public/missiles/frostnova.png`,16 方向,14 帧/方向,AnimLen 14) - **Kinematics**: `Vel = 24` (速度 600 px/s), `Range = 14` (扩散距离 336 像素) - **Multi-hit Invariant**: `NextHit = 1`, `NextDelay = 4`(确保单只怪物在 4 帧内不会被同一波环形投射物重复伤害) - **Audio**: `TravelSound = "sorceress_frostnova"` --- ## 缺陷根因深度剖析 (Root Cause Analysis) ### 根因 1:`src/scene/act-scene.ts` 早期分支粗暴拦截并短路 (Line 1339–1354) 在 `act-scene.ts` 的 `castSkill` 主逻辑中: ```typescript if (skillId === 44) { // Frost Nova: AoE radius 240 around player let hitCount = 0 for (const monster of engine.world.monsters) { if (monster.state === 'dead' || monster.hp <= 0) continue if (Math.hypot(monster.x - player.x, monster.y - player.y) <= 240) { damageMonster(engine.world, monster.index, 20) engine.metrics.castHits += 1 hitCount++ } } player.cooldown = 10 engine.metrics.casts += 1 status.textContent = `施展霜之新星,命中 ${hitCount} 个目标` return true // 💥 致命短路:直接返回,彻底剥夺投射物生成与绘制管线! } ``` 这一硬编码分支直接跳过了投射物调度逻辑,导致游戏世界中根本没有生成任何投射物实体! ### 根因 2:未接入通用 Nova 环形投射物生成系统 (Line 2001–2012 与 2153–2173) 新星(Nova #48)与剧毒新星(Poison Nova #92)在投射物装配表中配置了: ```typescript missileType = 'electricnova' // 或 'poisonnova' isNova = true multiCount = 64 // 64 束射线 ``` 随后在 2153 行由环形发射器以 2:1 等轴测地面长宽比(`ISO_GROUND_ASPECT_RATIO = 0.5`)向 360 度发射 64 颗真实投射物: ```typescript if (isNova) { for (let i = 0; i < multiCount; i++) { const angle = (i / multiCount) * 2 * Math.PI const pVx = Math.cos(angle) * perTick const pVy = Math.sin(angle) * (perTick * ISO_GROUND_ASPECT_RATIO) engine.projectiles.push({ skillId: String(skillId), x: player.x, y: player.y, vx: pVx, vy: pVy, damage, ttl, fromPlayer: true, missileType, statusEffect, statusDuration, nextHit: 1, nextDelay: 4, }) } } ``` 而 `skillId === 44` 在投射物配置表中完全缺席! ### 根因 3:`CANONICAL_113C_MISSILES` 缺失 `frostnova` 规范记录 在 `src/game/skills.ts` 中,`CANONICAL_113C_MISSILES` 常量表未包含 `frostnova`,导致通过 `getMissileTxtData('frostnova')` 查询时回退到默认 20 速度的未定义占位。 --- ## 修复方案与任务规划 (Remediation Plan) 1. **移除 `src/scene/act-scene.ts` 中的占位短路逻辑 (Line 1339–1354)**: - 彻底删除 `if (skillId === 44)` 强行 return 的死代码,将调度流交给标准投射物生成器。 2. **在投射物调度表中接入 `frostnova` (对齐 Nova #48)**: - 配置 `missileType = 'frostnova'`; - 启用 `isNova = true`,`multiCount = 64`; - 速度与射程:`speed = 600` px/s,`range = 336` px(`ttl = 14` ticks); - 接入 1.13c 伤害计算:`damage = calculateSkillDamage(44, slvl, synergies)`; - 接入 1.13c 减速状态:`statusEffect = 'chill'`,`statusDuration = 200 + (slvl - 1) * 25` 帧(8.0s + 1.0s/级); - 冷却归零:`player.cooldown = 0`(对齐 1.13c 0 延迟规范)。 3. **在 `src/game/skills.ts` 补齐 `CANONICAL_113C_MISSILES['frostnova']`**: - 准确配置 `id: 119, vel: 24, range: 14, celFile: 'FrostNova', animLen: 14, animSpeed: 16, nextHit: 1, nextDelay: 4` 等权威字段。 4. **验证美术资产与动画绘制**: - 验证 `loadMissileArtMap` 正常加载 `frostnova` 图集,并通过 `drawMissileProjectile` 在 2:1 等轴测展开时正确依方向播放 14 帧冰环波纹动画。 5. **单元测试与全量回归守门**: - 扩充 `tests/skills/sor/skill-044-frost-nova.test.ts`,增加对 64 束环形投射物发射、NextDelay=4 防重复判定、减速持续时间等级增长以及无 CD 连发的端到端单测。
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#431
No description provided.