Exemple #1
0
        public void Tick(Actor self)
        {
            var bombHeight       = self.World.Map.DistanceAboveTerrain(self.CenterPosition);
            var bombTarget       = Target.FromPos(self.CenterPosition - new WVec(WDist.Zero, WDist.Zero, bombHeight));
            var wasInAttackRange = inAttackRange;
            var wasFacingTarget  = facingTarget;

            inAttackRange = false;

            var f              = facing.Value.Facing;
            var delta          = target.CenterPosition - self.CenterPosition;
            var facingToTarget = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : f;

            facingTarget = Math.Abs(facingToTarget - f) % 256 <= info.FacingTolerance;

            // Bombs drop anywhere in range
            foreach (var a in Armaments.Where(a => a.Info.Name == info.Bombs))
            {
                if (!target.IsInRange(self.CenterPosition, a.MaxRange()))
                {
                    continue;
                }

                inAttackRange = true;
                a.CheckFire(self, facing.Value, bombTarget);
            }

            // Guns only fire when approaching the target
            if (facingTarget)
            {
                foreach (var a in Armaments.Where(a => a.Info.Name == info.Guns))
                {
                    if (!target.IsInRange(self.CenterPosition, a.MaxRange()))
                    {
                        continue;
                    }

                    inAttackRange = true;

                    var gunPos    = self.CenterPosition - new WVec(0, a.MaxRange().Length / 2, 0).Rotate(WRot.FromFacing(f));
                    var gunHeight = self.World.Map.DistanceAboveTerrain(gunPos);
                    a.CheckFire(self, facing.Value, Target.FromPos(gunPos - new WVec(WDist.Zero, WDist.Zero, gunHeight)));
                }
            }

            // Actors without armaments may want to trigger an action when it passes the target
            if (!Armaments.Any())
            {
                inAttackRange = !wasInAttackRange && !facingTarget && wasFacingTarget;
            }

            if (inAttackRange && !wasInAttackRange)
            {
                OnEnteredAttackRange(self);
            }

            if (!inAttackRange && wasInAttackRange)
            {
                OnExitedAttackRange(self);
            }
        }
Exemple #2
0
        public WVec FlyStep(int speed, int facing)
        {
            var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing));

            return(speed * dir / 1024);
        }
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivity(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (IsCanceling || carryall.State != Carryall.CarryallState.Carrying || carryall.Carryable.IsDead)
            {
                return(NextActivity);
            }

            // Drop the actor at the current position
            if (destination.Type == TargetType.Invalid)
            {
                destination = Target.FromCell(self.World, self.Location);
            }

            var target = FindDropLocation(destination, carryall.Info.DropRange);

            // Can't land, so wait at the target until something changes
            if (target.Type == TargetType.Invalid)
            {
                QueueChild(self, new Fly(self, destination), true);
                QueueChild(self, new Wait(25));
                return(this);
            }

            // Move to drop-off location
            var localOffset       = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation));
            var carryablePosition = self.CenterPosition + body.LocalToWorld(localOffset);

            if ((carryablePosition - target.CenterPosition).HorizontalLengthSquared != 0)
            {
                // For non-zero offsets the drop position depends on the carryall facing
                // We therefore need to predict/correct for the facing *at the drop point*
                if (carryall.CarryableOffset.HorizontalLengthSquared != 0)
                {
                    var dropFacing = (target.CenterPosition - self.CenterPosition).Yaw.Facing;
                    localOffset = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, WRot.FromFacing(dropFacing)));
                    QueueChild(self, new Fly(self, Target.FromPos(target.CenterPosition - body.LocalToWorld(localOffset))), true);
                    QueueChild(self, new Turn(self, dropFacing));
                    return(this);
                }

                QueueChild(self, new Fly(self, target), true);
                return(this);
            }

            // Make sure that the carried actor is on the ground before releasing it
            if (self.World.Map.DistanceAboveTerrain(carryablePosition) != WDist.Zero)
            {
                QueueChild(self, new Land(self), true);
            }

            // Pause briefly before releasing for visual effect
            if (carryall.Info.UnloadingDelay > 0)
            {
                QueueChild(self, new Wait(carryall.Info.UnloadingDelay, false), true);
            }

            // Release carried actor
            QueueChild(self, new ReleaseUnit(self));
            QueueChild(self, new Fly(self, Target.FromPos(self.CenterPosition)));
            return(this);
        }
        void PreSpawnClouds(World world)
        {
            var facing = 256 * info.WindDirection / 32;
            var delta  = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing));

            /*
             * The following codes find the middle point in map. Because clouds must
             * start from an edge to another edge, the paths of clouds
             * which contain this point will always be the longest path no matter the direction,
             * as long as the map is a symmetrical shape.
             *
             * For example, the middle point in map is "@", @ is (u = X/2, v = Y/2)
             * You can imagine any wind direction with all edges clouds possibly spawn.
             *
             *              ____X____
             *				|		|
             *				|		|
             *				|	@	Y
             *				|		|
             *				|_______|
             *
             * By using this longest path, we can figure out the number of clouds should
             * spawn when a certain cloud completely goes over a longest path, which should be
             * neither too little nor too big compared with the clouds per map-cell later
             * spawned by `SpawnCloud`.
             */
            var middlePoint       = new MPos(world.Map.MapSize.X / 2, world.Map.MapSize.Y / 2);
            var middlePointTarget = world.Map.CenterOfCell(middlePoint.ToCPos(world.Map));

            // lDistance and averageSpeed used are for loop condition below.
            var longestCloudDistance = System.Math.Abs(
                (world.Map.DistanceToEdge(middlePointTarget, -delta) + info.Cordon).Length
                + (world.Map.DistanceToEdge(middlePointTarget, delta) + info.Cordon).Length);
            var averageCloudSpeed = System.Math.Abs((int)info.Speed.Average(s => s.Length));

            var stepPerSpawn       = averageCloudSpeed * info.SpawnInterval;
            var stepPerSpawnVector = stepPerSpawn * delta / 1024;

            /*
             * Spawn clouds.
             *
             * Try to make clouds spawning cover the entire map, meanwhile
             * with some randomization. Choose random spawning point and
             * find startEdge, then add offset to let they go further to cover
             * the map. The offset will be increased to simulate the movement
             * the cloud would have made otherwise.
             */
            var offset = WVec.Zero;

            while (longestCloudDistance > 0)
            {
                var position = world.Map.ChooseRandomCell(world.SharedRandom);
                var target   = world.Map.CenterOfCell(position) + new WVec(0, 0, info.CruiseAltitude.Length);

                var startEdge = target - (world.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
                startEdge += offset;
                var finishEdge = target + (world.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

                var animation = new Animation(world, info.Image, () => WAngle.FromFacing(facing));
                animation.PlayRepeating(info.Sequences.Random(world.SharedRandom));

                world.AddFrameEndTask(w => w.Add(new Cloud(world, animation, startEdge, finishEdge, facing, info)));

                offset += stepPerSpawnVector;
                longestCloudDistance -= stepPerSpawn;
            }
        }
Exemple #5
0
        public void SendAirstrike(Actor self, WPos target, bool randomize = true, int attackFacing = 0)
        {
            var info = Info as AirstrikePowerInfo;

            if (randomize)
            {
                attackFacing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings;
            }

            var altitude       = self.World.Map.Rules.Actors[info.UnitType].TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var attackRotation = WRot.FromFacing(attackFacing);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            target = target + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
            var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

            Actor  camera          = null;
            Beacon beacon          = null;
            var    aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(self.World.Map.CellContaining(target)),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                if (beacon != null)
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        w.Remove(beacon);
                        beacon = null;
                    });
                }

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    if (camera != null)
                    {
                        camera.QueueActivity(new Wait(info.CameraRemoveDelay));
                        camera.QueueActivity(new RemoveSelf());
                    }

                    camera = null;

                    if (beacon != null)
                    {
                        self.World.AddFrameEndTask(w =>
                        {
                            w.Remove(beacon);
                            beacon = null;
                        });
                    }
                }
            };

            self.World.AddFrameEndTask(w =>
            {
                var isAllied = self.Owner.IsAlliedWith(self.World.RenderPlayer);
                Game.Sound.Play(isAllied ? Info.LaunchSound : Info.IncomingSound);

                var speech = isAllied ? Info.LaunchSpeechNotification : Info.IncomingSpeechNotification;
                Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", speech, self.Owner.Faction.InternalName);

                Actor distanceTestActor = null;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(attackFacing),
                    });

                    var attack = a.Trait <AttackBomber>();
                    attack.SetTarget(w, target + targetOffset);
                    attack.OnEnteredAttackRange += onEnterRange;
                    attack.OnExitedAttackRange  += onExitRange;
                    attack.OnRemovedFromWorld   += onExitRange;

                    a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        self.Owner,
                        target - new WVec(0, 0, altitude),
                        Info.BeaconPaletteIsPlayerPalette,
                        Info.BeaconPalette,
                        Info.BeaconImage,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        Info.ArrowSequence,
                        Info.CircleSequence,
                        Info.ClockSequence,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance);

                    w.Add(beacon);
                }
            });
        }
Exemple #6
0
        public override bool Tick(Actor self)
        {
            // Refuse to take off if it would land immediately again.
            if (aircraft.ForceLanding)
            {
                Cancel(self);
            }

            var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);

            if (IsCanceling)
            {
                // We must return the actor to a sensible height before continuing.
                // If the aircraft is on the ground we queue TakeOff to manage the influence reservation and takeoff sounds etc.
                // TODO: It would be better to not take off at all, but we lack the plumbing to detect current airborne/landed state.
                // If the aircraft lands when idle and is idle, we let the default idle handler manage this.
                // TODO: Remove this after fixing all activities to work properly with arbitrary starting altitudes.
                var landWhenIdle         = aircraft.Info.IdleBehavior == IdleBehaviorType.Land;
                var skipHeightAdjustment = landWhenIdle && self.CurrentActivity.IsCanceling && self.CurrentActivity.NextActivity == null;
                if (aircraft.Info.CanHover && !skipHeightAdjustment && dat != aircraft.Info.CruiseAltitude)
                {
                    if (dat <= aircraft.LandAltitude)
                    {
                        QueueChild(new TakeOff(self));
                    }
                    else
                    {
                        VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude);
                    }

                    return(false);
                }

                return(true);
            }
            else if (dat <= aircraft.LandAltitude)
            {
                QueueChild(new TakeOff(self));
                return(false);
            }

            bool targetIsHiddenActor;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget = Target.FromTargetPositions(target);
            }

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                return(true);
            }

            var checkTarget   = useLastVisibleTarget ? lastVisibleTarget : target;
            var pos           = aircraft.GetPosition();
            var delta         = checkTarget.CenterPosition - pos;
            var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw.Facing : aircraft.Facing;

            // Inside the target annulus, so we're done
            var insideMaxRange = maxRange.Length > 0 && checkTarget.IsInRange(pos, maxRange);
            var insideMinRange = minRange.Length > 0 && checkTarget.IsInRange(pos, minRange);

            if (insideMaxRange && !insideMinRange)
            {
                return(true);
            }

            var isSlider = aircraft.Info.CanSlide;
            var move     = isSlider ? aircraft.FlyStep(desiredFacing) : aircraft.FlyStep(aircraft.Facing);

            // Inside the minimum range, so reverse if we CanSlide
            if (isSlider && insideMinRange)
            {
                FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude, -move);
                return(false);
            }

            // HACK: Consider ourselves blocked if we have moved by less than 64 WDist in the last five ticks
            // Stop if we are blocked and close enough
            if (positionBuffer.Count >= 5 && (positionBuffer.Last() - positionBuffer[0]).LengthSquared < 4096 &&
                delta.HorizontalLengthSquared <= nearEnough.LengthSquared)
            {
                return(true);
            }

            // The next move would overshoot, so consider it close enough or set final position if we CanSlide
            if (delta.HorizontalLengthSquared < move.HorizontalLengthSquared)
            {
                // For VTOL landing to succeed, it must reach the exact target position,
                // so for the final move it needs to behave as if it had CanSlide.
                if (isSlider || aircraft.Info.VTOL)
                {
                    // Set final (horizontal) position
                    if (delta.HorizontalLengthSquared != 0)
                    {
                        // Ensure we don't include a non-zero vertical component here that would move us away from CruiseAltitude
                        var deltaMove = new WVec(delta.X, delta.Y, 0);
                        FlyTick(self, aircraft, desiredFacing, dat, deltaMove);
                    }

                    // Move to CruiseAltitude, if not already there
                    if (dat != aircraft.Info.CruiseAltitude)
                    {
                        Fly.VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude);
                        return(false);
                    }
                }

                return(true);
            }

            if (!isSlider)
            {
                // Using the turn rate, compute a hypothetical circle traced by a continuous turn.
                // If it contains the destination point, it's unreachable without more complex manuvering.
                var turnRadius = CalculateTurnRadius(aircraft.MovementSpeed, aircraft.TurnSpeed);

                // The current facing is a tangent of the minimal turn circle.
                // Make a perpendicular vector, and use it to locate the turn's center.
                var turnCenterFacing = aircraft.Facing;
                turnCenterFacing += Util.GetNearestFacing(aircraft.Facing, desiredFacing) > 0 ? 64 : -64;

                var turnCenterDir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(turnCenterFacing));
                turnCenterDir *= turnRadius;
                turnCenterDir /= 1024;

                // Compare with the target point, and keep flying away if it's inside the circle.
                var turnCenter = aircraft.CenterPosition + turnCenterDir;
                if ((checkTarget.CenterPosition - turnCenter).HorizontalLengthSquared < turnRadius * turnRadius)
                {
                    desiredFacing = aircraft.Facing;
                }
            }

            positionBuffer.Add(self.CenterPosition);
            if (positionBuffer.Count > 5)
            {
                positionBuffer.RemoveAt(0);
            }

            FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude);

            return(false);
        }
Exemple #7
0
        CPos?PickTargetLocation()
        {
            var target     = self.CenterPosition + new WVec(0, -1024 * effectiveMoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255)));
            var targetCell = self.World.Map.CellContaining(target);

            if (!self.World.Map.Contains(targetCell))
            {
                // If MoveRadius is too big there might not be a valid cell to order the attack to (if actor is on a small island and can't leave)
                if (++ticksIdle % info.ReduceMoveRadiusDelay == 0)
                {
                    effectiveMoveRadius--;
                }

                // We'll be back the next tick; better to sit idle for a few seconds than prolong this tick indefinitely with a loop
                return(null);
            }

            if (info.AvoidTerrainTypes.Count > 0)
            {
                var terrainType = self.World.Map.GetTerrainInfo(targetCell).Type;
                if (Info.AvoidTerrainTypes.Contains(terrainType))
                {
                    return(null);
                }
            }

            ticksIdle           = 0;
            effectiveMoveRadius = info.WanderMoveRadius;

            return(targetCell);
        }
Exemple #8
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);
            }
        }
Exemple #9
0
        CPos PickTargetLocation()
        {
            var target     = self.CenterPosition + new WVec(0, -1024 * effectiveMoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255)));
            var targetCell = self.World.Map.CellContaining(target);

            if (!self.World.Map.Contains(targetCell))
            {
                if (++ticksIdle % info.ReduceMoveRadiusDelay == 0)
                {
                    effectiveMoveRadius--;
                }

                return(CPos.Zero);
            }

            ticksIdle           = 0;
            effectiveMoveRadius = info.WanderMoveRadius;

            return(targetCell);
        }
Exemple #10
0
        public Pair <Actor[], Actor[]> SendParatroopers(Actor self, WPos target, int facing = -1)
        {
            var aircraft = new List <Actor>();
            var units    = new List <Actor>();

            var info = Info as ParatroopersPowerInfo;

            if (facing < 0)
            {
                facing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings;
            }

            var       utLower = info.UnitType.ToLowerInvariant();
            ActorInfo unitType;

            if (!self.World.Map.Rules.Actors.TryGetValue(utLower, out unitType))
            {
                throw new YamlException("Actors ruleset does not include the entry '{0}'".F(utLower));
            }

            var altitude     = unitType.TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var dropRotation = WRot.FromFacing(facing);
            var delta        = new WVec(0, -1024, 0).Rotate(dropRotation);

            target = target + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
            var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

            Actor  camera          = null;
            Beacon beacon          = null;
            var    aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && camera == null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(self.World.Map.CellContaining(target)),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                RemoveBeacon(beacon);

                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
                                                info.ReinforcementsArrivedSpeechNotification, self.Owner.Faction.InternalName);
                }

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    RemoveCamera(camera);
                }
            };

            Action <Actor> onRemovedFromWorld = a =>
            {
                aircraftInRange[a] = false;

                // Checking for attack range is not relevant here because
                // aircraft may be shot down before entering. Thus we remove
                // the camera and beacon only if the whole squad is dead.
                if (aircraftInRange.All(kv => kv.Key.IsDead))
                {
                    RemoveCamera(camera);
                    RemoveBeacon(beacon);
                }
            };

            // Create the actors immediately so they can be returned
            for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
            {
                // Even-sized squads skip the lead plane
                if (i == 0 && (info.SquadSize & 1) == 0)
                {
                    continue;
                }

                // Includes the 90 degree rotation between body and world coordinates
                var so          = info.SquadOffset;
                var spawnOffset = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);

                aircraft.Add(self.World.CreateActor(false, info.UnitType, new TypeDictionary
                {
                    new CenterPositionInit(startEdge + spawnOffset),
                    new OwnerInit(self.Owner),
                    new FacingInit(facing),
                }));
            }

            foreach (var p in info.DropItems)
            {
                units.Add(self.World.CreateActor(false, p.ToLowerInvariant(), new TypeDictionary
                {
                    new OwnerInit(self.Owner)
                }));
            }

            self.World.AddFrameEndTask(w =>
            {
                PlayLaunchSounds();

                Actor distanceTestActor = null;

                var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize;
                var added = 0;
                var j     = 0;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation);
                    var a            = aircraft[j++];
                    w.Add(a);

                    var drop = a.Trait <ParaDrop>();
                    drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells);
                    drop.OnEnteredDropRange += onEnterRange;
                    drop.OnExitedDropRange  += onExitRange;
                    drop.OnRemovedFromWorld += onRemovedFromWorld;

                    var cargo = a.Trait <Cargo>();
                    foreach (var unit in units.Skip(added).Take(passengersPerPlane))
                    {
                        cargo.Load(a, unit);
                        added++;
                    }

                    a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                // Dispose any unused units
                for (var i = added; i < units.Count; i++)
                {
                    units[i].Dispose();
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        self.Owner,
                        target - new WVec(0, 0, altitude),
                        Info.BeaconPaletteIsPlayerPalette,
                        Info.BeaconPalette,
                        Info.BeaconImage,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        Info.BeaconSequence,
                        Info.ArrowSequence,
                        Info.CircleSequence,
                        Info.ClockSequence,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance,
                        Info.BeaconDelay);

                    w.Add(beacon);
                }
            });

            return(Pair.New(aircraft.ToArray(), units.ToArray()));
        }
Exemple #11
0
        public void Tick(World world)
        {
            // Fade the trail out gradually
            if (exploded && info.ContrailLength > 0)
            {
                trail.Update(pos);
                return;
            }

            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);
            }
        }
Exemple #12
0
        public override void DoImpact(Target target, WarheadArgs args)
        {
            var firedBy = args.SourceActor;

            if (!target.IsValidFor(firedBy))
            {
                return;
            }

            var world = firedBy.World;
            var map   = world.Map;

            if (target.Type == TargetType.Invalid)
            {
                return;
            }

            var pos           = target.CenterPosition;
            var actorAtImpact = ImpactActors ? ActorTypeAtImpact(world, pos, firedBy) : ImpactActorType.None;

            // If there's either a) an invalid actor, or b) no actor and invalid terrain, we don't trigger the effect(s).
            if (actorAtImpact == ImpactActorType.Invalid)
            {
                return;
            }
            else if (actorAtImpact == ImpactActorType.None && !IsValidAgainstTerrain(world, pos))
            {
                return;
            }

            var epicenter = AroundTarget && args.WeaponTarget.Type != TargetType.Invalid
                                ? args.WeaponTarget.CenterPosition
                                : target.CenterPosition;

            var amount = Amount.Length == 2
                                        ? world.SharedRandom.Next(Amount[0], Amount[1])
                                        : Amount[0];

            var offset = 256 / amount;

            for (var i = 0; i < amount; i++)
            {
                Target radiusTarget = Target.Invalid;

                var rotation  = WRot.FromFacing(i * offset);
                var targetpos = epicenter + new WVec(weapon.Range.Length, 0, 0).Rotate(rotation);
                var tpos      = Target.FromPos(new WPos(targetpos.X, targetpos.Y, map.CenterOfCell(map.CellContaining(targetpos)).Z));
                if (weapon.IsValidAgainst(tpos, firedBy.World, firedBy))
                {
                    radiusTarget = tpos;
                }

                if (radiusTarget.Type == TargetType.Invalid)
                {
                    continue;
                }

                var projectileArgs = new ProjectileArgs
                {
                    Weapon = weapon,
                    Facing = (radiusTarget.CenterPosition - target.CenterPosition).Yaw,
                    CurrentMuzzleFacing = () => (radiusTarget.CenterPosition - target.CenterPosition).Yaw,

                    DamageModifiers = args.DamageModifiers,

                    InaccuracyModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing <IInaccuracyModifier>()
                                          .Select(a => a.GetInaccuracyModifier()).ToArray() : new int[0],

                    RangeModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing <IRangeModifier>()
                                     .Select(a => a.GetRangeModifier()).ToArray() : new int[0],

                    Source        = target.CenterPosition,
                    CurrentSource = () => target.CenterPosition,
                    SourceActor   = firedBy,
                    GuidedTarget  = radiusTarget,
                    PassiveTarget = radiusTarget.CenterPosition
                };

                if (projectileArgs.Weapon.Projectile != null)
                {
                    var projectile = projectileArgs.Weapon.Projectile.Create(projectileArgs);
                    if (projectile != null)
                    {
                        firedBy.World.AddFrameEndTask(w => w.Add(projectile));
                    }

                    if (projectileArgs.Weapon.Report != null && projectileArgs.Weapon.Report.Any())
                    {
                        Game.Sound.Play(SoundType.World, projectileArgs.Weapon.Report.Random(firedBy.World.SharedRandom), target.CenterPosition);
                    }
                }
            }
        }
Exemple #13
0
        public void SendDropPods(Actor self, Order order, int podFacing)
        {
            var actorInfo        = self.World.Map.Rules.Actors[info.UnitTypes.First().ToLowerInvariant()];
            var aircraftInfo     = actorInfo.TraitInfo <AircraftInfo>();
            var altitude         = aircraftInfo.CruiseAltitude.Length;
            var approachRotation = WRot.FromFacing(podFacing);
            var fallsToEarthInfo = actorInfo.TraitInfo <FallsToEarthInfo>();
            var delta            = new WVec(0, -altitude * aircraftInfo.Speed / fallsToEarthInfo.Velocity.Length, 0).Rotate(approachRotation);

            self.World.AddFrameEndTask(w =>
            {
                var target       = order.Target.CenterPosition;
                var targetCell   = self.World.Map.CellContaining(target);
                var podLocations = self.World.Map.FindTilesInCircle(targetCell, info.PodScatter)
                                   .Where(c => aircraftInfo.LandableTerrainTypes.Contains(w.Map.GetTerrainInfo(c).Type) &&
                                          !self.World.ActorMap.GetActorsAt(c).Any());

                if (!podLocations.Any())
                {
                    return;
                }

                if (info.CameraActor != null)
                {
                    var camera = w.CreateActor(info.CameraActor, new TypeDictionary
                    {
                        new LocationInit(targetCell),
                        new OwnerInit(self.Owner),
                    });

                    camera.QueueActivity(new Wait(info.CameraRemoveDelay));
                    camera.QueueActivity(new RemoveSelf());
                }

                PlayLaunchSounds();

                var drops = self.World.SharedRandom.Next(info.Drops.X, info.Drops.Y);
                for (var i = 0; i < drops; i++)
                {
                    var unitType    = info.UnitTypes.Random(self.World.SharedRandom);
                    var podLocation = podLocations.Random(self.World.SharedRandom);
                    var podTarget   = Target.FromCell(w, podLocation);
                    var location    = self.World.Map.CenterOfCell(podLocation) - delta + new WVec(0, 0, altitude);

                    var pod = w.CreateActor(false, unitType, new TypeDictionary
                    {
                        new CenterPositionInit(location),
                        new OwnerInit(self.Owner),
                        new FacingInit(podFacing)
                    });

                    var aircraft = pod.Trait <Aircraft>();
                    if (!aircraft.CanLand(podLocation))
                    {
                        pod.Dispose();
                    }
                    else
                    {
                        w.Add(new DropPodImpact(self.Owner, info.WeaponInfo, w, location, podTarget, info.WeaponDelay,
                                                info.EntryEffect, info.EntryEffectSequence, info.EntryEffectPalette));
                        w.Add(pod);
                    }
                }
            });
        }
Exemple #14
0
        public override bool Tick(Actor self)
        {
            if (IsCanceling || target.Type == TargetType.Invalid)
            {
                if (landingInitiated)
                {
                    // We must return the actor to a sensible height before continuing.
                    // If the aircraft lands when idle and is idle, continue landing,
                    // otherwise climb back to CruiseAltitude.
                    // TODO: Remove this after fixing all activities to work properly with arbitrary starting altitudes.
                    var shouldLand      = aircraft.Info.IdleBehavior == IdleBehaviorType.Land;
                    var continueLanding = shouldLand && self.CurrentActivity.IsCanceling && self.CurrentActivity.NextActivity == null;
                    if (!continueLanding)
                    {
                        var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);
                        if (dat > aircraft.LandAltitude && dat < aircraft.Info.CruiseAltitude)
                        {
                            QueueChild(new TakeOff(self));
                            return(false);
                        }

                        aircraft.RemoveInfluence();
                        return(true);
                    }
                }
                else
                {
                    return(true);
                }
            }

            var pos = aircraft.GetPosition();

            // Reevaluate target position in case the target has moved.
            targetPosition = target.CenterPosition + offset;
            landingCell    = self.World.Map.CellContaining(targetPosition);

            // We are already at the landing location.
            if ((targetPosition - pos).LengthSquared == 0)
            {
                return(true);
            }

            // Look for free landing cell
            if (target.Type == TargetType.Terrain && !landingInitiated)
            {
                var newLocation = aircraft.FindLandingLocation(landingCell, landRange);

                // Cannot land so fly towards the last target location instead.
                if (!newLocation.HasValue)
                {
                    QueueChild(aircraft.MoveTo(landingCell, 0));
                    return(true);
                }

                if (newLocation.Value != landingCell)
                {
                    target         = Target.FromCell(self.World, newLocation.Value);
                    targetPosition = target.CenterPosition + offset;
                    landingCell    = self.World.Map.CellContaining(targetPosition);
                }
            }

            // Move towards landing location/facing
            if (aircraft.Info.VTOL)
            {
                if ((pos - targetPosition).HorizontalLengthSquared != 0)
                {
                    QueueChild(new Fly(self, Target.FromPos(targetPosition)));
                    return(false);
                }
                else if (desiredFacing != -1 && desiredFacing != aircraft.Facing)
                {
                    QueueChild(new Turn(self, desiredFacing));
                    return(false);
                }
            }

            if (!aircraft.Info.VTOL && !finishedApproach)
            {
                // Calculate approach trajectory
                var altitude = aircraft.Info.CruiseAltitude.Length;

                // Distance required for descent.
                var landDistance = altitude * 1024 / aircraft.Info.MaximumPitch.Tan();

                // Approach landing from the opposite direction of the desired facing
                // TODO: Calculate sensible trajectory without preferred facing.
                var rotation = WRot.Zero;
                if (desiredFacing != -1)
                {
                    rotation = WRot.FromFacing(desiredFacing);
                }

                var approachStart = targetPosition + new WVec(0, landDistance, altitude).Rotate(rotation);

                // Add 10% to the turning radius to ensure we have enough room
                var speed      = aircraft.MovementSpeed * 32 / 35;
                var turnRadius = Fly.CalculateTurnRadius(speed, aircraft.Info.TurnSpeed);

                // Find the center of the turning circles for clockwise and counterclockwise turns
                var angle = WAngle.FromFacing(aircraft.Facing);
                var fwd   = -new WVec(angle.Sin(), angle.Cos(), 0);

                // Work out whether we should turn clockwise or counter-clockwise for approach
                var side           = new WVec(-fwd.Y, fwd.X, fwd.Z);
                var approachDelta  = self.CenterPosition - approachStart;
                var sideTowardBase = new[] { side, -side }
                .MinBy(a => WVec.Dot(a, approachDelta));

                // Calculate the tangent line that joins the turning circles at the current and approach positions
                var cp               = self.CenterPosition + turnRadius * sideTowardBase / 1024;
                var posCenter        = new WPos(cp.X, cp.Y, altitude);
                var approachCenter   = approachStart + new WVec(0, turnRadius * Math.Sign(self.CenterPosition.Y - approachStart.Y), 0);
                var tangentDirection = approachCenter - posCenter;
                var tangentLength    = tangentDirection.Length;
                var tangentOffset    = WVec.Zero;
                if (tangentLength != 0)
                {
                    tangentOffset = new WVec(-tangentDirection.Y, tangentDirection.X, 0) * turnRadius / tangentLength;
                }

                // TODO: correctly handle CCW <-> CW turns
                if (tangentOffset.X > 0)
                {
                    tangentOffset = -tangentOffset;
                }

                var w1 = posCenter + tangentOffset;
                var w2 = approachCenter + tangentOffset;
                var w3 = approachStart;

                turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.Info.TurnSpeed);

                // Move along approach trajectory.
                QueueChild(new Fly(self, Target.FromPos(w1), WDist.Zero, new WDist(turnRadius * 3)));
                QueueChild(new Fly(self, Target.FromPos(w2)));

                // Fix a problem when the airplane is sent to land near the landing cell
                QueueChild(new Fly(self, Target.FromPos(w3), WDist.Zero, new WDist(turnRadius / 2)));
                finishedApproach = true;
                return(false);
            }

            if (!landingInitiated)
            {
                var blockingCells = clearCells.Append(landingCell);

                if (!aircraft.CanLand(blockingCells, target.Actor))
                {
                    // Maintain holding pattern.
                    QueueChild(new FlyIdle(self, 25));

                    self.NotifyBlocker(blockingCells);
                    finishedApproach = false;
                    return(false);
                }

                if (aircraft.Info.LandingSounds.Length > 0)
                {
                    Game.Sound.Play(SoundType.World, aircraft.Info.LandingSounds, self.World, aircraft.CenterPosition);
                }

                aircraft.AddInfluence(landingCell);
                aircraft.EnteringCell(self);
                landingInitiated = true;
            }

            // Final descent.
            if (aircraft.Info.VTOL)
            {
                var landAltitude = self.World.Map.DistanceAboveTerrain(targetPosition) + aircraft.LandAltitude;
                if (Fly.VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, landAltitude))
                {
                    return(false);
                }

                return(true);
            }

            var d = targetPosition - pos;

            // The next move would overshoot, so just set the final position
            var move = aircraft.FlyStep(aircraft.Facing);

            if (d.HorizontalLengthSquared < move.HorizontalLengthSquared)
            {
                var landingAltVec = new WVec(WDist.Zero, WDist.Zero, aircraft.LandAltitude);
                aircraft.SetPosition(self, targetPosition + landingAltVec);
                return(true);
            }

            var landingAlt = self.World.Map.DistanceAboveTerrain(targetPosition) + aircraft.LandAltitude;

            Fly.FlyTick(self, aircraft, d.Yaw.Facing, landingAlt);

            return(false);
        }
Exemple #15
0
        public void SendAirstrike(Actor self, WPos target, bool randomize = true, int attackFacing = 0)
        {
            if (randomize)
            {
                attackFacing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings;
            }

            var altitude       = self.World.Map.Rules.Actors[info.UnitType].TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var attackRotation = WRot.FromFacing(attackFacing);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            target = target + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
            var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

            Actor  camera          = null;
            Beacon beacon          = null;
            var    aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && camera == null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(self.World.Map.CellContaining(target)),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                RemoveBeacon(beacon);

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    RemoveCamera(camera);
                }
            };

            Action <Actor> onRemovedFromWorld = a =>
            {
                aircraftInRange[a] = false;

                // Checking for attack range is not relevant here because
                // aircraft may be shot down before entering. Thus we remove
                // the camera and beacon only if the whole squad is dead.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    RemoveCamera(camera);
                    RemoveBeacon(beacon);
                }
            };

            self.World.AddFrameEndTask(w =>
            {
                PlayLaunchSounds();

                Actor distanceTestActor = null;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(attackFacing),
                    });

                    var attack = a.Trait <AttackBomberCA>();
                    attack.SetTarget(w, target + targetOffset);
                    a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
                    attack.OnEnteredAttackRange += onEnterRange;
                    a.QueueActivity(new AttackMoveActivity(a, () => new FlyIdle(a, info.GuardDuration)));
                    attack.OnExitedAttackRange += onExitRange;
                    a.QueueActivity(new FlyOffMap(a));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        self.Owner,
                        target - new WVec(0, 0, altitude),
                        Info.BeaconPaletteIsPlayerPalette,
                        Info.BeaconPalette,
                        Info.BeaconImage,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        Info.BeaconSequence,
                        Info.ArrowSequence,
                        Info.CircleSequence,
                        Info.ClockSequence,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance,
                        Info.BeaconDelay);

                    w.Add(beacon);
                }
            });
        }
        public VortexProjectile(VortexProjectileInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;

            sourcepos = args.Source;

            var firedBy = args.SourceActor;

            world = args.SourceActor.World;

            if (info.Speed.Length > 1)
            {
                speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length));
            }
            else
            {
                speed = info.Speed[0];
            }

            targetpos = GetTargetPos();

            mindelay = args.Weapon.MinRange.Length / speed.Length;

            projectiles = new VortexProjectileEffect[info.NumProjectiles];

            var mainFacing = (targetpos - sourcepos).Yaw;

            // used for lerping projectiles at the same pace
            var estimatedLifespan = Math.Max(args.Weapon.Range.Length / speed.Length, 1);

            // target that will be assigned
            Target target;

            // subprojectiles facing
            int slice = 256 / info.NumProjectiles;

            for (int i = 0; i < info.NumProjectiles; i++)
            {
                target = Target.FromPos(targetpos);

                // If it's true then lifespan is counted from source position to target instead of max range.
                lifespan = info.KillProjectilesWhenReachedTargetLocation
                                        ? Math.Max((args.PassiveTarget - args.Source).Length / speed.Length, 1)
                                        : estimatedLifespan;

                var facing        = mainFacing + (new WAngle(i * slice));
                var newRotation   = WRot.FromFacing(facing.Angle);
                var rotatedTarget = (targetpos - sourcepos).Rotate(newRotation);

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

                target = Target.FromPos(sourcepos + rotatedTarget);
                var normalizedVec = WVec.Lerp(WVec.Zero, rotatedTarget, 512, rotatedTarget.Length);

                var projectileArgs = new VortexProjectileArgs
                {
                    Weapon          = args.Weapon,
                    DamageModifiers = args.DamageModifiers,
                    Facing          = facing,
                    Source          = sourcepos,
                    CurrentSource   = () => sourcepos,
                    SourceActor     = firedBy,
                    GuidedTarget    = target,
                    PassiveTarget   = sourcepos + rotatedTarget,
                    VecNormalized   = normalizedVec,
                    Normal          = normal
                };

                projectiles[i] = new VortexProjectileEffect(info, projectileArgs, lifespan, estimatedLifespan);
            }

            foreach (var p in projectiles)
            {
                world.AddFrameEndTask(w => w.Add(p));
            }
        }
Exemple #17
0
        public override Activity Tick(Actor self)
        {
            // Refuse to take off if it would land immediately again.
            if (aircraft.ForceLanding)
            {
                Cancel(self);
            }

            if (IsCanceling)
            {
                return(NextActivity);
            }

            bool targetIsHiddenActor;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget = Target.FromTargetPositions(target);
            }

            var oldUseLastVisibleTarget = useLastVisibleTarget;

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // Update target lines if required
            if (useLastVisibleTarget != oldUseLastVisibleTarget && targetLineColor.HasValue)
            {
                self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, targetLineColor.Value, false);
            }

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                return(NextActivity);
            }

            var pos         = self.CenterPosition;
            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

            if (!soundPlayed && aircraft.Info.TakeoffSounds.Length > 0 && self.IsAtGroundLevel())
            {
                Game.Sound.Play(SoundType.World, aircraft.Info.TakeoffSounds, self.World, aircraft.CenterPosition);
                soundPlayed = true;
            }

            // We are taking off, so remove influence in ground cells.
            if (self.IsAtGroundLevel())
            {
                aircraft.RemoveInfluence();
            }

            // Inside the target annulus, so we're done
            var insideMaxRange = maxRange.Length > 0 && checkTarget.IsInRange(aircraft.CenterPosition, maxRange);
            var insideMinRange = minRange.Length > 0 && checkTarget.IsInRange(aircraft.CenterPosition, minRange);

            if (insideMaxRange && !insideMinRange)
            {
                return(NextActivity);
            }

            var delta = checkTarget.CenterPosition - self.CenterPosition;

            // The next move would overshoot, so consider it close enough
            var move = aircraft.FlyStep(aircraft.Facing);

            if (delta.HorizontalLengthSquared < move.HorizontalLengthSquared)
            {
                return(NextActivity);
            }

            // Don't turn until we've reached the cruise altitude
            var desiredFacing  = delta.Yaw.Facing;
            var targetAltitude = aircraft.CenterPosition.Z + aircraft.Info.CruiseAltitude.Length - self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition).Length;

            if (aircraft.CenterPosition.Z < targetAltitude)
            {
                desiredFacing = aircraft.Facing;
            }
            else
            {
                // Using the turn rate, compute a hypothetical circle traced by a continuous turn.
                // If it contains the destination point, it's unreachable without more complex manuvering.
                var turnRadius = CalculateTurnRadius(aircraft.MovementSpeed, aircraft.TurnSpeed);

                // The current facing is a tangent of the minimal turn circle.
                // Make a perpendicular vector, and use it to locate the turn's center.
                var turnCenterFacing = aircraft.Facing;
                turnCenterFacing += Util.GetNearestFacing(aircraft.Facing, desiredFacing) > 0 ? 64 : -64;

                var turnCenterDir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(turnCenterFacing));
                turnCenterDir *= turnRadius;
                turnCenterDir /= 1024;

                // Compare with the target point, and keep flying away if it's inside the circle.
                var turnCenter = aircraft.CenterPosition + turnCenterDir;
                if ((checkTarget.CenterPosition - turnCenter).HorizontalLengthSquared < turnRadius * turnRadius)
                {
                    desiredFacing = aircraft.Facing;
                }
            }

            FlyToward(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude);

            return(this);
        }
        public override Activity Tick(Actor self)
        {
            if (innerActivity != null)
            {
                innerActivity = ActivityUtils.RunActivity(self, innerActivity);
                return(this);
            }

            if (IsCanceled)
            {
                return(NextActivity);
            }

            if ((carryall.State == Carryall.CarryallState.Idle || carryall.Carryable.IsDead) && state != State.TakeOff)
            {
                state = State.Aborted;
            }

            switch (state)
            {
            case State.Transport:
            {
                var targetLocation = FindDropLocation(destination, carryall.Info.DropRange);

                // Can't land, so wait at the target until something changes
                if (!targetLocation.HasValue)
                {
                    innerActivity = ActivityUtils.SequenceActivities(
                        new HeliFly(self, Target.FromCell(self.World, destination)),
                        new Wait(25));

                    return(this);
                }

                var targetPosition = self.World.Map.CenterOfCell(targetLocation.Value);

                var localOffset       = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation));
                var carryablePosition = self.CenterPosition + body.LocalToWorld(localOffset);
                if ((carryablePosition - targetPosition).HorizontalLengthSquared != 0)
                {
                    // For non-zero offsets the drop position depends on the carryall facing
                    // We therefore need to predict/correct for the facing *at the drop point*
                    if (carryall.CarryableOffset.HorizontalLengthSquared != 0)
                    {
                        var facing = (targetPosition - self.CenterPosition).Yaw.Facing;
                        localOffset   = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, WRot.FromFacing(facing)));
                        innerActivity = ActivityUtils.SequenceActivities(
                            new HeliFly(self, Target.FromPos(targetPosition - body.LocalToWorld(localOffset))),
                            new Turn(self, facing));

                        return(this);
                    }

                    innerActivity = new HeliFly(self, Target.FromPos(targetPosition));
                    return(this);
                }

                state = State.Land;
                return(this);
            }

            case State.Land:
            {
                if (!CanDropHere())
                {
                    state = State.Transport;
                    return(this);
                }

                // Make sure that the carried actor is on the ground before releasing it
                var localOffset       = carryall.CarryableOffset.Rotate(body.QuantizeOrientation(self, self.Orientation));
                var carryablePosition = self.CenterPosition + body.LocalToWorld(localOffset);
                if (self.World.Map.DistanceAboveTerrain(carryablePosition) != WDist.Zero)
                {
                    innerActivity = new HeliLand(self, false, -new WDist(carryall.CarryableOffset.Z));
                    return(this);
                }

                state = carryall.Info.UnloadingDelay > 0 ? State.Wait : State.Release;
                return(this);
            }

            case State.Wait:
                state         = State.Release;
                innerActivity = new Wait(carryall.Info.UnloadingDelay, false);
                return(this);

            case State.Release:
                if (!CanDropHere())
                {
                    state = State.Transport;
                    return(this);
                }

                Release();
                state = State.TakeOff;
                return(this);

            case State.TakeOff:
                return(ActivityUtils.SequenceActivities(new HeliFly(self, Target.FromPos(self.CenterPosition)), NextActivity));

            case State.Aborted:
                carryall.UnreserveCarryable(self);
                break;
            }

            return(NextActivity);
        }
Exemple #19
0
        public WarheadTrailProjectile(WarheadTrailProjectileInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;

            projectilepos = args.Source;
            sourcepos     = args.Source;

            var firedBy = args.SourceActor;

            world = args.SourceActor.World;

            if (info.Speed.Length > 1)
            {
                speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length));
            }
            else
            {
                speed = info.Speed[0];
            }

            targetpos = GetTargetPos();

            mindelay = args.Weapon.MinRange.Length / speed.Length;

            projectiles = new WarheadTrailProjectileEffect[info.Offsets.Count()];
            var range      = Common.Util.ApplyPercentageModifiers(args.Weapon.Range.Length, args.RangeModifiers);
            var mainFacing = (targetpos - sourcepos).Yaw.Facing + 64;

            // used for lerping projectiles at the same pace
            var estimatedLifespan = Math.Max(args.Weapon.Range.Length / speed.Length, 1);

            // target that will be assigned
            Target target;

            for (int i = 0; i < info.Offsets.Count(); i++)
            {
                switch (info.FireMode)
                {
                case FireMode.Focus:
                    offsetRotation  = WRot.FromFacing(mainFacing);
                    offsetTargetPos = sourcepos + new WVec(range, 0, 0).Rotate(offsetRotation);
                    offsetSourcePos = sourcepos + info.Offsets[i].Rotate(offsetRotation);
                    break;

                case FireMode.Line:
                    offsetRotation  = WRot.FromFacing(mainFacing);
                    offsetTargetPos = sourcepos + new WVec(range + info.Offsets[i].X, info.Offsets[i].Y, info.Offsets[i].Z).Rotate(offsetRotation);
                    offsetSourcePos = sourcepos + info.Offsets[i].Rotate(offsetRotation);
                    break;

                case FireMode.Spread:
                    offsetRotation  = WRot.FromFacing(info.Offsets[i].Yaw.Facing - 64) + WRot.FromFacing(mainFacing);
                    offsetSourcePos = sourcepos + info.Offsets[i].Rotate(offsetRotation);
                    offsetTargetPos = sourcepos + new WVec(range + info.Offsets[i].X, info.Offsets[i].Y, info.Offsets[i].Z).Rotate(offsetRotation);
                    break;
                }

                if (info.Inaccuracy.Length > 0)
                {
                    var inaccuracy       = Common.Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers);
                    var maxOffset        = inaccuracy * (args.PassiveTarget - projectilepos).Length / range;
                    var inaccuracyOffset = WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024;
                    offsetTargetPos += inaccuracyOffset;
                }

                target = Target.FromPos(offsetTargetPos);

                // If it's true then lifespan is counted from source position to target instead of max range.
                lifespan = info.KillProjectilesWhenReachedTargetLocation
                                        ? Math.Max((args.PassiveTarget - args.Source).Length / speed.Length, 1)
                                        : estimatedLifespan;

                var facing         = (offsetTargetPos - offsetSourcePos).Yaw;
                var projectileArgs = new ProjectileArgs
                {
                    Weapon          = info.WeaponInfo,
                    DamageModifiers = args.DamageModifiers,
                    Facing          = facing,
                    Source          = offsetSourcePos,
                    CurrentSource   = () => offsetSourcePos,
                    SourceActor     = firedBy,
                    GuidedTarget    = target,
                    PassiveTarget   = target.CenterPosition
                };

                projectiles[i] = new WarheadTrailProjectileEffect(info, projectileArgs, lifespan, estimatedLifespan, info.ForceAtGroundLevel);
            }

            foreach (var p in projectiles)
            {
                world.AddFrameEndTask(w => w.Add(p));
            }
        }
Exemple #20
0
        public void Tick(World world)
        {
            ticks++;
            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.ROT);
            var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * info.Speed.Range / 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 cell = world.Map.CellContaining(pos);

            var shouldExplode = (pos.Z < 0) ||          // Hit the ground
                                (dist.LengthSquared < info.CloseEnough.Range * info.CloseEnough.Range) || // Within range
                                (info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel
                                (!info.High && world.ActorMap.GetUnitsAt(cell).Any(a => a.HasTrait <IBlocksBullets>())) || // Hit a wall
                                !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);
            }
        }
        public override void Activate(Actor self, Order order, SupportPowerManager manager)
        {
            base.Activate(self, order, manager);

            var info           = Info as AirstrikePowerInfo;
            var attackFacing   = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
            var attackRotation = WRot.FromFacing(attackFacing);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            var altitude   = self.World.Map.Rules.Actors[info.UnitType].Traits.Get <PlaneInfo>().CruiseAltitude.Range;
            var target     = order.TargetLocation.CenterPosition + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.DistanceToMapEdge(target, -delta) + info.Cordon).Range * delta / 1024;
            var finishEdge = target + (self.World.DistanceToMapEdge(target, delta) + info.Cordon).Range * delta / 1024;

            Actor  flare  = null;
            Actor  camera = null;
            Beacon beacon = null;
            Dictionary <Actor, bool> aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(order.TargetLocation),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                if (beacon != null)
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        w.Remove(beacon);
                        beacon = null;
                    });
                }

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera and flare when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    if (camera != null)
                    {
                        camera.QueueActivity(new Wait(info.CameraRemoveDelay));
                        camera.QueueActivity(new RemoveSelf());
                    }

                    if (flare != null)
                    {
                        flare.QueueActivity(new Wait(info.FlareRemoveDelay));
                        flare.QueueActivity(new RemoveSelf());
                    }

                    camera = flare = null;
                }
            };

            self.World.AddFrameEndTask(w =>
            {
                if (info.FlareActor != null)
                {
                    flare = w.CreateActor(info.FlareActor, new TypeDictionary
                    {
                        new LocationInit(order.TargetLocation),
                        new OwnerInit(self.Owner),
                    });
                }

                var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound;
                Sound.Play(notification);

                Actor distanceTestActor = null;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(attackFacing),
                    });

                    var attack = a.Trait <AttackBomber>();
                    attack.SetTarget(target + targetOffset);
                    attack.OnEnteredAttackRange += onEnterRange;
                    attack.OnExitedAttackRange  += onExitRange;
                    attack.OnRemovedFromWorld   += onExitRange;

                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        order.Player,
                        order.TargetLocation.CenterPosition,
                        Info.BeaconPalettePrefix,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Range) * 1f / distance
                        );

                    w.Add(beacon);
                }
            });
        }
Exemple #22
0
        void ITick.Tick(Actor self)
        {
            if (IsTraitDisabled || IsTraitPaused || !self.IsInWorld || --ticks > 0)
            {
                return;
            }

            ticks = Info.Delay.Length == 2
                                        ? world.SharedRandom.Next(Info.Delay[0], Info.Delay[1])
                                        : Info.Delay[0];

            var localoffset = body != null
                                        ? body.LocalToWorld(Info.LocalOffset.Rotate(body.QuantizeOrientation(self, self.Orientation)))
                                        : Info.LocalOffset;

            var position = self.CenterPosition + localoffset;

            var availableTargetActors = world.FindActorsOnCircle(position, Info.WeaponInfo.Range)
                                        .Where(x => (Info.AllowSelfHit || x != self) &&
                                               Info.WeaponInfo.IsValidAgainst(Target.FromActor(x), world, self) &&
                                               Info.AimTargetStances.HasStance(self.Owner.Stances[x.Owner]))
                                        .Where(x =>
            {
                var activeShapes = x.TraitsImplementing <HitShape>().Where(Exts.IsTraitEnabled);
                if (!activeShapes.Any())
                {
                    return(false);
                }

                var distance = activeShapes.Min(t => t.DistanceFromEdge(x, position));

                if (distance < Info.WeaponInfo.Range)
                {
                    return(true);
                }

                return(false);
            })
                                        .Shuffle(world.SharedRandom);

            var targetActor = availableTargetActors.GetEnumerator();

            var amount = Info.Amount.Length == 2
                                        ? world.SharedRandom.Next(Info.Amount[0], Info.Amount[1])
                                        : Info.Amount[0];

            for (var i = 0; i < amount; i++)
            {
                Target shrapnelTarget = Target.Invalid;

                if (world.SharedRandom.Next(100) < Info.AimChance && targetActor.MoveNext())
                {
                    shrapnelTarget = Target.FromActor(targetActor.Current);
                }

                if (Info.ThrowWithoutTarget && shrapnelTarget.Type == TargetType.Invalid)
                {
                    var rotation  = WRot.FromFacing(world.SharedRandom.Next(1024));
                    var range     = world.SharedRandom.Next(Info.WeaponInfo.MinRange.Length, Info.WeaponInfo.Range.Length);
                    var targetpos = position + new WVec(range, 0, 0).Rotate(rotation);
                    var tpos      = Target.FromPos(new WPos(targetpos.X, targetpos.Y, world.Map.CenterOfCell(world.Map.CellContaining(targetpos)).Z));
                    if (Info.WeaponInfo.IsValidAgainst(tpos, world, self))
                    {
                        shrapnelTarget = tpos;
                    }
                }

                if (shrapnelTarget.Type == TargetType.Invalid)
                {
                    continue;
                }

                var args = new ProjectileArgs
                {
                    Weapon = Info.WeaponInfo,
                    Facing = (shrapnelTarget.CenterPosition - position).Yaw,

                    DamageModifiers = !self.IsDead ? self.TraitsImplementing <IFirepowerModifier>()
                                      .Select(a => a.GetFirepowerModifier()).ToArray() : new int[0],

                    InaccuracyModifiers = !self.IsDead ? self.TraitsImplementing <IInaccuracyModifier>()
                                          .Select(a => a.GetInaccuracyModifier()).ToArray() : new int[0],

                    RangeModifiers = !self.IsDead ? self.TraitsImplementing <IRangeModifier>()
                                     .Select(a => a.GetRangeModifier()).ToArray() : new int[0],

                    Source        = position,
                    CurrentSource = () => position,
                    SourceActor   = self,
                    GuidedTarget  = shrapnelTarget,
                    PassiveTarget = shrapnelTarget.CenterPosition
                };

                if (args.Weapon.Projectile != null)
                {
                    var projectile = args.Weapon.Projectile.Create(args);
                    if (projectile != null)
                    {
                        world.AddFrameEndTask(w => w.Add(projectile));
                    }

                    if (args.Weapon.Report != null && args.Weapon.Report.Any())
                    {
                        Game.Sound.Play(SoundType.World, args.Weapon.Report.Random(world.SharedRandom), position);
                    }
                }
            }
        }
Exemple #23
0
        /// <summary>
        /// 开火
        /// </summary>
        /// <param name="self"></param>
        /// <param name="facing"></param>
        /// <param name="target"></param>
        /// <param name="barrel"></param>
        protected virtual void FireBarrel(Actor self, IFacing facing, Target target, Barrel barrel)
        {
            Func <WPos> muzzlePosition = () => self.CenterPosition + MuzzleOffset(self, barrel);//枪口位置

            var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4;

            var passiveTarget = Weapon.TargetActorCenter ? target.CenterPosition : target.Positions.PositionClosestTo(muzzlePosition());
            var initialOffset = Weapon.FirstBurstTargetOffset;

            if (initialOffset != WVec.Zero)
            {
                //We want this to match Armament.LocalOffset,so we need to convert it to forward,right,up.
                //我们希望这个匹配Armament.LocalOffset,所以我们需要将它转换为向前,向上,向右。
                initialOffset  = new WVec(initialOffset.Y, -initialOffset.X, initialOffset.Z);
                passiveTarget += initialOffset.Rotate(WRot.FromFacing(legacyFacing));
            }

            var followingOffset = Weapon.FollowingBurstTargetOffset;

            if (followingOffset != WVec.Zero)
            {
                //We want this to match Armament.LocalOffset,so we need to convert it to forward,right,up.
                followingOffset = new WVec(followingOffset.Y, -followingOffset.X, followingOffset.Z);
                passiveTarget  += ((Weapon.Burst - Burst) * followingOffset).Rotate(WRot.FromFacing(legacyFacing));
            }

            var args = new ProjectileArgs
            {
                Weapon = Weapon,
                Facing = legacyFacing,

                DamagedModifiers = damageModifiers.ToArray(),

                InaccuracyModifiers = inaccuracyModifiers.ToArray(),

                RangeModifiers = rangeModifiers.ToArray(),

                Source = muzzlePosition(),

                CurrentSource = muzzlePosition,

                SourceActor = self,

                PassiveTarget = passiveTarget,

                GuidedTarget = target
            };

            foreach (var na in notifyAttacks)
            {
                na.PreparingAttack(self, target, this, barrel);
            }

            ScheduleDelayedAction(Info.FireDelay, () =>
            {
                if (args.Weapon.Projectile != null)
                {
                    var projectile = args.Weapon.Projectile.Create(args);
                    if (projectile != null)
                    {
                        self.World.Add(projectile);
                    }

                    if (args.Weapon.Report != null && args.Weapon.Report.Any())
                    {
                        WarGame.Sound.Play(SoundType.World, args.Weapon.Report.Random(self.World.SharedRandom), self.CenterPosition);
                    }

                    if (Burst == args.Weapon.Burst && args.Weapon.StartBurstReport != null && args.Weapon.StartBurstReport.Any())
                    {
                        WarGame.Sound.Play(SoundType.World, args.Weapon.StartBurstReport.Random(self.World.SharedRandom), self.CenterPosition);
                    }

                    foreach (var na in notifyAttacks)
                    {
                        na.Attacking(self, target, this, barrel);
                    }

                    Recoil = Info.Recoil;
                }
            });
        }
Exemple #24
0
        public override void Activate(Actor self, Order order, SupportPowerManager manager)
        {
            base.Activate(self, order, manager);

            var info         = Info as ParatroopersPowerInfo;
            var dropFacing   = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
            var dropRotation = WRot.FromFacing(dropFacing);
            var delta        = new WVec(0, -1024, 0).Rotate(dropRotation);

            var altitude   = self.World.Map.Rules.Actors[info.UnitType].Traits.Get <PlaneInfo>().CruiseAltitude.Range;
            var target     = self.World.Map.CenterOfCell(order.TargetLocation) + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Range * delta / 1024;
            var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Range * delta / 1024;

            Actor  camera          = null;
            Beacon beacon          = null;
            var    aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(order.TargetLocation),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                if (beacon != null)
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        w.Remove(beacon);
                        beacon = null;
                    });
                }

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    if (camera != null)
                    {
                        camera.QueueActivity(new Wait(info.CameraRemoveDelay));
                        camera.QueueActivity(new RemoveSelf());
                    }

                    camera = null;
                }
            };

            self.World.AddFrameEndTask(w =>
            {
                var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound;
                Sound.Play(notification);

                Actor distanceTestActor = null;

                var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize;
                var added = 0;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(dropFacing),
                    });

                    var drop = a.Trait <ParaDrop>();
                    drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells);
                    drop.OnEnteredDropRange += onEnterRange;
                    drop.OnExitedDropRange  += onExitRange;
                    drop.OnRemovedFromWorld += onExitRange;

                    var cargo      = a.Trait <Cargo>();
                    var passengers = info.DropItems.Skip(added).Take(passengersPerPlane);
                    added         += passengersPerPlane;

                    foreach (var p in passengers)
                    {
                        cargo.Load(a, self.World.CreateActor(false, p.ToLowerInvariant(),
                                                             new TypeDictionary {
                            new OwnerInit(a.Owner)
                        }));
                    }

                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        order.Player,
                        self.World.Map.CenterOfCell(order.TargetLocation),
                        Info.BeaconPalettePrefix,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Range) * 1f / distance
                        );

                    w.Add(beacon);
                }
            });
        }
Exemple #25
0
        public override void DoImpact(Target target, Actor firedBy, IEnumerable <int> damageModifiers)
        {
            var world = firedBy.World;
            var map   = world.Map;

            if (!IsValidImpact(target.CenterPosition, firedBy))
            {
                return;
            }

            var directActors = world.FindActorsInCircle(target.CenterPosition, TargetSearchRadius);

            var availableTargetActors = world.FindActorsInCircle(target.CenterPosition, weapon.Range)
                                        .Where(x => (AllowDirectHit || !directActors.Contains(x)) &&
                                               weapon.IsValidAgainst(Target.FromActor(x), firedBy.World, firedBy) &&
                                               AimTargetStances.HasStance(firedBy.Owner.Stances[x.Owner]))
                                        .Shuffle(world.SharedRandom);

            var targetActor = availableTargetActors.GetEnumerator();

            var amount = Amount.Length == 2
                                        ? world.SharedRandom.Next(Amount[0], Amount[1])
                                        : Amount[0];

            for (var i = 0; i < amount; i++)
            {
                Target shrapnelTarget = Target.Invalid;

                if (world.SharedRandom.Next(100) < AimChance && targetActor.MoveNext())
                {
                    shrapnelTarget = Target.FromActor(targetActor.Current);
                }

                if (ThrowWithoutTarget && shrapnelTarget.Type == TargetType.Invalid)
                {
                    var rotation  = WRot.FromFacing(world.SharedRandom.Next(1024));
                    var range     = world.SharedRandom.Next(weapon.MinRange.Length, weapon.Range.Length);
                    var targetpos = target.CenterPosition + new WVec(range, 0, 0).Rotate(rotation);
                    var tpos      = Target.FromPos(new WPos(targetpos.X, targetpos.Y, map.CenterOfCell(map.CellContaining(targetpos)).Z));
                    if (weapon.IsValidAgainst(tpos, firedBy.World, firedBy))
                    {
                        shrapnelTarget = tpos;
                    }
                }

                if (shrapnelTarget.Type == TargetType.Invalid)
                {
                    continue;
                }

                var args = new ProjectileArgs
                {
                    Weapon = weapon,
                    Facing = (shrapnelTarget.CenterPosition - target.CenterPosition).Yaw.Facing,

                    DamageModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing <IFirepowerModifier>()
                                      .Select(a => a.GetFirepowerModifier()).ToArray() : new int[0],

                    InaccuracyModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing <IInaccuracyModifier>()
                                          .Select(a => a.GetInaccuracyModifier()).ToArray() : new int[0],

                    RangeModifiers = !firedBy.IsDead ? firedBy.TraitsImplementing <IRangeModifier>()
                                     .Select(a => a.GetRangeModifier()).ToArray() : new int[0],

                    Source        = target.CenterPosition,
                    SourceActor   = firedBy,
                    GuidedTarget  = shrapnelTarget,
                    PassiveTarget = shrapnelTarget.CenterPosition
                };

                if (args.Weapon.Projectile != null)
                {
                    var projectile = args.Weapon.Projectile.Create(args);
                    if (projectile != null)
                    {
                        firedBy.World.AddFrameEndTask(w => w.Add(projectile));
                    }

                    if (args.Weapon.Report != null && args.Weapon.Report.Any())
                    {
                        Game.Sound.Play(SoundType.World, args.Weapon.Report.Random(firedBy.World.SharedRandom), target.CenterPosition);
                    }
                }
            }
        }
Exemple #26
0
        CPos PickTargetLocation()
        {
            var target     = self.CenterPosition + new WVec(0, -1024 * effectiveMoveRadius, 0).Rotate(WRot.FromFacing(self.World.SharedRandom.Next(255)));
            var targetCell = self.World.Map.CellContaining(target);

            if (!self.World.Map.Contains(targetCell))
            {
                // If MoveRadius is too big there might not be a valid cell to order the attack to (if actor is on a small island and can't leave)
                if (++ticksIdle % info.TicksToWaitBeforeReducingMoveRadius == 0)
                {
                    effectiveMoveRadius--;
                }

                return(CPos.Zero);                 // We'll be back the next tick; better to sit idle for a few seconds than prolong this tick indefinitely with a loop
            }

            ticksIdle           = 0;
            effectiveMoveRadius = info.WanderMoveRadius;

            return(targetCell);
        }
Exemple #27
0
		public Actor[] SendParatroopers(Actor self, WPos target, bool randomize = true, int dropFacing = 0)
		{
			var units = new List<Actor>();

			var info = Info as ParatroopersPowerInfo;

			if (randomize)
				dropFacing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings;

			var utLower = info.UnitType.ToLowerInvariant();
			ActorInfo unitType;
			if (!self.World.Map.Rules.Actors.TryGetValue(utLower, out unitType))
				throw new YamlException("Actors ruleset does not include the entry '{0}'".F(utLower));

			var altitude = unitType.TraitInfo<AircraftInfo>().CruiseAltitude.Length;
			var dropRotation = WRot.FromFacing(dropFacing);
			var delta = new WVec(0, -1024, 0).Rotate(dropRotation);
			target = target + new WVec(0, 0, altitude);
			var startEdge = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
			var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

			Actor camera = null;
			Beacon beacon = null;
			var aircraftInRange = new Dictionary<Actor, bool>();

			Action<Actor> onEnterRange = a =>
			{
				// Spawn a camera and remove the beacon when the first plane enters the target area
				if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
				{
					self.World.AddFrameEndTask(w =>
					{
						camera = w.CreateActor(info.CameraActor, new TypeDictionary
						{
							new LocationInit(self.World.Map.CellContaining(target)),
							new OwnerInit(self.Owner),
						});
					});
				}

				if (beacon != null)
				{
					self.World.AddFrameEndTask(w =>
					{
						w.Remove(beacon);
						beacon = null;
					});
				}

				aircraftInRange[a] = true;
			};

			Action<Actor> onExitRange = a =>
			{
				aircraftInRange[a] = false;

				// Remove the camera when the final plane leaves the target area
				if (!aircraftInRange.Any(kv => kv.Value))
				{
					if (camera != null)
					{
						camera.QueueActivity(new Wait(info.CameraRemoveDelay));
						camera.QueueActivity(new RemoveSelf());
					}

					camera = null;

					if (beacon != null)
					{
						self.World.AddFrameEndTask(w =>
						{
							w.Remove(beacon);
							beacon = null;
						});
					}
				}
			};

			foreach (var p in info.DropItems)
			{
				var unit = self.World.CreateActor(false, p.ToLowerInvariant(),
					new TypeDictionary { new OwnerInit(self.Owner) });

				units.Add(unit);
			}

			self.World.AddFrameEndTask(w =>
			{
				PlayLaunchSounds();

				Actor distanceTestActor = null;

				var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize;
				var added = 0;
				for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
				{
					// Even-sized squads skip the lead plane
					if (i == 0 && (info.SquadSize & 1) == 0)
						continue;

					// Includes the 90 degree rotation between body and world coordinates
					var so = info.SquadOffset;
					var spawnOffset = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);
					var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation);

					var a = w.CreateActor(info.UnitType, new TypeDictionary
					{
						new CenterPositionInit(startEdge + spawnOffset),
						new OwnerInit(self.Owner),
						new FacingInit(dropFacing),
					});

					var drop = a.Trait<ParaDrop>();
					drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells);
					drop.OnEnteredDropRange += onEnterRange;
					drop.OnExitedDropRange += onExitRange;
					drop.OnRemovedFromWorld += onExitRange;

					var cargo = a.Trait<Cargo>();
					var passengers = units.Skip(added).Take(passengersPerPlane);
					added += passengersPerPlane;

					foreach (var p in passengers)
						cargo.Load(a, p);

					a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
					a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
					a.QueueActivity(new RemoveSelf());
					aircraftInRange.Add(a, false);
					distanceTestActor = a;
				}

				if (Info.DisplayBeacon)
				{
					var distance = (target - startEdge).HorizontalLength;

					beacon = new Beacon(
						self.Owner,
						target - new WVec(0, 0, altitude),
						Info.BeaconPaletteIsPlayerPalette,
						Info.BeaconPalette,
						Info.BeaconImage,
						Info.BeaconPoster,
						Info.BeaconPosterPalette,
						Info.ArrowSequence,
						Info.CircleSequence,
						Info.ClockSequence,
						() => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance,
						Info.BeaconDelay);

					w.Add(beacon);
				}
			});

			return units.ToArray();
		}
Exemple #28
0
        public override void Activate(Actor self, Order order)
        {
            var info = Info as AirstrikePowerInfo;

            var attackFacing   = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
            var attackRotation = WRot.FromFacing(attackFacing);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            var altitude   = Rules.Info[info.UnitType].Traits.Get <PlaneInfo>().CruiseAltitude * 1024 / Game.CellSize;
            var target     = order.TargetLocation.CenterPosition + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.DistanceToMapEdge(target, -delta) + info.Cordon).Range * delta / 1024;
            var finishEdge = target + (self.World.DistanceToMapEdge(target, delta) + info.Cordon).Range * delta / 1024;

            self.World.AddFrameEndTask(w =>
            {
                var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound;
                Sound.Play(notification);

                Actor flare = null;
                if (info.FlareType != null)
                {
                    flare = w.CreateActor(info.FlareType, new TypeDictionary
                    {
                        new LocationInit(order.TargetLocation),
                        new OwnerInit(self.Owner),
                    });

                    flare.QueueActivity(new Wait(info.FlareTime));
                    flare.QueueActivity(new RemoveSelf());
                }

                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(attackFacing),
                    });

                    a.Trait <AttackBomber>().SetTarget(target + targetOffset);

                    if (flare != null)
                    {
                        a.QueueActivity(new CallFunc(() => flare.Destroy()));
                    }

                    a.QueueActivity(Fly.ToPos(finishEdge + spawnOffset));
                    a.QueueActivity(new RemoveSelf());
                }
            });
        }
Exemple #29
0
        public override void Tick(Actor self)
        {
            base.Tick(self);

            var facing     = self.TraitOrDefault <IFacing>();
            var cp         = self.CenterPosition;
            var bombTarget = Target.FromPos(cp - new WVec(0, 0, cp.Z));

            // Bombs drop anywhere in range
            foreach (var a in Armaments.Where(a => a.Info.Name == info.Bombs))
            {
                var range = new WRange((int)(1024 * a.Weapon.Range));
                if (!target.IsInRange(self.CenterPosition, range))
                {
                    continue;
                }

                a.CheckFire(self, this, facing, bombTarget);
            }

            // Guns only fire when approaching the target
            var facingToTarget = Util.GetFacing(target.CenterPosition - self.CenterPosition, facing.Facing);

            if (Math.Abs(facingToTarget - facing.Facing) % 256 > info.FacingTolerance)
            {
                return;
            }

            foreach (var a in Armaments.Where(a => a.Info.Name == info.Guns))
            {
                var range = new WRange((int)(1024 * a.Weapon.Range));
                if (!target.IsInRange(self.CenterPosition, range))
                {
                    continue;
                }

                var t = Target.FromPos(cp - new WVec(0, range.Range / 2, cp.Z).Rotate(WRot.FromFacing(facing.Facing)));
                a.CheckFire(self, this, facing, t);
            }
        }
Exemple #30
0
        public void Tick(Actor self)
        {
            var cp               = self.CenterPosition;
            var bombTarget       = Target.FromPos(cp - new WVec(0, 0, cp.Z));
            var wasInAttackRange = inAttackRange;
            var wasFacingTarget  = facingTarget;

            inAttackRange = false;

            var f = facing.Value.Facing;
            var facingToTarget = Util.GetFacing(target.CenterPosition - self.CenterPosition, f);

            facingTarget = Math.Abs(facingToTarget - f) % 256 <= info.FacingTolerance;

            // Bombs drop anywhere in range
            foreach (var a in Armaments.Where(a => a.Info.Name == info.Bombs))
            {
                if (!target.IsInRange(self.CenterPosition, a.Weapon.Range))
                {
                    continue;
                }

                inAttackRange = true;
                a.CheckFire(self, facing.Value, bombTarget);
            }

            // Guns only fire when approaching the target
            if (facingTarget)
            {
                foreach (var a in Armaments.Where(a => a.Info.Name == info.Guns))
                {
                    if (!target.IsInRange(self.CenterPosition, a.Weapon.Range))
                    {
                        continue;
                    }

                    var t = Target.FromPos(cp - new WVec(0, a.Weapon.Range.Length / 2, cp.Z).Rotate(WRot.FromFacing(f)));
                    inAttackRange = true;
                    a.CheckFire(self, facing.Value, t);
                }
            }

            // Actors without armaments may want to trigger an action when it passes the target
            if (!Armaments.Any())
            {
                inAttackRange = !wasInAttackRange && !facingTarget && wasFacingTarget;
            }

            if (inAttackRange && !wasInAttackRange)
            {
                OnEnteredAttackRange(self);
            }

            if (!inAttackRange && wasInAttackRange)
            {
                OnExitedAttackRange(self);
            }
        }