# 「永夜」脊炮命中无 bounds 目标导致战斗崩溃(NPE)
> AI 使用说明:由 AI 助手协助整理撰写
## 环境
- 游戏版本:0.98a-RC8
- Mod:人类联合帝国 v1.0.11
## 表现
战斗中弹出 Fatal 窗口,战斗随即中断:
```
Fatal: Cannot invoke "com.fs.starfarer.api.combat.BoundsAPI$SegmentAPI.getP1()" because "seg" is null
Check starsector.log for more info.
```
`starsector.log` 中的完整堆栈:
```
java.lang.NullPointerException: Cannot invoke "com.fs.starfarer.api.combat.BoundsAPI$SegmentAPI.getP1()" because "seg" is null
at data.scripts.weapons.Silent_ForeverNightSpinalGunOnHit.onHit(Silent_ForeverNightSpinalGunOnHit.java:39)
at com.fs.starfarer.combat.entities.Missile.notifyDealtDamage(Unknown Source)
at com.fs.starfarer.combat.E.A.D.o00000(Unknown Source)
at com.fs.starfarer.combat.E.oOOO.super(Unknown Source)
at com.fs.starfarer.combat.E.oOOO.super(Unknown Source)
at com.fs.starfarer.combat.CombatEngine.advanceInner(Unknown Source)
at com.fs.starfarer.combat.CombatEngine.advance(Unknown Source)
at com.fs.starfarer.combat.CombatState.traverse(Unknown Source)
at com.fs.state.AppDriver.begin(Unknown Source)
at com.fs.starfarer.combat.CombatMain.main(Unknown Source)
at com.fs.starfarer.StarfarerLauncher.o00000(Unknown Source)
at com.fs.starfarer.StarfarerLauncher$1.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
```
触发条件:「永夜」脊状炮(`silent_forever_night_spinal`)的弹体命中一个**无护盾**目标时。
## 根因
文件:`data/scripts/weapons/Silent_ForeverNightSpinalGunOnHit.java`
第 38–39 行:
```java
var seg = getNearestSegmentOnBounds(point, targetShip);
var dir = Vector2f.sub(seg.getP1(), seg.getP2(), null); // seg 可能为 null
```
`getNearestSegmentOnBounds()`(第 74–93 行)沿用了 LazyWizard 的工具方法,注释里也写着
`// Fallback in case entity somehow lacks any segments in its bounds`,但兜底并未实现:
- 第 78 行 `SegmentAPI closestSeg = null;` 遍历结束后**直接 return**——当目标 bounds 内没有任何线段时,返回的就是 null;
- 第 76 行 `entity.getExactBounds()` 自身返回 null 的情况同样未判断(会先在第 80 行 `bounds.update(...)` 处再抛一次 NPE)。
调用方第 39 行拿到 null 后立即执行 `seg.getP1()`,抛出 NPE。由于异常发生在 `Missile.notifyDealtDamage` 内,被 CombatMain 捕获后直接弹 Fatal 并中断战斗。
目标 bounds 为空的现实来源:部分舰船 hull 未定义 `"bounds"`,或使用空贴图占位(如 `graphics/fx/empty.png`)——典型是隐形无人机、护盾体一类的辅助单位。这类目标 `getExactBounds()` 没有任何线段,就会走进 null 分支。命中带护盾的目标走的是第 43 行的另一条分支(纯角度计算),因此不受影响。
同 mod 的 `data/scripts/util/SilentUtil.java` 中相同形式的遍历是有 `if (potTarget.getExactBounds() == null) continue;` 兜底的,可见此处属于遗漏。
## 修复建议
给第 38–41 行加空值兜底,fallback 直接复用第 43 行护盾分支已有的角度算法:
```java
var seg = getNearestSegmentOnBounds(point, targetShip);
// 某些无 bounds / 空贴图的舰船(隐形无人机等)没有线段,seg 会是 null
if (seg != null) {
var dir = Vector2f.sub(seg.getP1(), seg.getP2(), null);
float delta = Misc.getAngleInDegrees(dir);
renderer.dirOffset = delta - targetShip.getFacing();
} else {
renderer.dirOffset = Misc.getAngleInDegrees(point, targetShip.getLocation()) - 90f - targetShip.getFacing();
}
```
同时建议在 `getNearestSegmentOnBounds()` 内补上 bounds 自身的判空:
```java
final BoundsAPI bounds = entity.getExactBounds();
if (bounds == null) return null;
SegmentAPI closestSeg = null;
```
改动只影响特效朝向,不涉及伤害结算;无 bounds 的目标退回「命中点 → 目标中心」的方向,视觉上等价。
## 补充
该缺陷位于 `jars/Silence.jar` 内,只改 `src/` 不生效——日志会提示
`Class [data.scripts.weapons.Silent_ForeverNightSpinalGunOnHit] already loaded (perhaps from jar file, ...), skipping compilation`,
需要重新编译并打包 `jars/Silence.jar`。
|