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; }
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; } }
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())); }
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); }
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()); }
public Land(Actor self, WAngle?facing = null, Color?targetLineColor = null) : this(self, Target.Invalid, new WDist(-1), WVec.Zero, facing, null) { assignTargetOnFirstRun = true; }
public Land(Actor self, Target target, WVec offset, WAngle?facing = null, Color?targetLineColor = null) : this(self, target, WDist.Zero, offset, facing, targetLineColor : targetLineColor) { }
public Land(Actor self, Target target, WDist landRange, WAngle?facing = null, Color?targetLineColor = null) : this(self, target, landRange, WVec.Zero, facing, targetLineColor : targetLineColor) { }
public Actor[] TargetParatroopers(WPos target, WAngle?facing = null) { var actors = pp.SendParatroopers(Self, target, facing); return(actors.First); }
public Actor[] TargetAirstrike(WPos target, WAngle?facing = null) { return(ap.SendAirstrike(Self, target, facing)); }
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()); }