diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index e6c7719..e2e49d2 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 fb0e415..035eb3a 100644
--- a/1.6/1.6/Defs/AbilityDefs/Abilities_TrackingCharge.xml
+++ b/1.6/1.6/Defs/AbilityDefs/Abilities_TrackingCharge.xml
@@ -39,10 +39,13 @@
6
Blunt
ARA_Flyer_TrackingCharge
- 600
2.5
Pawn_Melee_BigBash_HitPawn
+
+ WarTrumpet
+ 20
+
diff --git a/Source/ArachnaeSwarm/Abilities/CompProperties_TrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/CompProperties_TrackingCharge.cs
index f770f25..2ab22a9 100644
--- a/Source/ArachnaeSwarm/Abilities/CompProperties_TrackingCharge.cs
+++ b/Source/ArachnaeSwarm/Abilities/CompProperties_TrackingCharge.cs
@@ -11,10 +11,8 @@ namespace ArachnaeSwarm
public float inertiaDistance = 3f;
public DamageDef collisionDamageDef;
public ThingDef flyerDef;
- public int maxFlightTicks = 300; // Default 5 seconds timeout
public float collisionRadius = 1.5f;
public SoundDef impactSound;
-
public CompProperties_TrackingCharge()
{
this.compClass = typeof(CompAbilityEffect_TrackingCharge);
diff --git a/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs
index 2edbe83..197e2be 100644
--- a/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs
+++ b/Source/ArachnaeSwarm/Abilities/PawnFlyer_TrackingCharge.cs
@@ -23,12 +23,9 @@ namespace ArachnaeSwarm
public SoundDef impactSound;
// --- Internal state ---
- private Vector3 currentSpeed;
- private float distanceTraveled = 0f;
private bool homing = true;
- private int inertiaTicks = -1;
- private Vector3 exactPosition;
private bool hasHitPrimaryTarget = false;
+ private Vector3 exactPosition;
// --- Reflection Fields ---
private static FieldInfo TicksFlyingInfo;
@@ -83,88 +80,76 @@ namespace ArachnaeSwarm
protected override void Tick()
{
- int ticksFlying = (int)TicksFlyingInfo.GetValue(this);
+ // --- THE CORRECT APPROACH ---
+ // Let the base class handle all flight mechanics (position, timing, etc.)
+ // We only intervene to do two things:
+ // 1. Continuously update the destination to "steer" the flyer.
+ // 2. Perform our own collision check.
- if (ticksFlying == 0)
+ if (homing && primaryTarget.HasThing && primaryTarget.Thing.Spawned)
{
- Vector3 startVec = (Vector3)StartVecInfo.GetValue(this);
- IntVec3 destCell = (IntVec3)DestCellInfo.GetValue(this);
- Vector3 destinationPos = GenThing.TrueCenter(destCell, Rot4.North, this.FlyingThing.def.size, this.def.Altitude);
- Vector3 direction = (destinationPos - startVec).normalized;
- this.currentSpeed = direction * this.def.pawnFlyer.flightSpeed;
+ // Steer the flyer by constantly updating its destination cell.
+ DestCellInfo.SetValue(this, primaryTarget.Thing.Position);
}
-
- this.exactPosition += this.currentSpeed;
- this.distanceTraveled += this.currentSpeed.magnitude;
- if (inertiaTicks > 0)
+ // Perform our custom collision check.
+ if (!hasHitPrimaryTarget && primaryTarget.HasThing && primaryTarget.Thing.Spawned)
{
- inertiaTicks--;
- if (inertiaTicks <= 0) { Land(); return; }
- }
- else
- {
- if (homing && primaryTarget.HasThing && primaryTarget.Thing.Spawned)
- {
- Vector3 desiredDirection = (primaryTarget.Thing.DrawPos - this.exactPosition).normalized;
- this.currentSpeed = Vector3.RotateTowards(this.currentSpeed, desiredDirection, this.homingSpeed * 0.017f, 999f).normalized * this.def.pawnFlyer.flightSpeed;
- }
- else
- {
- homing = false;
- }
-
- float calculatedDamage = this.initialDamage + (this.distanceTraveled * this.damagePerTile);
- var dinfo = new DamageInfo(this.collisionDamageDef, calculatedDamage, 1f, -1, this.FlyingPawn);
-
- if (!hasHitPrimaryTarget && homing && primaryTarget.HasThing && primaryTarget.Thing.Spawned && (this.exactPosition - primaryTarget.Thing.DrawPos).sqrMagnitude < this.collisionRadius * this.collisionRadius)
+ // Use DrawPos for accurate distance checking, not Position.
+ if ((this.DrawPos - primaryTarget.Thing.DrawPos).sqrMagnitude < this.collisionRadius * this.collisionRadius)
{
+ // --- Impact! ---
if (this.impactSound != null)
{
- SoundStarter.PlayOneShot(this.impactSound, new TargetInfo(this.exactPosition.ToIntVec3(), this.Map));
+ SoundStarter.PlayOneShot(this.impactSound, new TargetInfo(this.Position, this.Map));
}
- hasHitPrimaryTarget = true; // Mark as hit to prevent re-triggering
+
+ // Calculate damage based on distance traveled so far.
+ // We need to get the distance from the base class now.
+ Vector3 startPosition = (Vector3)StartVecInfo.GetValue(this);
+ float distance = (this.DrawPos - startPosition).magnitude;
+ float calculatedDamage = this.initialDamage + (distance * this.damagePerTile);
+ var dinfo = new DamageInfo(this.collisionDamageDef, calculatedDamage, 1f, -1, this.FlyingPawn);
+
primaryTarget.Thing.TakeDamage(dinfo);
- homing = false;
- this.inertiaTicks = (int)(this.inertiaDistance / this.currentSpeed.magnitude);
- }
-
- foreach (var thing in GenRadial.RadialDistinctThingsAround(this.exactPosition.ToIntVec3(), this.Map, 1.0f, false))
- {
- // Avoid damaging self or the primary target (which is handled above)
- if (thing == this.FlyingPawn || thing == this || (hasHitPrimaryTarget && thing == primaryTarget.Thing)) continue;
+ hasHitPrimaryTarget = true;
- 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);
+ // Stop homing. The flyer will now continue to its last set destination.
+ homing = false;
+
+ // To create the "inertia" effect, we now set the destination to be a point
+ // past the target.
+ Vector3 direction = (this.DrawPos - startPosition).normalized;
+ IntVec3 inertiaEndPos = (this.DrawPos + (direction * this.inertiaDistance)).ToIntVec3();
+ DestCellInfo.SetValue(this, inertiaEndPos);
}
}
- try
+ // --- AOE Damage Logic ---
+ // Damage other hostiles in the path.
+ float aoeDamage = this.initialDamage + (((Vector3)StartVecInfo.GetValue(this) - this.DrawPos).magnitude * this.damagePerTile);
+ var aoeDinfo = new DamageInfo(this.collisionDamageDef, aoeDamage, 1f, -1, this.FlyingPawn);
+ foreach (var thing in GenRadial.RadialDistinctThingsAround(this.Position, this.Map, 1.0f, false))
{
- // We still need to update the destination for the base flyer logic to work correctly
- DestCellInfo.SetValue(this, this.exactPosition.ToIntVec3());
- // --- FIX for infinite flight ---
- // The old TicksFlightTimeInfo update logic is removed.
- }
- catch (System.Exception ex)
- {
- Log.ErrorOnce($"Exception during reflection in PawnFlyer_TrackingCharge: {ex}", this.thingIDNumber);
- }
-
- TicksFlyingInfo.SetValue(this, ticksFlying + 1);
-
- // --- RELIABLE TIMEOUT & BOUNDS CHECK ---
- if (ticksFlying > this.maxFlightTicks || !this.exactPosition.ToIntVec3().InBounds(this.Map))
- {
- Land();
+ if (thing != this.FlyingPawn && thing != this && thing != primaryTarget.Thing)
+ {
+ if (thing is Pawn pawn && !pawn.Downed && pawn.HostileTo(this.FlyingPawn))
+ {
+ pawn.TakeDamage(aoeDinfo);
+ }
+ }
}
+
+ // Let the base class do its thing. This is crucial.
+ // It will handle the movement, timing, and eventual landing based on its calculated ticksFlightTime.
+ base.Tick();
}
- private void Land()
+ protected override void RespawnPawn()
{
- if (this.Destroyed) return;
+ // This is the correct place to call the base method.
+ // The base class's TickInterval will call this method before destroying the flyer.
base.RespawnPawn();
- this.Destroy();
}
}
}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs b/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs
index 2c07dce..e69a855 100644
--- a/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs
+++ b/Source/ArachnaeSwarm/Abilities/Verb_CastAbilityTrackingCharge.cs
@@ -46,7 +46,6 @@ namespace ArachnaeSwarm
trackingCharge.inertiaDistance = props.inertiaDistance;
trackingCharge.collisionDamageDef = props.collisionDamageDef;
trackingCharge.primaryTarget = this.currentTarget;
- trackingCharge.maxFlightTicks = props.maxFlightTicks;
trackingCharge.collisionRadius = props.collisionRadius;
trackingCharge.impactSound = props.impactSound;
@@ -54,6 +53,13 @@ namespace ArachnaeSwarm
trackingCharge.StartFlight(this.CasterPawn, this.currentTarget.Cell);
GenSpawn.Spawn(trackingCharge, this.CasterPawn.Position, map); // Use the cached map
+ // --- FIX for Comp Effects ---
+ // --- The Standard Pattern to trigger Comps like EffecterOnCaster ---
+ // After our custom verb logic (spawning the flyer) is done,
+ // we call the ability's Activate method with invalid targets.
+ // This triggers the standard Comp cycle without re-casting the verb.
+ this.ability.Activate(LocalTargetInfo.Invalid, LocalTargetInfo.Invalid);
+
return true;
}
}