示例#1
0
        public void Tick(World world)
        {
            // Check for blocking actors
            WPos blockedPos;

            if (info.Blockable)
            {
                // If GuidedTarget has become invalid due to getting killed the same tick,
                // we need to set target to args.PassiveTarget to prevent target.CenterPosition below from crashing.
                // The warheads have target validity checks themselves so they don't need this, but AnyBlockingActorsBetween does.
                if (target.Type == TargetType.Invalid)
                {
                    target = Target.FromPos(args.PassiveTarget);
                }

                if (BlocksProjectiles.AnyBlockingActorsBetween(world, args.Source, target.CenterPosition,
                                                               info.Width, out blockedPos))
                {
                    target = Target.FromPos(blockedPos);
                }
            }

            args.Weapon.Impact(target, new WarheadArgs(args));
            world.AddFrameEndTask(w => w.Remove(this));
        }
示例#2
0
        public void Tick(World world)
        {
            if (anim != null)
            {
                anim.Tick();
            }

            pos = WPos.LerpQuadratic(args.Source, target, angle, ticks, length);

            if (!string.IsNullOrEmpty(info.Trail) && --smokeTicks < 0)
            {
                var delayedPos = WPos.LerpQuadratic(args.Source, target, angle, ticks - info.TrailDelay, length);
                world.AddFrameEndTask(w => w.Add(new Smoke(w, delayedPos, info.Trail, trailPalette, info.Sequences.Random(world.SharedRandom))));
                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            var shouldExplode = ticks++ >= length ||          // Flight length reached/exceeded
                                (info.Blockable && BlocksProjectiles.AnyBlockingActorAt(world, pos));    // Hit a wall or other blocking obstacle

            if (shouldExplode)
            {
                Explode(world);
            }
        }
示例#3
0
        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));
            }
        }
示例#4
0
        public void Tick(World world)
        {
            //Check for blocking actors.
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, source, target.CenterPosition, info.Width, info.BlockerScanRadius, out blockedPos))
            {
                target = Target.FromPos(blockedPos);
            }

            args.Weapon.Impact(target, args.SourceActor, args.DamagedModifiers);
            world.AddFrameEndTask(w => w.Remove(this));
        }
示例#5
0
        void CalculateVectors()
        {
            // Check for blocking actors
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(args.SourceActor.World, target, args.Source,
                                                                             info.BeamWidth, out blockedPos))
            {
                target = blockedPos;
            }

            // Note: WAngle.Sin(x) = 1024 * Math.Sin(2pi/1024 * x)
            AngleStep = new WAngle(1024 / info.QuantizationCount);

            SourceToTarget = target - args.Source;

            // Forward step, pointing from src to target.
            // QuantizationCont * forwardStep == One cycle of beam in src2target direction.
            ForwardStep = (info.HelixPitch.Length * SourceToTarget) / (info.QuantizationCount * SourceToTarget.Length);

            // An easy vector to find which is perpendicular vector to forwardStep, with 0 Z component
            LeftVector = new WVec(ForwardStep.Y, -ForwardStep.X, 0);
            if (LeftVector.LengthSquared != 0)
            {
                LeftVector = 1024 * LeftVector / LeftVector.Length;
            }

            // Vector that is pointing upwards from the ground
            UpVector = new WVec(
                -ForwardStep.X * ForwardStep.Z,
                -ForwardStep.Z * ForwardStep.Y,
                ForwardStep.X * ForwardStep.X + ForwardStep.Y * ForwardStep.Y);

            if (UpVector.LengthSquared != 0)
            {
                UpVector = 1024 * UpVector / UpVector.Length;
            }

            //// LeftVector and UpVector are unit vectors of size 1024.

            CycleCount = SourceToTarget.Length / info.HelixPitch.Length;
            if (SourceToTarget.Length % info.HelixPitch.Length != 0)
            {
                CycleCount += 1;                 // math.ceil, int version.
            }
            // Using ForwardStep * CycleCount, the helix and the main beam gets "out of sync"
            // if drawn from source to target. Instead, the main beam is drawn from source to end point of helix.
            // Trade-off between computation vs Railgun weapon range.
            // Modders must not have too large range for railgun weapons.
            SourceToTarget = info.QuantizationCount * CycleCount * ForwardStep;
        }
示例#6
0
        void CalculateVectors()
        {
            // Check for blocking actors
            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(args.SourceActor.World, args.SourceActor.Owner, target, args.Source,
                                                                             info.LineWidth, out var blockedPos))
            {
                target = blockedPos;
            }

            // Note: WAngle.Sin(x) = 1024 * Math.Sin(2pi/1024 * x)
            angleStep = new WAngle(1024 / info.QuantizationCount);

            var sourceToTarget = target - args.Source;

            // Forward step, pointing from src to target.
            // QuantizationCont * forwardStep == One cycle of beam in src2target direction.
            forwardStep = (info.HelixPitch.Length * sourceToTarget) / (info.QuantizationCount * sourceToTarget.Length);

            if (forwardStep == WVec.Zero)
            {
                return;
            }

            // An easy vector to find which is perpendicular vector to forwardStep, with 0 Z component
            leftVector = new WVec(forwardStep.Y, -forwardStep.X, 0);
            if (leftVector.Length != 0)
            {
                leftVector = 1024 * leftVector / leftVector.Length;
            }

            // Vector that is pointing upwards from the ground
            upVector = leftVector.Length != 0
                                        ? new WVec(
                -forwardStep.X * forwardStep.Z,
                -forwardStep.Z * forwardStep.Y,
                forwardStep.X * forwardStep.X + forwardStep.Y * forwardStep.Y)
                                        : new WVec(forwardStep.Z, forwardStep.Z, 0);
            if (upVector.Length != 0)
            {
                upVector = 1024 * upVector / upVector.Length;
            }

            //// LeftVector and UpVector are unit vectors of size 1024.

            cycleCount = sourceToTarget.Length / info.HelixPitch.Length;
            if (sourceToTarget.Length % info.HelixPitch.Length != 0)
            {
                cycleCount += 1;                 // math.ceil, int version.
            }
        }
示例#7
0
        public void Tick(World world)
        {
            ticks++;
            if (anim != null)
            {
                anim.Tick();
            }

            var lastPos = projectilepos;

            projectilepos = WPos.Lerp(source, targetpos, ticks, estimatedlifespan);

            // Check for walls or other blocking obstacles.
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, projectilepos, info.Width, out blockedPos))
            {
                projectilepos = blockedPos;
                DetonateSelf  = true;
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.Lerp(source, targetpos, ticks - info.TrailDelay, estimatedlifespan);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, false, false, GetEffectiveFacing())));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(projectilepos);
            }

            var flightLengthReached = ticks >= lifespan;

            if (flightLengthReached)
            {
                DetonateSelf = true;
            }

            // Driving into cell with higher height level
            DetonateSelf |= world.Map.DistanceAboveTerrain(projectilepos) < info.ExplodeUnderThisAltitude;

            if (DetonateSelf)
            {
                Explode(world);
            }
        }
示例#8
0
        public void Tick(World world)
        {
            // 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 (!doneDamage)
            {
                if (hitanim != null)
                {
                    hitanim.PlayThen(info.HitAnimSequence, () => animationComplete = true);
                }
                else
                {
                    animationComplete = true;
                }

                var warheadArgs = new WarheadArgs(args)
                {
                    ImpactOrientation = new WRot(WAngle.Zero, Common.Util.GetVerticalAngle(source, target), args.CurrentMuzzleFacing()),
                    ImpactPosition    = target,
                };

                args.Weapon.Impact(Target.FromPos(target), warheadArgs);
                doneDamage = true;
            }

            if (hitanim != null)
            {
                hitanim.Tick();
            }

            if (++ticks >= info.Duration && animationComplete)
            {
                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));
            }
        }
示例#10
0
        public void Tick(World world)
        {
            if (anim != null)
            {
                anim.Tick();
            }

            var lastPos = pos;

            pos = WPos.LerpQuadratic(args.Source, target, angle, ticks, length);

            // Check for walls or other blocking obstacles
            var  shouldExplode = false;
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width,
                                                                             info.TargetExtraSearchRadius, out blockedPos))
            {
                pos           = blockedPos;
                shouldExplode = true;
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.LerpQuadratic(args.Source, target, angle, ticks - info.TrailDelay, length);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, false, false, GetEffectiveFacing())));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            // Flight length reached / exceeded
            shouldExplode |= ticks++ >= length;

            if (shouldExplode)
            {
                Explode(world);
            }
        }
示例#11
0
        public void Tick(World world)
        {
            // Beam tracks target
            if (info.TrackTarget && args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                target = args.GuidedTarget.CenterPosition;
            }

            // Check for blocking actors
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, source, target,
                                                                             info.Width, info.TargetExtraSearchRadius, out blockedPos))
            {
                target = blockedPos;
            }

            if (!doneDamage)
            {
                if (hitanim != null)
                {
                    hitanim.PlayThen(info.HitAnimSequence, () => animationComplete = true);
                }
                else
                {
                    animationComplete = true;
                }

                args.Weapon.Impact(Target.FromPos(target), args.SourceActor, args.DamageModifiers);
                doneDamage = true;
            }

            if (hitanim != null)
            {
                hitanim.Tick();
            }

            if (++ticks >= info.Duration && animationComplete)
            {
                world.AddFrameEndTask(w => w.Remove(this));
            }
        }
示例#12
0
        public void Tick(World world)
        {
            // If GuidedTarget has become invalid due to getting killed the same tick,
            // we need to set target to args.PassiveTarget to prevent target.CenterPosition below from crashing.
            if (target.Type == TargetType.Invalid)
            {
                target = Target.FromPos(args.PassiveTarget);
            }

            // Check for blocking actors
            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, args.SourceActor.Owner, args.Source, target.CenterPosition, info.Width, out var blockedPos))
            {
                target = Target.FromPos(blockedPos);
            }

            var warheadArgs = new WarheadArgs(args)
            {
                ImpactOrientation = new WRot(WAngle.Zero, Util.GetVerticalAngle(args.Source, target.CenterPosition), args.Facing),
                ImpactPosition    = target.CenterPosition,
            };

            args.Weapon.Impact(target, warheadArgs);
            world.AddFrameEndTask(w => w.Remove(this));
        }
示例#13
0
        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));
            }
        }
示例#14
0
        bool ShouldExplode(World world)
        {
            // Check for walls or other blocking obstacles
            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width, out var blockedPos))
            {
                pos = blockedPos;
                return(true);
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.LerpQuadratic(source, target, angle, ticks - info.TrailDelay, length);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, GetEffectiveFacing(), w,
                                                                  info.TrailImage, info.TrailSequences.Random(world.SharedRandom), trailPalette)));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            var flightLengthReached = ticks++ >= length;
            var shouldBounce        = remainingBounces > 0;

            if (flightLengthReached && shouldBounce)
            {
                var cell = world.Map.CellContaining(pos);
                if (!world.Map.Contains(cell))
                {
                    return(true);
                }

                if (info.InvalidBounceTerrain.Contains(world.Map.GetTerrainInfo(cell).Type))
                {
                    return(true);
                }

                if (AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true))
                {
                    return(true);
                }

                target += (pos - source) * info.BounceRangeModifier / 100;
                var dat = world.Map.DistanceAboveTerrain(target);
                target += new WVec(0, 0, -dat.Length);
                length  = Math.Max((target - pos).Length / speed.Length, 1);

                ticks  = 0;
                source = pos;
                Game.Sound.Play(SoundType.World, info.BounceSound, source);
                remainingBounces--;
            }

            // Flight length reached / exceeded
            if (flightLengthReached && !shouldBounce)
            {
                return(true);
            }

            // Driving into cell with higher height level
            if (world.Map.DistanceAboveTerrain(pos).Length < 0)
            {
                return(true);
            }

            // After first bounce, check for targets each tick
            if (remainingBounces < info.BounceCount && AnyValidTargetsInRadius(world, pos, info.Width, args.SourceActor, true))
            {
                return(true);
            }

            if (!string.IsNullOrEmpty(info.PointDefenseType) && world.ActorsWithTrait <IPointDefense>().Any(x => x.Trait.Destroy(pos, args.SourceActor.Owner, info.PointDefenseType)))
            {
                return(true);
            }

            return(false);
        }
示例#15
0
        public void Tick(World world)
        {
            if (anim != null)
            {
                anim.Tick();
            }

            var lastPos = pos;

            pos = WPos.LerpQuadratic(source, target, angle, ticks, length);

            // Check for walls or other blocking obstacles
            var  shouldExplode = false;
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width,
                                                                             info.TargetExtraSearchRadius, out blockedPos))
            {
                pos           = blockedPos;
                shouldExplode = true;
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.LerpQuadratic(source, target, angle, ticks - info.TrailDelay, length);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, false, false, GetEffectiveFacing())));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            var flightLengthReached = ticks++ >= length;
            var shouldBounce        = remainingBounces > 0;

            if (flightLengthReached && shouldBounce)
            {
                shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width + info.TargetExtraSearchRadius, args.SourceActor, true);
                target        += (pos - source) * info.BounceRangeModifier / 100;
                var dat = world.Map.DistanceAboveTerrain(target);
                target += new WVec(0, 0, -dat.Length);
                length  = Math.Max((target - pos).Length / speed.Length, 1);
                ticks   = 0;
                source  = pos;
                remainingBounces--;
            }

            // Flight length reached / exceeded
            shouldExplode |= flightLengthReached && !shouldBounce;

            // Driving into cell with different height level
            shouldExplode |= world.Map.DistanceAboveTerrain(pos) < info.ExplodeUnderThisAltitude;

            // After first bounce, check for targets each tick
            if (remainingBounces < info.BounceCount)
            {
                shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width + info.TargetExtraSearchRadius, args.SourceActor, true);
            }

            if (shouldExplode)
            {
                Explode(world);
            }
        }
示例#16
0
        public void Tick(World world)
        {
            ticks++;
            if (anim != null)
            {
                anim.Tick();
            }

            var lastPos = projectilepos;

            var dx     = projectilepos.X - source.X;
            var dy     = projectilepos.Y - source.Y;
            var normal = new WVec(dy, -dx, 0);

            targetpos = projectilepos;
            var originalVec = projectilepos - source;

            if (dx != 0 || dy != 0)
            {
                int circSpeed = 1 + (ticks * 3);
                int maxSpeed  = 200;
                if (circSpeed > maxSpeed)
                {
                    circSpeed = maxSpeed;
                }

                normal     = WVec.Lerp(WVec.Zero, normal, circSpeed, normal.Length);
                targetpos -= normal;
                targetpos += WVec.Lerp(WVec.Zero, originalVec, 30, originalVec.Length);
            }
            else
            {
                targetpos += args.VecNormalized;
            }

            projectilepos = targetpos;

            if (ticks > 90)
            {
                DetonateSelf = true;
            }

            // Check for walls or other blocking obstacles.
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, projectilepos, info.Width, out blockedPos))
            {
                projectilepos = blockedPos;
                DetonateSelf  = true;
            }

            if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0)
            {
                var delayedPos = WPos.Lerp(lastPos, targetpos, ticks - info.TrailDelay, estimatedlifespan);
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, false, GetEffectiveFacing().Angle)));

                smokeTicks = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(projectilepos);
            }

            var flightLengthReached = ticks >= lifespan;

            if (flightLengthReached)
            {
                DetonateSelf = true;
            }

            // Driving into cell with higher height level
            DetonateSelf |= world.Map.DistanceAboveTerrain(projectilepos) < info.ExplodeUnderThisAltitude;

            if (DetonateSelf)
            {
                Explode(world);
            }
        }
示例#17
0
        public void Tick(World world)
        {
            ticks++;
            if (anim != null)
            {
                anim.Tick();
            }

            // Switch from freefall mode to homing mode
            if (ticks == info.HomingActivationDelay + 1)
            {
                state = States.Homing;
                speed = velocity.Length;

                // Compute the vertical loop radius
                loopRadius = LoopRadius(speed, info.VerticalRateOfTurn);
            }

            // Switch from homing mode to freefall mode
            if (info.RangeLimit != 0 && ticks == info.RangeLimit + 1)
            {
                state    = States.Freefall;
                velocity = new WVec(0, -speed, 0)
                           .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero))
                           .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing)));
            }

            // Check if target position should be updated (actor visible & locked on)
            var newTarPos = targetPosition;

            if (args.GuidedTarget.IsValidFor(args.SourceActor) && lockOn)
            {
                newTarPos = args.GuidedTarget.CenterPosition
                            + new WVec(WDist.Zero, WDist.Zero, info.AirburstAltitude);
            }

            // Compute target's predicted velocity vector (assuming uniform circular motion)
            var fac1 = OpenRA.Traits.Util.GetFacing(tarVel, hFacing);

            tarVel = newTarPos - targetPosition;
            var fac2 = OpenRA.Traits.Util.GetFacing(tarVel, hFacing);

            predVel        = tarVel.Rotate(WRot.FromFacing(fac2 - fac1));
            targetPosition = newTarPos;

            // Compute current distance from target position
            var tarDistVec    = targetPosition + offset - pos;
            var relTarDist    = tarDistVec.Length;
            var relTarHorDist = tarDistVec.HorizontalLength;

            WVec move;

            if (state == States.Freefall)
            {
                move = FreefallTick();
            }
            else
            {
                move = HomingTick(world, tarDistVec, relTarHorDist);
            }

            renderFacing = WAngle.ArcTan(move.Z - move.Y, move.X).Angle / 4 - 64;

            // Move the missile
            pos += move;

            // Create the smoke trail effect
            if (!string.IsNullOrEmpty(info.TrailImage) && --ticksToNextSmoke < 0 && (state != States.Freefall || info.TrailWhenDeactivated))
            {
                world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.TrailImage, trailPalette, info.TrailSequence)));
                ticksToNextSmoke = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            var cell = world.Map.CellContaining(pos);

            // NOTE: High speeds might cause the missile to miss the target or fly through obstacles
            //       In that case, big moves should probably be decomposed into multiple smaller ones with hit checks
            var height        = world.Map.DistanceAboveTerrain(pos);
            var shouldExplode = (height.Length < 0) ||          // Hit the ground
                                (relTarDist < info.CloseEnough.Length) || // Within range
                                (info.ExplodeWhenEmpty && info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel
                                (info.Blockable && BlocksProjectiles.AnyBlockingActorAt(world, pos)) || // Hit a wall or other blocking obstacle
                                !world.Map.Contains(cell) || // This also avoids an IndexOutOfRangeException in GetTerrainInfo below.
                                (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType) || // Hit incompatible terrain
                                (height.Length < info.AirburstAltitude.Length && relTarHorDist < info.CloseEnough.Length);    // Airburst

            if (shouldExplode)
            {
                Explode(world);
            }
        }
示例#18
0
        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));
            }
        }
示例#19
0
        public void Tick(World world)
        {
            ticks++;
            if (anim != null)
            {
                anim.Tick();
            }

            // Switch from freefall mode to homing mode
            if (ticks == info.HomingActivationDelay + 1)
            {
                state = States.Homing;
                speed = velocity.Length;

                // Compute the vertical loop radius
                loopRadius = LoopRadius(speed, info.VerticalRateOfTurn);
            }

            // Switch from homing mode to freefall mode
            if (rangeLimit >= WDist.Zero && distanceCovered > rangeLimit)
            {
                state    = States.Freefall;
                velocity = new WVec(0, -speed, 0)
                           .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero))
                           .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing)));
            }

            // Check if target position should be updated (actor visible & locked on)
            var newTarPos = targetPosition;

            if (args.GuidedTarget.IsValidFor(args.SourceActor) && lockOn)
            {
                newTarPos = (args.Weapon.TargetActorCenter ? args.GuidedTarget.CenterPosition : args.GuidedTarget.Positions.PositionClosestTo(args.Source))
                            + new WVec(WDist.Zero, WDist.Zero, info.AirburstAltitude);
            }

            // Compute target's predicted velocity vector (assuming uniform circular motion)
            var yaw1 = tarVel.HorizontalLengthSquared != 0 ? tarVel.Yaw : WAngle.FromFacing(hFacing);

            tarVel = newTarPos - targetPosition;
            var yaw2 = tarVel.HorizontalLengthSquared != 0 ? tarVel.Yaw : WAngle.FromFacing(hFacing);

            predVel        = tarVel.Rotate(WRot.FromYaw(yaw2 - yaw1));
            targetPosition = newTarPos;

            // Compute current distance from target position
            var tarDistVec    = targetPosition + offset - pos;
            var relTarDist    = tarDistVec.Length;
            var relTarHorDist = tarDistVec.HorizontalLength;

            WVec move;

            if (state == States.Freefall)
            {
                move = FreefallTick();
            }
            else
            {
                move = HomingTick(world, tarDistVec, relTarHorDist);
            }

            renderFacing = new WVec(move.X, move.Y - move.Z, 0).Yaw.Facing;

            // Move the missile
            var lastPos = pos;

            if (info.AllowSnapping && state != States.Freefall && relTarDist < move.Length)
            {
                pos = targetPosition + offset;
            }
            else
            {
                pos += move;
            }

            // Check for walls or other blocking obstacles
            var  shouldExplode = false;
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width,
                                                                             out blockedPos))
            {
                pos           = blockedPos;
                shouldExplode = true;
            }

            // Create the sprite trail effect
            if (!string.IsNullOrEmpty(info.TrailImage) && --ticksToNextSmoke < 0 && (state != States.Freefall || info.TrailWhenDeactivated))
            {
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(pos - 3 * move / 2, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, facing: renderFacing)));

                ticksToNextSmoke = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            distanceCovered += new WDist(speed);
            var cell   = world.Map.CellContaining(pos);
            var height = world.Map.DistanceAboveTerrain(pos);

            shouldExplode |= height.Length < 0 ||          // Hit the ground
                             relTarDist < info.CloseEnough.Length ||    // Within range
                             (info.ExplodeWhenEmpty && rangeLimit >= WDist.Zero && distanceCovered > rangeLimit) ||    // Ran out of fuel
                             !world.Map.Contains(cell) ||    // This also avoids an IndexOutOfRangeException in GetTerrainInfo below.
                             (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType) ||    // Hit incompatible terrain
                             (height.Length < info.AirburstAltitude.Length && relTarHorDist < info.CloseEnough.Length);       // Airburst

            if (shouldExplode)
            {
                Explode(world);
            }
        }