示例#1
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);
            }
        }
示例#2
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);
            }
        }