예제 #1
0
 public Drag(Actor self, WPos start, WPos end, int length, WAngle?facing = null)
 {
     positionable    = self.Trait <IPositionable>();
     disableable     = self.TraitOrDefault <IMove>() as IDisabledTrait;
     this.start      = start;
     this.end        = end;
     this.length     = length;
     desiredFacing   = facing;
     IsInterruptible = false;
 }
예제 #2
0
        public Land(Actor self, Target target, WDist landRange, WVec offset, WAngle?facing = null, CPos[] clearCells = null, Color?targetLineColor = null)
        {
            aircraft             = self.Trait <Aircraft>();
            this.target          = target;
            this.offset          = offset;
            this.clearCells      = clearCells ?? new CPos[0];
            this.landRange       = landRange.Length >= 0 ? landRange : aircraft.Info.LandRange;
            this.targetLineColor = targetLineColor;

            // NOTE: desiredFacing = -1 means we should not prefer any particular facing and instead just
            // use whatever facing gives us the most direct path to the landing site.
            if (!facing.HasValue && aircraft.Info.TurnToLand)
            {
                desiredFacing = WAngle.FromFacing(aircraft.Info.InitialFacing);
            }
            else
            {
                desiredFacing = facing;
            }
        }
예제 #3
0
        public Pair <Actor[], Actor[]> SendParatroopers(Actor self, WPos target, WAngle?facing = null)
        {
            var aircraft = new List <Actor>();
            var units    = new List <Actor>();

            var info = Info as ParatroopersPowerInfo;

            if (!facing.HasValue)
            {
                facing = new WAngle(1024 * 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.FromYaw(facing.Value);
            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 the range.
                // If at the map's edge, they may be removed from world before leaving.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    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.Value),
                }));
            }

            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()));
        }
예제 #4
0
        public override bool Tick(Actor self)
        {
            // Refuse to take off if it would land immediately again.
            // Special case: Don't kill other deploy hotkey activities.
            if (aircraft.ForceLanding)
            {
                return(true);
            }

            if (IsCanceling || self.IsDead)
            {
                return(true);
            }

            if (dest == null || dest.IsDead || !Reservable.IsAvailableFor(dest, self))
            {
                dest = ChooseResupplier(self, true);
            }

            if (dest == null)
            {
                var nearestResupplier = ChooseResupplier(self, false);

                if (nearestResupplier != null)
                {
                    if (aircraft.Info.CanHover)
                    {
                        var distanceFromResupplier = (nearestResupplier.CenterPosition - self.CenterPosition).HorizontalLength;
                        var distanceLength         = aircraft.Info.WaitDistanceFromResupplyBase.Length;

                        // If no pad is available, move near one and wait
                        if (distanceFromResupplier > distanceLength)
                        {
                            var randomPosition = WVec.FromPDF(self.World.SharedRandom, 2) * distanceLength / 1024;
                            var target         = Target.FromPos(nearestResupplier.CenterPosition + randomPosition);

                            QueueChild(new Fly(self, target, WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green));
                        }

                        return(false);
                    }

                    QueueChild(new Fly(self, Target.FromActor(nearestResupplier), WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase, targetLineColor: Color.Green));
                    QueueChild(new FlyIdle(self, aircraft.Info.NumberOfTicksToVerifyAvailableAirport));
                    return(false);
                }

                // Prevent an infinite loop in case we'd return to the activity that called ReturnToBase in the first place. Go idle instead.
                self.CancelActivity();
                return(true);
            }

            if (ShouldLandAtBuilding(self, dest))
            {
                var exit   = dest.FirstExitOrDefault();
                var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero;
                if (aircraft.Info.TurnToDock || !aircraft.Info.VTOL)
                {
                    facing = WAngle.FromFacing(aircraft.Info.InitialFacing);
                }

                aircraft.MakeReservation(dest);
                QueueChild(new Land(self, Target.FromActor(dest), offset, facing, Color.Green));
                QueueChild(new Resupply(self, dest, WDist.Zero, alwaysLand));
                return(true);
            }

            QueueChild(new Fly(self, Target.FromActor(dest), targetLineColor: Color.Green));
            return(true);
        }
예제 #5
0
        public Actor[] SendAirstrike(Actor self, WPos target, WAngle?facing = null)
        {
            var aircraft = new List <Actor>();

            if (!facing.HasValue)
            {
                facing = new WAngle(1024 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings);
            }

            var altitude       = self.World.Map.Rules.Actors[info.UnitType].TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var attackRotation = WRot.FromYaw(facing.Value);
            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 the range.
                // If at the map's edge, they may be removed from world before leaving.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    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(attackRotation);
                var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);
                var a            = self.World.CreateActor(false, info.UnitType, new TypeDictionary
                {
                    new CenterPositionInit(startEdge + spawnOffset),
                    new OwnerInit(self.Owner),
                    new FacingInit(facing.Value),
                });

                aircraft.Add(a);
                aircraftInRange.Add(a, false);

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

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

                var j = 0;
                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 a = aircraft[j++];
                    w.Add(a);

                    a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    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);
                }
            });

            return(aircraft.ToArray());
        }
예제 #6
0
 public Land(Actor self, WAngle?facing = null, Color?targetLineColor = null)
     : this(self, Target.Invalid, new WDist(-1), WVec.Zero, facing, null)
 {
     assignTargetOnFirstRun = true;
 }
예제 #7
0
 public Land(Actor self, Target target, WVec offset, WAngle?facing = null, Color?targetLineColor = null)
     : this(self, target, WDist.Zero, offset, facing, targetLineColor : targetLineColor)
 {
 }
예제 #8
0
 public Land(Actor self, Target target, WDist landRange, WAngle?facing = null, Color?targetLineColor = null)
     : this(self, target, landRange, WVec.Zero, facing, targetLineColor : targetLineColor)
 {
 }
예제 #9
0
        public Actor[] TargetParatroopers(WPos target, WAngle?facing = null)
        {
            var actors = pp.SendParatroopers(Self, target, facing);

            return(actors.First);
        }
예제 #10
0
 public Actor[] TargetAirstrike(WPos target, WAngle?facing = null)
 {
     return(ap.SendAirstrike(Self, target, facing));
 }
예제 #11
0
        public Actor[] SendAirstrike(Actor self, WPos target, WAngle?facing = null)
        {
            var aircraft = new List <Actor>();

            if (!facing.HasValue)
            {
                facing = new WAngle(1024 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings);
            }

            var altitude       = self.World.Map.Rules.Actors[info.UnitTypes.First(ut => ut.Key == GetLevel()).Value].TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var attackRotation = WRot.FromYaw(facing.Value);
            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;
            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),
                        });
                    });
                }

                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 the range.
                // If at the map's edge, they may be removed from world before leaving.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    RemoveCamera(camera);
                }
            };

            // Create the actors immediately so they can be returned
            var squadSize = info.SquadSizes.First(ss => ss.Key == GetLevel()).Value;

            for (var i = -squadSize / 2; i <= squadSize / 2; i++)
            {
                // Even-sized squads skip the lead plane
                if (i == 0 && (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            = self.World.CreateActor(false, info.UnitTypes.First(ut => ut.Key == GetLevel()).Value, new TypeDictionary
                {
                    new CenterPositionInit(startEdge + spawnOffset),
                    new OwnerInit(self.Owner),
                    new FacingInit(facing.Value),
                });

                aircraft.Add(a);
                aircraftInRange.Add(a, false);

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

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

                var effect = new AirstrikePowerRVEffect(self.World, self.Owner, target, startEdge, finishEdge, attackRotation, altitude, GetLevel(), aircraft.ToArray(), this, info);
                self.World.Add(effect);
            });

            return(aircraft.ToArray());
        }