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; } }