diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 54e8aea..7cdd6ef 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/AbilityDefs/Abilities_TrackingCharge.xml b/1.6/1.6/Defs/AbilityDefs/Abilities_TrackingCharge.xml index b8db240..bd9904e 100644 --- a/1.6/1.6/Defs/AbilityDefs/Abilities_TrackingCharge.xml +++ b/1.6/1.6/Defs/AbilityDefs/Abilities_TrackingCharge.xml @@ -6,8 +6,8 @@ ARA_Flyer_TrackingCharge ArachnaeSwarm.PawnFlyer_TrackingCharge - 0.1 - 0.1 + 0.5 + 0 @@ -16,7 +16,7 @@ ARA_Ability_TrackingCharge 阿拉克涅盾头种对目标发起蓄势冲撞,对路径上的一切造成伤害。飞行的距离越远,伤害越高。 - UI/Abilities/Charge + UI/Commands/WarTrumpet 600 ArachnaeSwarm.Verb_CastAbilityTrackingCharge @@ -36,8 +36,8 @@ 1.5 15 2 - 4 - Crush + 6 + Blunt ARA_Flyer_TrackingCharge diff --git a/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs index d15706d..de8bea8 100644 --- a/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs +++ b/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs @@ -17,6 +17,7 @@ namespace ArachnaeSwarm public float inertiaDistance; public DamageDef collisionDamageDef; public LocalTargetInfo primaryTarget; + public int maxFlightTicks; // --- Internal state --- private Vector3 currentSpeed; @@ -24,6 +25,7 @@ namespace ArachnaeSwarm private bool homing = true; private int inertiaTicks = -1; private Vector3 exactPosition; + private bool hasHitPrimaryTarget = false; // --- Reflection Fields --- private static FieldInfo TicksFlyingInfo; @@ -112,8 +114,9 @@ namespace ArachnaeSwarm float calculatedDamage = this.initialDamage + (this.distanceTraveled * this.damagePerTile); var dinfo = new DamageInfo(this.collisionDamageDef, calculatedDamage, 1f, -1, this.FlyingPawn); - if (homing && primaryTarget.HasThing && (this.exactPosition - primaryTarget.Thing.DrawPos).sqrMagnitude < 1.5f * 1.5f) + if (!hasHitPrimaryTarget && homing && primaryTarget.HasThing && primaryTarget.Thing.Spawned && (this.exactPosition - primaryTarget.Thing.DrawPos).sqrMagnitude < 1.5f * 1.5f) { + hasHitPrimaryTarget = true; // Mark as hit to prevent re-triggering primaryTarget.Thing.TakeDamage(dinfo); homing = false; this.inertiaTicks = (int)(this.inertiaDistance / this.currentSpeed.magnitude); @@ -121,7 +124,9 @@ namespace ArachnaeSwarm foreach (var thing in GenRadial.RadialDistinctThingsAround(this.exactPosition.ToIntVec3(), this.Map, 1.0f, false)) { - if (thing == this.FlyingPawn || thing == this || thing == primaryTarget.Thing) continue; + // Avoid damaging self or the primary target (which is handled above) + if (thing == this.FlyingPawn || thing == this || (hasHitPrimaryTarget && thing == primaryTarget.Thing)) continue; + if (thing is Pawn pawn && !pawn.Downed && pawn.HostileTo(this.FlyingPawn)) pawn.TakeDamage(dinfo); else if (thing.def.destroyable && thing.def.building != null) thing.TakeDamage(dinfo); } @@ -129,8 +134,10 @@ namespace ArachnaeSwarm try { + // We still need to update the destination for the base flyer logic to work correctly DestCellInfo.SetValue(this, this.exactPosition.ToIntVec3()); - TicksFlightTimeInfo.SetValue(this, ticksFlying + 2); + // --- FIX for infinite flight --- + // The old TicksFlightTimeInfo update logic is removed. } catch (System.Exception ex) { @@ -138,9 +145,9 @@ namespace ArachnaeSwarm } TicksFlyingInfo.SetValue(this, ticksFlying + 1); - - int flightTime = (int)TicksFlightTimeInfo.GetValue(this); - if (!this.exactPosition.ToIntVec3().InBounds(this.Map) || ticksFlying > flightTime * 2) + + // --- RELIABLE TIMEOUT & BOUNDS CHECK --- + if (ticksFlying > this.maxFlightTicks || !this.exactPosition.ToIntVec3().InBounds(this.Map)) { Land(); } diff --git a/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs index 0cb816c..71f2a6f 100644 --- a/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs +++ b/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs @@ -21,11 +21,21 @@ namespace ArachnaeSwarm return false; } + // --- Best Practice: Cache Map and Position FIRST --- + // Per MCP analysis, Caster.Map is the most reliable source. + // Cache this before ANY other logic. + Map map = this.Caster.Map; + if (map == null) + { + Log.Error($"Verb_CastAbilityTrackingCharge: Caster {this.Caster.LabelCap} has a null map. Cannot cast."); + return false; + } + if (this.CasterPawn == null || !this.CasterPawn.Spawned) { return false; } - + // --- This is now a fully custom Thing, so we spawn it directly --- var trackingCharge = (PawnFlyer_TrackingCharge)ThingMaker.MakeThing(props.flyerDef); @@ -37,9 +47,20 @@ namespace ArachnaeSwarm trackingCharge.collisionDamageDef = props.collisionDamageDef; trackingCharge.primaryTarget = this.currentTarget; + // --- Dynamic Timeout Calculation --- + float flightSpeed = props.flyerDef.pawnFlyer.flightSpeed; + if (flightSpeed <= 0) + { + Log.Error("flyerDef has no flightSpeed, cannot calculate timeout."); + return false; + } + // Timeout is twice the time it would take to fly the max range in a straight line + int maxTicks = (int)((this.verbProps.range / flightSpeed) * 2.0f); + trackingCharge.maxFlightTicks = maxTicks; + // Setup and spawn trackingCharge.StartFlight(this.CasterPawn, this.currentTarget.Cell); - GenSpawn.Spawn(trackingCharge, this.CasterPawn.Position, this.CasterPawn.Map); + GenSpawn.Spawn(trackingCharge, this.CasterPawn.Position, map); // Use the cached map return true; }