fix(possess): don't destroy caster in Apply; defer possession to OnJumpCompleted to prevent PawnFlyer NRE

- CompAbilityEffect_Possess.Apply now only logs; DoPossession runs after landing
- Harden HediffComp_GestaltNode against null/destroyed pawn (tick/severity/overlord search/relations)
- Fix GestaltOverseer Notify_PostRemovedByDeath message condition (only when overlord died/destroyed and hive node alive)
This commit is contained in:
2026-02-12 17:39:58 +08:00
parent c04d0bdba6
commit 033a618921
4 changed files with 56 additions and 21 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -35,7 +35,13 @@ namespace ArachnaeSwarm
{ {
get get
{ {
CompGestalt comp = Pawn.TryGetComp<CompGestalt>(); var pawn = Pawn;
if (pawn == null)
{
return tracker;
}
CompGestalt comp = pawn.TryGetComp<CompGestalt>();
if (comp != null) if (comp != null)
{ {
return comp.GestaltTracker; return comp.GestaltTracker;
@@ -43,7 +49,7 @@ namespace ArachnaeSwarm
if (tracker == null && NodeType == GestaltNodeType.OverlordNode) if (tracker == null && NodeType == GestaltNodeType.OverlordNode)
{ {
tracker = new Pawn_GestaltTracker(Pawn); tracker = new Pawn_GestaltTracker(pawn);
} }
return tracker; return tracker;
} }
@@ -60,6 +66,12 @@ namespace ArachnaeSwarm
{ {
base.CompPostTick(ref severityAdjustment); base.CompPostTick(ref severityAdjustment);
var pawn = Pawn;
if (pawn == null)
{
return;
}
if (NodeType == GestaltNodeType.HiveNode) if (NodeType == GestaltNodeType.HiveNode)
{ {
// 定期更新严重性 // 定期更新严重性
@@ -69,9 +81,9 @@ namespace ArachnaeSwarm
} }
// 定期寻找 OverlordNode // 定期寻找 OverlordNode
if (Pawn.IsColonist && if (pawn.IsColonist &&
Pawn.Spawned && pawn.Spawned &&
!Pawn.Dead && !pawn.Dead &&
Find.TickManager.TicksGame % 250 == 0) Find.TickManager.TicksGame % 250 == 0)
{ {
TryFindOverlord(); TryFindOverlord();
@@ -87,6 +99,12 @@ namespace ArachnaeSwarm
if (NodeType != GestaltNodeType.HiveNode || parent == null) if (NodeType != GestaltNodeType.HiveNode || parent == null)
return; return;
var pawn = Pawn;
if (pawn?.health == null)
{
return;
}
bool isConnected = IsConnectedToOverlord(); bool isConnected = IsConnectedToOverlord();
// 只有连接状态改变时才更新,避免每帧都设置 // 只有连接状态改变时才更新,避免每帧都设置
@@ -103,7 +121,7 @@ namespace ArachnaeSwarm
wasConnected = isConnected; wasConnected = isConnected;
// 如果严重性改变需要重新计算Pawn的能力 // 如果严重性改变需要重新计算Pawn的能力
Pawn.health.Notify_HediffChanged(parent); pawn.health.Notify_HediffChanged(parent);
} }
} }
@@ -118,6 +136,12 @@ namespace ArachnaeSwarm
public void TryFindOverlord() public void TryFindOverlord()
{ {
// 如果已经有Overlord更新连接状态并返回 // 如果已经有Overlord更新连接状态并返回
var pawn = Pawn;
if (pawn == null || pawn.Dead || pawn.Destroyed)
{
return;
}
if (IsConnectedToOverlord()) if (IsConnectedToOverlord())
{ {
UpdateSeverityBasedOnConnection(); UpdateSeverityBasedOnConnection();
@@ -125,7 +149,7 @@ namespace ArachnaeSwarm
} }
// 如果Pawn当前不在地图上无法寻找Overlord // 如果Pawn当前不在地图上无法寻找Overlord
if (Pawn.Map == null) if (pawn.Map == null)
{ {
return; return;
} }
@@ -134,22 +158,23 @@ namespace ArachnaeSwarm
Pawn bestOverlord = null; Pawn bestOverlord = null;
float bestScore = -1f; float bestScore = -1f;
foreach (Pawn pawn in Pawn.Map.mapPawns.FreeColonists) foreach (Pawn candidate in pawn.Map.mapPawns.FreeColonists)
{ {
HediffComp_GestaltNode nodeComp = pawn.GetGestaltNodeComp(); HediffComp_GestaltNode nodeComp = candidate.GetGestaltNodeComp();
if (nodeComp?.NodeType == GestaltNodeType.OverlordNode && if (nodeComp?.NodeType == GestaltNodeType.OverlordNode &&
pawn != Pawn && candidate != pawn &&
!pawn.Dead && !candidate.Dead &&
!pawn.Downed) !candidate.Downed &&
!candidate.Destroyed)
{ {
Pawn_GestaltTracker tracker = pawn.GestaltTracker(); Pawn_GestaltTracker tracker = candidate.GestaltTracker();
if (tracker != null && tracker.CanControlPawn(Pawn)) if (tracker != null && tracker.CanControlPawn(pawn))
{ {
float score = CalculateOverlordScore(pawn, tracker); float score = CalculateOverlordScore(candidate, tracker);
if (score > bestScore) if (score > bestScore)
{ {
bestScore = score; bestScore = score;
bestOverlord = pawn; bestOverlord = candidate;
} }
} }
} }
@@ -158,7 +183,11 @@ namespace ArachnaeSwarm
// 找到合适的Overlord建立关系并更新严重性 // 找到合适的Overlord建立关系并更新严重性
if (bestOverlord != null) if (bestOverlord != null)
{ {
Pawn.relations.AddDirectRelation(ARA_PawnRelationDefOf.ARA_GestaltOverseer, bestOverlord); // Pawns being destroyed/vanished can temporarily have no relations tracker.
if (pawn.relations != null && ARA_PawnRelationDefOf.ARA_GestaltOverseer != null)
{
pawn.relations.AddDirectRelation(ARA_PawnRelationDefOf.ARA_GestaltOverseer, bestOverlord);
}
UpdateSeverityBasedOnConnection(); UpdateSeverityBasedOnConnection();
} }
} }
@@ -188,10 +217,16 @@ namespace ArachnaeSwarm
// 当Hediff被移除时如果连接到Overlord需要断开连接 // 当Hediff被移除时如果连接到Overlord需要断开连接
if (NodeType == GestaltNodeType.HiveNode && IsConnectedToOverlord()) if (NodeType == GestaltNodeType.HiveNode && IsConnectedToOverlord())
{ {
Pawn overlord = Pawn.GetOverlord(); var pawn = Pawn;
if (pawn?.relations == null || ARA_PawnRelationDefOf.ARA_GestaltOverseer == null)
{
return;
}
Pawn overlord = pawn.GetOverlord();
if (overlord != null) if (overlord != null)
{ {
Pawn.relations.RemoveDirectRelation(ARA_PawnRelationDefOf.ARA_GestaltOverseer, overlord); pawn.relations.RemoveDirectRelation(ARA_PawnRelationDefOf.ARA_GestaltOverseer, overlord);
} }
} }
} }

View File

@@ -83,7 +83,7 @@ namespace ArachnaeSwarm
} }
} }
if (overlord != null && hiveNode != null && (!overlord.Dead || overlord.Destroyed)) if (overlord != null && hiveNode != null && !hiveNode.Dead && (overlord.Dead || overlord.Destroyed))
{ {
Messages.Message("MessageGestaltLostControl".Translate(overlord, hiveNode) + ": " + hiveNode.LabelShortCap, Messages.Message("MessageGestaltLostControl".Translate(overlord, hiveNode) + ": " + hiveNode.LabelShortCap,
new LookTargets(new Thing[] { overlord, hiveNode }), MessageTypeDefOf.NeutralEvent); new LookTargets(new Thing[] { overlord, hiveNode }), MessageTypeDefOf.NeutralEvent);