Example #1
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, false, false, 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);
            }
        }
Example #2
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,
                                                                             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, 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)
            {
                shouldExplode |= AnyValidTargetsInRadius(world, pos, info.Width, 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;
                Game.Sound.Play(SoundType.World, info.BounceSound, source);
                remainingBounces--;
            }

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

            // Driving into cell with higher height level
            shouldExplode |= world.Map.DistanceAboveTerrain(pos).Length < 0;

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

            if (shouldExplode)
            {
                Explode(world);
            }
        }
Example #3
0
        public void Tick(World world)
        {
            ticks++;
            anim.Tick();

            // Missile tracks target
            if (args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                targetPosition = args.GuidedTarget.CenterPosition;
            }

            var dist            = targetPosition + offset - pos;
            var desiredFacing   = Traits.Util.GetFacing(dist, facing);
            var desiredAltitude = targetPosition.Z;
            var jammed          = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(j => JammedBy(j));

            if (jammed)
            {
                desiredFacing   = facing + world.SharedRandom.Next(-20, 21);
                desiredAltitude = world.SharedRandom.Next(-43, 86);
            }
            else if (!args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                desiredFacing = facing;
            }

            facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT);
            var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * speed / 1024;

            if (targetPosition.Z > 0 && info.TurboBoost)
            {
                move = (move * 3) / 2;
            }

            if (pos.Z != desiredAltitude)
            {
                var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024;
                var dz    = (targetPosition.Z - pos.Z).Clamp(-delta, delta);
                move += new WVec(0, 0, dz);
            }

            pos += move;

            if (info.Trail != null && --ticksToNextSmoke < 0)
            {
                world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail)));
                ticksToNextSmoke = info.TrailInterval;
            }

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

            var shouldExplode = (pos.Z < 0) ||          // Hit the ground
                                (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) || // Within range
                                (info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel
                                (!info.High && world.ActorMap.GetUnitsAt(pos.ToCPos())
                                 .Any(a => a.HasTrait <IBlocksBullets>()));       // Hit a wall

            if (shouldExplode)
            {
                Explode(world);
            }
        }
Example #4
0
        bool ShouldExplode(World world)
        {
            // Check for walls or other blocking obstacles
            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, args.SourceActor.Owner, 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);
        }
Example #5
0
        public void Tick(World world)
        {
            ticks++;
            if (anim != null)
            {
                anim.Tick();
            }

            // Missile tracks target
            if (args.GuidedTarget.IsValidFor(args.SourceActor) && lockOn)
            {
                targetPosition = args.GuidedTarget.CenterPosition;
            }

            var dist            = targetPosition + offset - pos;
            var desiredFacing   = OpenRA.Traits.Util.GetFacing(dist, facing);
            var desiredAltitude = targetPosition.Z;
            var jammed          = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(JammedBy);

            if (jammed)
            {
                desiredFacing   = facing + world.SharedRandom.Next(-20, 21);
                desiredAltitude = world.SharedRandom.Next(-43, 86);
            }
            else if (!args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                desiredFacing = facing;
            }

            facing = OpenRA.Traits.Util.TickFacing(facing, desiredFacing, info.RateOfTurn);
            var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * info.Speed.Length / 1024;

            if (pos.Z != desiredAltitude)
            {
                var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024;
                var dz    = (targetPosition.Z - pos.Z).Clamp(-delta, delta);
                move += new WVec(0, 0, dz);
            }

            pos += move;

            if (!string.IsNullOrEmpty(info.Trail) && --ticksToNextSmoke < 0)
            {
                world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail, trailPalette, info.Sequence)));
                ticksToNextSmoke = info.TrailInterval;
            }

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

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

            var shouldExplode = (pos.Z < 0) ||          // Hit the ground
                                (dist.LengthSquared < info.CloseEnough.LengthSquared) || // Within range
                                (info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel
                                (info.Blockable && world.ActorMap.GetUnitsAt(cell).Any(a => a.HasTrait <IBlocksProjectiles>())) || // 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

            if (shouldExplode)
            {
                Explode(world);
            }
        }
Example #6
0
        public void Tick(Actor self)
        {
            var local = info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation));

            trail.Update(self.CenterPosition + body.LocalToWorld(local));
        }