暂存
This commit is contained in:
Binary file not shown.
@@ -39,10 +39,13 @@
|
||||
<inertiaDistance>6</inertiaDistance>
|
||||
<collisionDamageDef>Blunt</collisionDamageDef>
|
||||
<flyerDef>ARA_Flyer_TrackingCharge</flyerDef>
|
||||
<maxFlightTicks>600</maxFlightTicks> <!-- 7.5 seconds timeout -->
|
||||
<collisionRadius>2.5</collisionRadius> <!-- Larger collision radius -->
|
||||
<impactSound>Pawn_Melee_BigBash_HitPawn</impactSound>
|
||||
</li>
|
||||
<li Class="CompProperties_AbilityEffecterOnCaster">
|
||||
<effecterDef>WarTrumpet</effecterDef>
|
||||
<maintainTicks>20</maintainTicks> <!-- Long enough for the "2nd wave" to spawn -->
|
||||
</li>
|
||||
</comps>
|
||||
</AbilityDef>
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user