public void Tick(World world) { source = args.CurrentSource(); if (hasLaunchEffect && ticks == 0) { world.AddFrameEndTask(w => w.Add(new SpriteEffect(args.CurrentSource, args.CurrentMuzzleFacing, world, info.LaunchEffectImage, info.LaunchEffectSequence, info.LaunchEffectPalette))); } // Beam tracks target if (info.TrackTarget && args.GuidedTarget.IsValidFor(args.SourceActor)) { target = args.Weapon.TargetActorCenter ? args.GuidedTarget.CenterPosition : args.GuidedTarget.Positions.PositionClosestTo(source); } // Check for blocking actors WPos blockedPos; if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, source, target, info.Width, out blockedPos)) { target = blockedPos; } if (ticks < info.DamageDuration && --interval <= 0) { var warheadArgs = new WarheadArgs(args) { ImpactOrientation = new WRot(WAngle.Zero, Util.GetVerticalAngle(source, target), args.CurrentMuzzleFacing()), ImpactPosition = target, }; args.Weapon.Impact(Target.FromPos(target), warheadArgs); interval = info.DamageInterval; } if (showHitAnim) { if (ticks == 0) { hitanim.PlayThen(info.HitAnimSequence, () => showHitAnim = false); } hitanim.Tick(); } if (++ticks >= info.Duration && !showHitAnim) { world.AddFrameEndTask(w => w.Remove(this)); } }
public void Tick(World world) { source = args.CurrentSource(); if (hasLaunchEffect && ticks == 0) { world.AddFrameEndTask(w => w.Add(new LaunchEffect(world, args.CurrentSource, () => 0, info.LaunchEffectImage, info.LaunchEffectSequence, info.LaunchEffectPalette))); } // Beam tracks target if (info.TrackTarget && args.GuidedTarget.IsValidFor(args.SourceActor)) { target = args.Weapon.TargetActorCenter ? args.GuidedTarget.CenterPosition : args.GuidedTarget.Positions.PositionClosestTo(source); } // Check for blocking actors WPos blockedPos; if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, source, target, info.Width, out blockedPos)) { target = blockedPos; } if (ticks < info.DamageDuration && --interval <= 0) { args.Weapon.Impact(Target.FromPos(target), args.SourceActor, args.DamageModifiers); interval = info.DamageInterval; } if (showHitAnim) { if (ticks == 0) { hitanim.PlayThen(info.HitAnimSequence, () => showHitAnim = false); } hitanim.Tick(); } if (++ticks >= info.Duration && !showHitAnim) { world.AddFrameEndTask(w => w.Remove(this)); } }
public void Tick(World world) { if (info.TrackTarget) { TrackTarget(); } if (++headTicks >= length) { headPos = target; isHeadTravelling = false; } else if (isHeadTravelling) { headPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, headTicks, length); } if (tailTicks <= 0 && args.SourceActor.IsInWorld && !args.SourceActor.IsDead) { args.Source = args.CurrentSource(); tailPos = args.Source; } // Allow for leniency to avoid edge case stuttering (start firing and immediately stop again). var outOfWeaponRange = args.Weapon.Range + info.BeyondTargetRange < new WDist((args.PassiveTarget - args.Source).Length); // While the head is travelling, the tail must start to follow Duration ticks later. // Alternatively, also stop emitting the beam if source actor dies or is ordered to stop. if ((headTicks >= info.Duration && !isTailTravelling) || args.SourceActor.IsDead || !actorAttackBase.IsAttacking || outOfWeaponRange) { StopTargeting(); } if (isTailTravelling) { if (++tailTicks >= length) { tailPos = target; isTailTravelling = false; } else { tailPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, tailTicks, length); } } // Check for blocking actors WPos blockedPos; if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, tailPos, headPos, info.Width, info.TargetExtraSearchRadius, out blockedPos)) { headPos = blockedPos; target = headPos; length = Math.Min(headTicks, length); } // Damage is applied to intersected actors every DamageInterval ticks if (headTicks % info.DamageInterval == 0) { var actors = world.FindActorsOnLine(tailPos, headPos, info.Width, info.TargetExtraSearchRadius); foreach (var a in actors) { var adjustedModifiers = args.DamageModifiers.Append(GetFalloff((args.Source - a.CenterPosition).Length)); args.Weapon.Impact(Target.FromActor(a), args.SourceActor, adjustedModifiers); } } if (IsBeamComplete) { world.AddFrameEndTask(w => w.Remove(this)); } }
public void Tick(World world) { if (info.TrackTarget) { TrackTarget(); } if (++headTicks >= length) { headPos = target; isHeadTravelling = false; } else if (isHeadTravelling) { headPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, headTicks, length); } if (tailTicks <= 0 && args.SourceActor.IsInWorld && !args.SourceActor.IsDead) { args.Source = args.CurrentSource(); tailPos = args.Source; } // Allow for leniency to avoid edge case stuttering (start firing and immediately stop again). var outOfWeaponRange = args.Weapon.Range + info.BeyondTargetRange < new WDist((args.PassiveTarget - args.Source).Length); // While the head is travelling, the tail must start to follow Duration ticks later. // Alternatively, also stop emitting the beam if source actor dies or is ordered to stop. if ((headTicks >= info.Duration && !isTailTravelling) || args.SourceActor.IsDead || !actorAttackBase.IsAiming || outOfWeaponRange) { StopTargeting(); } if (isTailTravelling) { if (++tailTicks >= length) { tailPos = target; isTailTravelling = false; } else { tailPos = WPos.LerpQuadratic(args.Source, target, WAngle.Zero, tailTicks, length); } } // Check for blocking actors if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, tailPos, headPos, info.Width, out var blockedPos)) { headPos = blockedPos; target = headPos; length = Math.Min(headTicks, length); } // Damage is applied to intersected actors every DamageInterval ticks if (headTicks % info.DamageInterval == 0) { var actors = world.FindActorsOnLine(tailPos, headPos, info.Width); foreach (var a in actors) { var adjustedModifiers = args.DamageModifiers.Append(GetFalloff((args.Source - a.CenterPosition).Length)); var warheadArgs = new WarheadArgs(args) { ImpactOrientation = new WRot(WAngle.Zero, Util.GetVerticalAngle(args.Source, target), args.CurrentMuzzleFacing()), // Calculating an impact position is bogus for line damage. // FindActorsOnLine guarantees that the beam touches the target's HitShape, // so we just assume a center hit to avoid bogus warhead recalculations. ImpactPosition = a.CenterPosition, DamageModifiers = adjustedModifiers.ToArray(), }; args.Weapon.Impact(Target.FromActor(a), warheadArgs); } } if (IsBeamComplete) { world.AddFrameEndTask(w => w.Remove(this)); } }