diff --git a/OpenRA.Game/PathFinder.cs b/OpenRA.Game/PathFinder.cs index 5cf8c0acc8..51530e91fd 100644 --- a/OpenRA.Game/PathFinder.cs +++ b/OpenRA.Game/PathFinder.cs @@ -35,7 +35,6 @@ namespace OpenRA public PathFinder( World world ) { this.world = world; - var map = world.Map; } class CachedPath diff --git a/OpenRA.Game/Traits/Activities/Move.cs b/OpenRA.Game/Traits/Activities/Move.cs index 2ce112741b..839a6982cb 100755 --- a/OpenRA.Game/Traits/Activities/Move.cs +++ b/OpenRA.Game/Traits/Activities/Move.cs @@ -306,6 +306,7 @@ namespace OpenRA.Traits.Activities { self.CenterLocation = Util.CenterOfCell( mobile.toCell ); mobile.fromCell = mobile.toCell; + mobile.FinishedMoving(self); return null; } } diff --git a/OpenRA.Game/Traits/Mobile.cs b/OpenRA.Game/Traits/Mobile.cs index 22b9bc27d1..2cdab4a6b5 100644 --- a/OpenRA.Game/Traits/Mobile.cs +++ b/OpenRA.Game/Traits/Mobile.cs @@ -30,6 +30,7 @@ namespace OpenRA.Traits { public readonly TerrainType[] TerrainTypes; public readonly float[] TerrainSpeeds; + public readonly string[] Crushes; public readonly int WaitAverage = 60; public readonly int WaitSpread = 20; @@ -39,6 +40,7 @@ namespace OpenRA.Traits public class Mobile : IIssueOrder, IResolveOrder, IOccupySpace, IMove { public readonly Actor self; + public readonly MobileInfo Info; public readonly Dictionary TerrainCost; public readonly Dictionary TerrainSpeed; @@ -67,6 +69,7 @@ namespace OpenRA.Traits public Mobile(ActorInitializer init, MobileInfo info) { this.self = init.self; + this.Info = info; this.__fromCell = this.__toCell = init.location; AddInfluence(); @@ -137,9 +140,19 @@ namespace OpenRA.Traits public virtual bool CanEnterCell(int2 cell, Actor ignoreActor, bool checkTransientActors) { - if (!self.World.WorldActor.traits.Get().CanMoveHere(cell, ignoreActor)) - return false; + // Check for buildings + var building = self.World.WorldActor.traits.Get().GetBuildingBlocking(cell); + if (building != null && building != ignoreActor) + { + var crushable = building.traits.WithInterface(); + if (crushable.Count() == 0) + return false; + + if (!crushable.Any(b => b.CrushClasses.Intersect(Info.Crushes).Any())) + return false; + } + // Check mobile actors if (checkTransientActors) { var canShare = self.traits.Contains(); @@ -150,16 +163,27 @@ namespace OpenRA.Traits // only allow 5 in a cell if (shareable.Count() >= 5) return false; - + // We can enter a cell with nonshareable units if we can crush all of them - if (nonshareable.Any( - a => !self.World.IsActorCrushableByActor(a, self))) + if (nonshareable.Any(a => !(a.traits.Contains() && + a.traits.WithInterface().Any(b => b.CrushClasses.Intersect(Info.Crushes).Any())))) return false; } return MovementCostForCell(self, cell) < float.PositiveInfinity; } + public virtual void FinishedMoving(Actor self) + { + var crushable = self.World.WorldActor.traits.Get().GetUnitsAt(self.Location).Where(a => a != self && a.traits.Contains()); + foreach (var a in crushable) + { + var crushActions = a.traits.WithInterface().Where(b => b.CrushClasses.Intersect(Info.Crushes).Any()); + foreach (var b in crushActions) + b.OnCrush(self); + } + } + public virtual float MovementCostForCell(Actor self, int2 cell) { if (!self.World.Map.IsInMap(cell.X,cell.Y)) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 251fb98f42..6242b253a2 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -115,8 +115,7 @@ namespace OpenRA.Traits public interface ICrushable { void OnCrush(Actor crusher); - bool IsCrushableBy(UnitMovementType umt, Player player); - bool IsPathableCrush(UnitMovementType umt, Player player); + IEnumerable CrushClasses { get; } } public struct Renderable diff --git a/OpenRA.Game/Traits/World/BuildingInfluence.cs b/OpenRA.Game/Traits/World/BuildingInfluence.cs index 21e7c28c55..a17e4d0ff8 100644 --- a/OpenRA.Game/Traits/World/BuildingInfluence.cs +++ b/OpenRA.Game/Traits/World/BuildingInfluence.cs @@ -65,6 +65,12 @@ namespace OpenRA.Traits if (!map.IsInMap(cell)) return null; return influence[cell.X, cell.Y]; } + + public Actor GetBuildingBlocking(int2 cell) + { + if (!map.IsInMap(cell) || !blocked[cell.X, cell.Y]) return null; + return influence[cell.X, cell.Y]; + } public bool CanMoveHere(int2 cell) { diff --git a/OpenRA.Game/Traits/World/HazardLayer.cs b/OpenRA.Game/Traits/World/HazardLayer.cs index 861d3068e3..e00f526aa0 100644 --- a/OpenRA.Game/Traits/World/HazardLayer.cs +++ b/OpenRA.Game/Traits/World/HazardLayer.cs @@ -38,7 +38,6 @@ namespace OpenRA.Traits public HazardLayer( World world ) { - map = world.Map; hazards = new List>[world.Map.MapSize.X, world.Map.MapSize.Y]; for (int i = 0; i < world.Map.MapSize.X; i++) for (int j = 0; j < world.Map.MapSize.Y; j++) diff --git a/OpenRA.Game/Traits/World/UnitInfluence.cs b/OpenRA.Game/Traits/World/UnitInfluence.cs index 1e0c3c0f55..f601e22933 100644 --- a/OpenRA.Game/Traits/World/UnitInfluence.cs +++ b/OpenRA.Game/Traits/World/UnitInfluence.cs @@ -49,27 +49,6 @@ namespace OpenRA.Traits public void Tick( Actor self ) { - // Does this belong here? NO, but it's your mess. - - // Get the crushable actors - foreach (var aa in self.World.Queries.WithTrait()) - { - var a = aa.Actor; - // Are there any units in the same cell that can crush this? - foreach( var ios in a.traits.WithInterface() ) - foreach( var cell in ios.OccupiedCells() ) - { - // There should only be one (counterexample: An infantry and a tank try to pick up a crate at the same time.) - // If there is more than one, do action on the first crusher - var crusher = GetUnitsAt(cell).Where(b => a != b && self.World.IsActorCrushableByActor(a, b)).FirstOrDefault(); - if (crusher != null) - { - // Apply the crush action - foreach (var crush in a.traits.WithInterface()) - crush.OnCrush(crusher); - } - } - } SanityCheck( self ); } diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 4682807132..ad023ae3e6 100755 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -49,29 +49,6 @@ namespace OpenRA .Buildable; } - public static bool IsActorCrushableByActor(this World world, Actor a, Actor b) - { - return false; - //var movement = b.traits.GetOrDefault(); - //return movement != null && world.IsActorCrushableByMovementType(a, movement.GetMovementType()); - } - - // Todo: Reenable crushing based on actor, not umt - /* - public static bool IsActorPathableToCrush(this World world, Actor a, UnitMovementType umt) - { - return a != null && - a.traits.WithInterface() - .Any(c => c.IsPathableCrush(umt, a.Owner)); - } - - public static bool IsActorCrushableByMovementType(this World world, Actor a, UnitMovementType umt) - { - return a != null && - a.traits.WithInterface() - .Any(c => c.IsCrushableBy(umt, a.Owner)); - } - */ public static IEnumerable FindUnitsAtMouse(this World world, int2 mouseLocation) { var loc = mouseLocation + Game.viewport.Location; diff --git a/OpenRA.Mods.Cnc/MobileAir.cs b/OpenRA.Mods.Cnc/MobileAir.cs index a056623aa2..4468039941 100644 --- a/OpenRA.Mods.Cnc/MobileAir.cs +++ b/OpenRA.Mods.Cnc/MobileAir.cs @@ -37,11 +37,11 @@ namespace OpenRA.Traits public class MobileAir : Mobile, ITick, IOccupyAir { - MobileAirInfo Info; + MobileAirInfo AirInfo; public MobileAir (ActorInitializer init, MobileAirInfo info) : base(init, info) { - Info = info; + AirInfo = info; } public override void AddInfluence() @@ -62,7 +62,9 @@ namespace OpenRA.Traits return self.World.WorldActor.traits.Get().GetUnitsAt(p).Count() == 0; } - public virtual float MovementCostForCell(Actor self, int2 cell) + public override void FinishedMoving(Actor self) {} + + public override float MovementCostForCell(Actor self, int2 cell) { if (!self.World.Map.IsInMap(cell.X,cell.Y)) return float.PositiveInfinity; @@ -73,7 +75,7 @@ namespace OpenRA.Traits return additionalCost; } - public virtual float MovementSpeedForCell(Actor self, int2 cell) + public override float MovementSpeedForCell(Actor self, int2 cell) { var unitInfo = self.Info.Traits.GetOrDefault(); if( unitInfo == null ) @@ -108,14 +110,14 @@ namespace OpenRA.Traits //if (unit.Altitude <= 0) // return; - if (unit.Altitude < Info.CruiseAltitude) + if (unit.Altitude < AirInfo.CruiseAltitude) unit.Altitude++; if (--offsetTicks <= 0) { - self.CenterLocation += Info.InstabilityMagnitude * self.World.SharedRandom.Gauss2D(5); - unit.Altitude += (int)(Info.InstabilityMagnitude * self.World.SharedRandom.Gauss1D(5)); - offsetTicks = Info.InstabilityTicks; + self.CenterLocation += AirInfo.InstabilityMagnitude * self.World.SharedRandom.Gauss2D(5); + unit.Altitude += (int)(AirInfo.InstabilityMagnitude * self.World.SharedRandom.Gauss1D(5)); + offsetTicks = AirInfo.InstabilityTicks; } } } diff --git a/OpenRA.Mods.RA/Activities/HeliAttack.cs b/OpenRA.Mods.RA/Activities/HeliAttack.cs index 4f636d2148..c5db812617 100644 --- a/OpenRA.Mods.RA/Activities/HeliAttack.cs +++ b/OpenRA.Mods.RA/Activities/HeliAttack.cs @@ -44,8 +44,6 @@ namespace OpenRA.Mods.RA.Activities return NextActivity; } var unit = self.traits.Get(); - var aircraft = self.traits.Get(); - var info = self.Info.Traits.Get(); if (unit.Altitude != info.CruiseAltitude) { diff --git a/OpenRA.Mods.RA/Mine.cs b/OpenRA.Mods.RA/Mine.cs index 6e3951cdb3..a99fddba9f 100644 --- a/OpenRA.Mods.RA/Mine.cs +++ b/OpenRA.Mods.RA/Mine.cs @@ -28,22 +28,24 @@ namespace OpenRA.Mods.RA { class MineInfo : ITraitInfo { - public readonly UnitMovementType[] TriggeredBy = { }; + public readonly string[] CrushClasses = { }; public readonly string Weapon = "ATMine"; public readonly bool AvoidFriendly = true; - public object Create(ActorInitializer init) { return new Mine(init); } + public object Create(ActorInitializer init) { return new Mine(init, this); } } class Mine : ICrushable, IOccupySpace { readonly Actor self; + readonly MineInfo info; [Sync] readonly int2 location; - public Mine(ActorInitializer init) + public Mine(ActorInitializer init, MineInfo info) { this.self = init.self; + this.info = info; this.location = init.location; self.World.WorldActor.traits.Get().Add(self, this); } @@ -57,17 +59,10 @@ namespace OpenRA.Mods.RA Combat.DoExplosion(self, info.Weapon, crusher.CenterLocation.ToInt2(), 0); self.QueueActivity(new RemoveSelf()); } - - public bool IsPathableCrush(UnitMovementType umt, Player player) - { - return !self.Info.Traits.Get().AvoidFriendly || (player != self.Owner); - } - - public bool IsCrushableBy(UnitMovementType umt, Player player) - { - return self.Info.Traits.Get().TriggeredBy.Contains(umt); - } - + + // TODO: Re-implement friendly-mine avoidance using a Hazard + public IEnumerable CrushClasses { get { return info.CrushClasses; } } + public int2 TopLeft { get { return location; } } public IEnumerable OccupiedCells() { yield return TopLeft; } diff --git a/OpenRA.Mods.RA/Wall.cs b/OpenRA.Mods.RA/Wall.cs index a85a08b7b3..951d2ef1a4 100644 --- a/OpenRA.Mods.RA/Wall.cs +++ b/OpenRA.Mods.RA/Wall.cs @@ -27,30 +27,24 @@ namespace OpenRA.Mods.RA { public class WallInfo : ITraitInfo { - public readonly UnitMovementType[] CrushableBy = { }; + public readonly string[] CrushClasses = { }; - public object Create(ActorInitializer init) { return new Wall(init.self); } + public object Create(ActorInitializer init) { return new Wall(init.self, this); } } public class Wall : ICrushable, IBlocksBullets { readonly Actor self; - - public Wall(Actor self) + readonly WallInfo info; + + public Wall(Actor self, WallInfo info) { this.self = self; + this.info = info; self.World.WorldActor.traits.Get().Add(self, self.traits.Get()); } - + + public IEnumerable CrushClasses { get { return info.CrushClasses; } } public void OnCrush(Actor crusher) { self.InflictDamage(crusher, self.Health, null); } - public bool IsCrushableBy(UnitMovementType umt, Player player) - { - return self.Info.Traits.Get().CrushableBy.Contains(umt); - } - - public bool IsPathableCrush(UnitMovementType umt, Player player) - { - return IsCrushableBy(umt, player); - } } } diff --git a/mods/cnc/defaults.yaml b/mods/cnc/defaults.yaml index 43bb721679..883c8fc459 100644 --- a/mods/cnc/defaults.yaml +++ b/mods/cnc/defaults.yaml @@ -1,5 +1,3 @@ - -### Todo: Need a ^Tank default for MovementType: Track ^Vehicle: Category: Vehicle Unit: @@ -23,6 +21,7 @@ Unit: ROT: 5 Mobile: + Crushes: wall TerrainTypes: Clear, Rough, Road, Tree, Water, Rock, Wall, Ore, Beach, River, Special TerrainSpeeds: 80%, 70%, 100%, 0%, 0%, 0%, 0%, 70%, 70%, 0%, 100% Selectable: @@ -112,6 +111,7 @@ DamagedSound: xplos.aud DestroyedSound: xplobig4.aud Wall: + CrushClasses: wall LineBuild: Selectable: Priority: 1 diff --git a/mods/cnc/structures.yaml b/mods/cnc/structures.yaml index b28fef0d1e..1741e07d10 100644 --- a/mods/cnc/structures.yaml +++ b/mods/cnc/structures.yaml @@ -465,6 +465,8 @@ BRIK: Armor: none RenderBuildingWall: DamageStates: 4 + Wall: + CrushClasses: heavywall GUN: Category: Defense diff --git a/mods/cnc/system.yaml b/mods/cnc/system.yaml index 52904f4d2e..2f4475180b 100644 --- a/mods/cnc/system.yaml +++ b/mods/cnc/system.yaml @@ -209,7 +209,6 @@ World: ResourceLayer: ResourceType@green-tib: ResourceType: 1 - MovementTerrainType: Tiberium Palette: terrain SpriteNames: ti1,ti2,ti3,ti4,ti5,ti6,ti7,ti8,ti9,ti10,ti11,ti12 ValuePerUnit: 30 diff --git a/mods/cnc/vehicles.yaml b/mods/cnc/vehicles.yaml index a438d12026..87e69cb493 100644 --- a/mods/cnc/vehicles.yaml +++ b/mods/cnc/vehicles.yaml @@ -294,6 +294,8 @@ HTNK: Explodes: Weapon: UnitExplodeSmall EmptyWeapon: UnitExplodeSmall + Mobile: + Crushes: wall, heavywall MSAM: Inherits: ^Tank