diff --git a/OpenRA.Mods.D2k/AttackSwallow.cs b/OpenRA.Mods.D2k/AttackSwallow.cs index a5c56bc4bf..0732f0fa9a 100644 --- a/OpenRA.Mods.D2k/AttackSwallow.cs +++ b/OpenRA.Mods.D2k/AttackSwallow.cs @@ -17,9 +17,10 @@ namespace OpenRA.Mods.D2k class AttackSwallowInfo : AttackFrontalInfo { [Desc("The number of ticks it takes to return underground.")] - public int ReturnTime = 60; + public readonly int ReturnTime = 60; + [Desc("The number of ticks it takes to get in place under the target to attack.")] - public int AttackTime = 30; + public readonly int AttackTime = 30; public readonly string WormAttackNotification = "WormAttack"; @@ -28,13 +29,8 @@ namespace OpenRA.Mods.D2k class AttackSwallow : AttackFrontal { - new public readonly AttackSwallowInfo Info; - public AttackSwallow(Actor self, AttackSwallowInfo info) - : base(self, info) - { - Info = info; - } + : base(self, info) { } public override void DoAttack(Actor self, Target target) { diff --git a/OpenRA.Mods.D2k/SwallowActor.cs b/OpenRA.Mods.D2k/SwallowActor.cs index db2c49baea..e581fbb571 100644 --- a/OpenRA.Mods.D2k/SwallowActor.cs +++ b/OpenRA.Mods.D2k/SwallowActor.cs @@ -30,6 +30,7 @@ namespace OpenRA.Mods.D2k readonly RenderUnit renderUnit; readonly RadarPings radarPings; readonly AttackSwallow swallow; + readonly AttackSwallowInfo swallowInfo; readonly IPositionable positionable; int countdown; @@ -41,9 +42,10 @@ namespace OpenRA.Mods.D2k this.weapon = weapon; positionable = self.Trait(); swallow = self.Trait(); + swallowInfo = (AttackSwallowInfo)swallow.Info; renderUnit = self.Trait(); radarPings = self.World.WorldActor.TraitOrDefault(); - countdown = swallow.Info.AttackTime; + countdown = swallowInfo.AttackTime; renderUnit.DefaultAnimation.ReplaceAnim("burrowed"); stance = AttackState.Burrowed; @@ -53,12 +55,14 @@ namespace OpenRA.Mods.D2k bool WormAttack(Actor worm) { var targetLocation = target.Actor.Location; + // The target has moved too far away if ((location - targetLocation).Length > NearEnough) return false; var lunch = worm.World.ActorMap.GetUnitsAt(targetLocation) .Where(t => !t.Equals(worm) && weapon.IsValidAgainst(t, worm)); + if (!lunch.Any()) return false; @@ -86,7 +90,7 @@ namespace OpenRA.Mods.D2k void NotifyPlayer(Player player, WPos location) { - Sound.PlayNotification(player.World.Map.Rules, player, "Speech", swallow.Info.WormAttackNotification, player.Country.Race); + Sound.PlayNotification(player.World.Map.Rules, player, "Speech", swallowInfo.WormAttackNotification, player.Country.Race); radarPings.Add(() => true, location, Color.Red, 50); } @@ -98,37 +102,39 @@ namespace OpenRA.Mods.D2k return this; } - if (stance == AttackState.ReturningUnderground) // Wait for the worm to get back underground + // Wait for the worm to get back underground + if (stance == AttackState.ReturningUnderground) { - if (self.World.SharedRandom.Next() % 2 == 0) // There is a 50-50 chance that the worm would just go away + // There is a 50-50 chance that the worm would just go away + if (self.World.SharedRandom.Next() % 2 == 0) { self.CancelActivity(); self.World.AddFrameEndTask(w => w.Remove(self)); + var wormManager = self.World.WorldActor.TraitOrDefault(); if (wormManager != null) wormManager.DecreaseWorms(); } else - { renderUnit.DefaultAnimation.ReplaceAnim("idle"); - } + return NextActivity; } - if (stance == AttackState.Burrowed) // Wait for the worm to get in position + // Wait for the worm to get in position + if (stance == AttackState.Burrowed) { // This is so that the worm cancels an attack against a target that has reached solid rock if (!positionable.CanEnterCell(target.Actor.Location, null, false)) return NextActivity; - var success = WormAttack(self); - if (!success) + if (!WormAttack(self)) { renderUnit.DefaultAnimation.ReplaceAnim("idle"); return NextActivity; } - countdown = swallow.Info.ReturnTime; + countdown = swallowInfo.ReturnTime; stance = AttackState.ReturningUnderground; } diff --git a/OpenRA.Mods.D2k/WormManager.cs b/OpenRA.Mods.D2k/WormManager.cs index 867d68e5c8..deb74aca5c 100644 --- a/OpenRA.Mods.D2k/WormManager.cs +++ b/OpenRA.Mods.D2k/WormManager.cs @@ -40,12 +40,13 @@ namespace OpenRA.Mods.D2k class WormManager : ITick { - int countdown; - int wormsPresent; readonly WormManagerInfo info; readonly Lazy spawnPoints; readonly Lazy radarPings; + int countdown; + int wormsPresent; + public WormManager(WormManagerInfo info, Actor self) { this.info = info; @@ -72,9 +73,12 @@ namespace OpenRA.Mods.D2k var wormLocations = new List(); - wormLocations.Add(SpawnWorm(self)); - while (wormsPresent < info.Minimum) + do + { + // Always spawn at least one worm, plus however many + // more we need to reach the defined minimum count. wormLocations.Add(SpawnWorm(self)); + } while (wormsPresent < info.Minimum); AnnounceWormSign(self, wormLocations); } @@ -87,6 +91,7 @@ namespace OpenRA.Mods.D2k new OwnerInit(w.Players.First(x => x.PlayerName == info.WormOwnerPlayer)), new LocationInit(spawnPoint.Location) })); + wormsPresent++; return spawnPoint.CenterPosition; @@ -112,7 +117,6 @@ namespace OpenRA.Mods.D2k foreach (var wormLocation in wormLocations) radarPings.Value.Add(() => true, wormLocation, Color.Red, 50); - } } diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index 89ba53efc7..a948404b8c 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -35,14 +35,15 @@ namespace OpenRA.Mods.RA public abstract class AttackBase : IIssueOrder, IResolveOrder, IOrderVoice, ISync { [Sync] public bool IsAttacking { get; internal set; } - public IEnumerable Armaments { get { return GetArmaments(); } } + public IEnumerable Armaments { get { return getArmaments(); } } + public readonly AttackBaseInfo Info; + protected Lazy facing; protected Lazy building; - protected Lazy positionable; - protected Func> GetArmaments; + protected Lazy positionable; + protected Func> getArmaments; readonly Actor self; - public readonly AttackBaseInfo Info; public AttackBase(Actor self, AttackBaseInfo info) { @@ -52,7 +53,7 @@ namespace OpenRA.Mods.RA var armaments = Exts.Lazy(() => self.TraitsImplementing() .Where(a => info.Armaments.Contains(a.Info.Name))); - GetArmaments = () => armaments.Value; + getArmaments = () => armaments.Value; facing = Exts.Lazy(() => self.TraitOrDefault()); building = Exts.Lazy(() => self.TraitOrDefault()); @@ -145,11 +146,8 @@ namespace OpenRA.Mods.RA public bool HasAnyValidWeapons(Target t) { - if (Info.AttackRequiresEnteringCell) - { - if (!positionable.Value.CanEnterCell(t.Actor.Location, null, false)) - return false; - } + if (Info.AttackRequiresEnteringCell && !positionable.Value.CanEnterCell(t.Actor.Location, null, false)) + return false; return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World, self)); } diff --git a/OpenRA.Mods.RA/Attack/AttackGarrisoned.cs b/OpenRA.Mods.RA/Attack/AttackGarrisoned.cs index e1c25ee926..3d7a701a34 100644 --- a/OpenRA.Mods.RA/Attack/AttackGarrisoned.cs +++ b/OpenRA.Mods.RA/Attack/AttackGarrisoned.cs @@ -29,13 +29,13 @@ namespace OpenRA.Mods.RA public class AttackGarrisonedInfo : AttackFollowInfo, Requires { [Desc("Fire port offsets in local coordinates.")] - public readonly WVec[] PortOffsets = {}; + public readonly WVec[] PortOffsets = { }; [Desc("Fire port yaw angles.")] - public readonly WAngle[] PortYaws = {}; + public readonly WAngle[] PortYaws = { }; [Desc("Fire port yaw cone angle.")] - public readonly WAngle[] PortCones = {}; + public readonly WAngle[] PortCones = { }; public readonly string MuzzlePalette = "effect"; @@ -54,7 +54,6 @@ namespace OpenRA.Mods.RA Dictionary paxPos; Dictionary paxRender; - public AttackGarrisoned(Actor self, AttackGarrisonedInfo info) : base(self, info) { @@ -66,8 +65,7 @@ namespace OpenRA.Mods.RA paxPos = new Dictionary(); paxRender = new Dictionary(); - GetArmaments = () => armaments; - + getArmaments = () => armaments; if (info.PortOffsets.Length == 0) throw new InvalidOperationException("PortOffsets must have at least one entry."); @@ -110,7 +108,6 @@ namespace OpenRA.Mods.RA armaments.RemoveAll(a => a.Actor == passenger); } - FirePort SelectFirePort(Actor self, WAngle targetYaw) { // Pick a random port that faces the target @@ -171,6 +168,7 @@ namespace OpenRA.Mods.RA muzzles.Add(muzzleFlash); muzzleAnim.PlayThen(sequence, () => muzzles.Remove(muzzleFlash)); } + foreach (var npa in self.TraitsImplementing()) npa.Attacking(self, target, a, barrel); } @@ -179,6 +177,7 @@ namespace OpenRA.Mods.RA public IEnumerable Render(Actor self, WorldRenderer wr) { var pal = wr.Palette(info.MuzzlePalette); + // Display muzzle flashes foreach (var m in muzzles) foreach (var r in m.Render(self, wr, pal, 1f)) diff --git a/OpenRA.Mods.RA/Attack/AttackWander.cs b/OpenRA.Mods.RA/Attack/AttackWander.cs index b9174fe3e2..e3c4f4b2ce 100644 --- a/OpenRA.Mods.RA/Attack/AttackWander.cs +++ b/OpenRA.Mods.RA/Attack/AttackWander.cs @@ -27,14 +27,15 @@ namespace OpenRA.Mods.RA class AttackWander : INotifyIdle { + readonly AttackMove attackMove; + readonly AttackWanderInfo info; + int ticksIdle; int effectiveMoveRadius; - readonly AttackMove attackMove; - readonly AttackWanderInfo Info; public AttackWander(Actor self, AttackWanderInfo info) { - Info = info; + this.info = info; effectiveMoveRadius = info.WanderMoveRadius; attackMove = self.TraitOrDefault(); } @@ -47,16 +48,16 @@ namespace OpenRA.Mods.RA 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.MoveReductionRadiusScale == 0) + if (++ticksIdle % info.MoveReductionRadiusScale == 0) effectiveMoveRadius--; - return; // We'll be back the next tick; better to sit idle for a few seconds than prolongue this tick indefinitely with a loop + return; // We'll be back the next tick; better to sit idle for a few seconds than prolong this tick indefinitely with a loop } attackMove.ResolveOrder(self, new Order("AttackMove", self, false) { TargetLocation = targetCell }); ticksIdle = 0; - effectiveMoveRadius = Info.WanderMoveRadius; + effectiveMoveRadius = info.WanderMoveRadius; } } }