diff --git a/OpenRA.Mods.Common/Activities/FindAndDeliverResources.cs b/OpenRA.Mods.Common/Activities/FindAndDeliverResources.cs index 23344d9901..9c1a8df189 100644 --- a/OpenRA.Mods.Common/Activities/FindAndDeliverResources.cs +++ b/OpenRA.Mods.Common/Activities/FindAndDeliverResources.cs @@ -24,7 +24,6 @@ namespace OpenRA.Mods.Common.Activities readonly Harvester harv; readonly HarvesterInfo harvInfo; readonly Mobile mobile; - readonly LocomotorInfo locomotorInfo; readonly ResourceClaimLayer claimLayer; readonly IPathFinder pathFinder; readonly DomainIndex domainIndex; @@ -43,7 +42,6 @@ namespace OpenRA.Mods.Common.Activities harv = self.Trait(); harvInfo = self.Info.TraitInfo(); mobile = self.Trait(); - locomotorInfo = mobile.Info.LocomotorInfo; claimLayer = self.World.WorldActor.Trait(); pathFinder = self.World.WorldActor.Trait(); domainIndex = self.World.WorldActor.Trait(); @@ -195,7 +193,7 @@ namespace OpenRA.Mods.Common.Activities // Find any harvestable resources: List path; using (var search = PathSearch.Search(self.World, mobile.Locomotor, self, BlockedByActor.Stationary, loc => - domainIndex.IsPassable(self.Location, loc, locomotorInfo) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) + domainIndex.IsPassable(self.Location, loc, mobile.Locomotor) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc)) .WithCustomCost(loc => { if ((loc - searchFromLoc.Value).LengthSquared > searchRadiusSquared) diff --git a/OpenRA.Mods.Common/Activities/Move/MoveAdjacentTo.cs b/OpenRA.Mods.Common/Activities/Move/MoveAdjacentTo.cs index 9c056ce8d3..e6c25af018 100644 --- a/OpenRA.Mods.Common/Activities/Move/MoveAdjacentTo.cs +++ b/OpenRA.Mods.Common/Activities/Move/MoveAdjacentTo.cs @@ -133,7 +133,7 @@ namespace OpenRA.Mods.Common.Activities searchCells.Clear(); searchCellsTick = self.World.WorldTick; foreach (var cell in CandidateMovementCells(self)) - if (domainIndex.IsPassable(loc, cell, Mobile.Info.LocomotorInfo) && Mobile.CanEnterCell(cell)) + if (domainIndex.IsPassable(loc, cell, Mobile.Locomotor) && Mobile.CanEnterCell(cell)) searchCells.Add(cell); } diff --git a/OpenRA.Mods.Common/Pathfinder/PathGraph.cs b/OpenRA.Mods.Common/Pathfinder/PathGraph.cs index f6937bd38a..175a8d06c0 100644 --- a/OpenRA.Mods.Common/Pathfinder/PathGraph.cs +++ b/OpenRA.Mods.Common/Pathfinder/PathGraph.cs @@ -87,7 +87,6 @@ namespace OpenRA.Mods.Common.Pathfinder readonly BlockedByActor checkConditions; readonly Locomotor locomotor; - readonly LocomotorInfo.WorldMovementInfo worldMovementInfo; readonly CellInfoLayerPool.PooledCellInfoLayer pooledLayer; readonly bool checkTerrainHeight; CellLayer groundInfo; @@ -108,7 +107,6 @@ namespace OpenRA.Mods.Common.Pathfinder customLayerInfo[cml.Index] = (cml, pooledLayer.GetLayer()); World = world; - worldMovementInfo = locomotorInfo.GetWorldMovementInfo(world); Actor = actor; LaneBias = 1; checkConditions = check; diff --git a/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs index b1e9fe23f9..902d78ac0e 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs @@ -145,7 +145,7 @@ namespace OpenRA.Mods.Common.Traits Target FindNextResource(Actor actor, HarvesterTraitWrapper harv) { Func isValidResource = cell => - domainIndex.IsPassable(actor.Location, cell, harv.Locomotor.Info) && + domainIndex.IsPassable(actor.Location, cell, harv.Locomotor) && harv.Harvester.CanHarvestCell(actor, cell) && claimLayer.CanClaimCell(actor, cell); diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs index ad37b91b13..637f6b6add 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs @@ -29,11 +29,11 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads // (Way better than finding a nearest target which is likely to be on Ground) // You might be tempted to move these lookups into Activate() but that causes null reference exception. var domainIndex = first.World.WorldActor.Trait(); - var locomotorInfo = first.Info.TraitInfo().LocomotorInfo; + var locomotor = first.Trait().Locomotor; var navalProductions = owner.World.ActorsHavingTrait().Where(a => owner.SquadManager.Info.NavalProductionTypes.Contains(a.Info.Name) - && domainIndex.IsPassable(first.Location, a.Location, locomotorInfo) + && domainIndex.IsPassable(first.Location, a.Location, locomotor) && a.AppearsHostileTo(first)); if (navalProductions.Any()) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index bc0ef44e7f..2f7f2a24ca 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -725,16 +725,7 @@ namespace OpenRA.Mods.Common.Traits public int MovementSpeedForCell(Actor self, CPos cell) { - var index = cell.Layer == 0 ? self.World.Map.GetTerrainIndex(cell) : - self.World.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell); - - if (index == byte.MaxValue) - return 0; - - var terrainSpeed = Info.LocomotorInfo.TilesetTerrainInfo[self.World.Map.Rules.TileSet][index].Speed; - if (terrainSpeed == 0) - return 0; - + var terrainSpeed = Locomotor.MovementSpeedForCell(cell); var modifiers = speedModifiers.Value.Append(terrainSpeed); return Util.ApplyPercentageModifiers(Info.Speed, modifiers); diff --git a/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs b/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs index 011d79d5be..2c194032ad 100644 --- a/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Primitives; using OpenRA.Traits; @@ -62,9 +63,9 @@ namespace OpenRA.Mods.Common.Traits if (mobileInfo != null) { - var locomotorInfo = mobileInfo.LocomotorInfo; + var locomotor = self.World.WorldActor.TraitsImplementing().First(l => l.Info.Name == mobileInfo.Locomotor); location = self.World.Map.ChooseClosestMatchingEdgeCell(self.Location, - c => mobileInfo.CanEnterCell(self.World, null, c) && domainIndex.IsPassable(c, destinations[0], locomotorInfo)); + c => mobileInfo.CanEnterCell(self.World, null, c) && domainIndex.IsPassable(c, destinations[0], locomotor)); } } diff --git a/OpenRA.Mods.Common/Traits/World/DomainIndex.cs b/OpenRA.Mods.Common/Traits/World/DomainIndex.cs index c299fd9b7b..5856dcce34 100644 --- a/OpenRA.Mods.Common/Traits/World/DomainIndex.cs +++ b/OpenRA.Mods.Common/Traits/World/DomainIndex.cs @@ -31,24 +31,23 @@ namespace OpenRA.Mods.Common.Traits domainIndexes = new Dictionary(); tileSet = world.Map.Rules.TileSet; var locomotors = world.WorldActor.TraitsImplementing().Where(l => !string.IsNullOrEmpty(l.Info.Name)); - var movementClasses = locomotors.Select(t => (uint)t.Info.GetMovementClass(tileSet)).Distinct(); + var movementClasses = locomotors.Select(t => t.MovementClass).Distinct(); foreach (var mc in movementClasses) domainIndexes[mc] = new MovementClassDomainIndex(world, mc); } - public bool IsPassable(CPos p1, CPos p2, LocomotorInfo li) + public bool IsPassable(CPos p1, CPos p2, Locomotor locomotor) { // HACK: Work around units in other movement layers from being blocked // when the point in the main layer is not pathable if (p1.Layer != 0 || p2.Layer != 0) return true; - if (li.DisableDomainPassabilityCheck) + if (locomotor.Info.DisableDomainPassabilityCheck) return true; - var movementClass = li.GetMovementClass(tileSet); - return domainIndexes[movementClass].IsPassable(p1, p2); + return domainIndexes[locomotor.MovementClass].IsPassable(p1, p2); } /// Regenerate the domain index for a group of cells. diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index bfe2239a46..0fdab33f13 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -99,21 +99,6 @@ namespace OpenRA.Mods.Common.Traits return ret; } - TerrainInfo[] LoadTilesetSpeeds(TileSet tileSet) - { - var info = new TerrainInfo[tileSet.TerrainInfo.Length]; - for (var i = 0; i < info.Length; i++) - info[i] = TerrainInfo.Impassable; - - foreach (var kvp in TerrainSpeeds) - { - if (tileSet.TryGetTerrainIndex(kvp.Key, out var index)) - info[index] = kvp.Value; - } - - return info; - } - public class TerrainInfo { public static readonly TerrainInfo Impassable = new TerrainInfo(); @@ -134,53 +119,6 @@ namespace OpenRA.Mods.Common.Traits } } - public struct WorldMovementInfo - { - internal readonly World World; - internal readonly TerrainInfo[] TerrainInfos; - internal WorldMovementInfo(World world, LocomotorInfo info) - { - // PERF: This struct allows us to cache the terrain info for the tileset used by the world. - // This allows us to speed up some performance-sensitive pathfinding calculations. - World = world; - TerrainInfos = info.TilesetTerrainInfo[world.Map.Rules.TileSet]; - } - } - - public readonly Cache TilesetTerrainInfo; - public readonly Cache TilesetMovementClass; - - public LocomotorInfo() - { - TilesetTerrainInfo = new Cache(LoadTilesetSpeeds); - TilesetMovementClass = new Cache(CalculateTilesetMovementClass); - } - - public int CalculateTilesetMovementClass(TileSet tileset) - { - // collect our ability to cross *all* terraintypes, in a bitvector - return TilesetTerrainInfo[tileset].Select(ti => ti.Cost < short.MaxValue).ToBits(); - } - - public uint GetMovementClass(TileSet tileset) - { - return (uint)TilesetMovementClass[tileset]; - } - - public int TileSetMovementHash(TileSet tileSet) - { - var terrainInfos = TilesetTerrainInfo[tileSet]; - - // Compute and return the hash using aggregate - return terrainInfos.Aggregate(terrainInfos.Length, - (current, terrainInfo) => unchecked(current * 31 + terrainInfo.Cost)); - } - - public WorldMovementInfo GetWorldMovementInfo(World world) - { - return new WorldMovementInfo(world, this); - } - public virtual bool DisableDomainPassabilityCheck { get { return false; } } public override object Create(ActorInitializer init) { return new Locomotor(init.Self, this); } @@ -203,14 +141,15 @@ namespace OpenRA.Mods.Common.Traits } public readonly LocomotorInfo Info; + public readonly uint MovementClass; CellLayer cellsCost; CellLayer blockingCache; readonly Dictionary> customLayerCellsCost = new Dictionary>(); readonly Dictionary> customLayerBlockingCache = new Dictionary>(); - LocomotorInfo.TerrainInfo[] terrainInfos; - World world; + readonly LocomotorInfo.TerrainInfo[] terrainInfos; + readonly World world; readonly HashSet dirtyCells = new HashSet(); IActorMap actorMap; @@ -220,6 +159,15 @@ namespace OpenRA.Mods.Common.Traits { Info = info; sharesCell = info.SharesCell; + world = self.World; + + var tileSet = world.Map.Rules.TileSet; + terrainInfos = new LocomotorInfo.TerrainInfo[tileSet.TerrainInfo.Length]; + for (var i = 0; i < terrainInfos.Length; i++) + if (!info.TerrainSpeeds.TryGetValue(tileSet.TerrainInfo[i].Type, out terrainInfos[i])) + terrainInfos[i] = LocomotorInfo.TerrainInfo.Impassable; + + MovementClass = (uint)terrainInfos.Select(ti => ti.Cost < short.MaxValue).ToBits(); } public short MovementCostForCell(CPos cell) @@ -230,6 +178,14 @@ namespace OpenRA.Mods.Common.Traits return cell.Layer == 0 ? cellsCost[cell] : customLayerCellsCost[cell.Layer][cell]; } + public int MovementSpeedForCell(CPos cell) + { + var index = cell.Layer == 0 ? world.Map.GetTerrainIndex(cell) : + world.GetCustomMovementLayers()[cell.Layer].GetTerrainIndex(cell); + + return terrainInfos[index].Speed; + } + public short MovementCostToEnterCell(Actor actor, CPos destNode, BlockedByActor check, Actor ignoreActor) { if (!world.Map.Contains(destNode)) @@ -404,11 +360,9 @@ namespace OpenRA.Mods.Common.Traits public void WorldLoaded(World w, WorldRenderer wr) { - world = w; var map = w.Map; actorMap = w.ActorMap; actorMap.CellUpdated += CellUpdated; - terrainInfos = Info.TilesetTerrainInfo[map.Rules.TileSet]; blockingCache = new CellLayer(map); cellsCost = new CellLayer(map); diff --git a/OpenRA.Mods.Common/Traits/World/PathFinder.cs b/OpenRA.Mods.Common/Traits/World/PathFinder.cs index c311acf37b..b90cc5b8d7 100644 --- a/OpenRA.Mods.Common/Traits/World/PathFinder.cs +++ b/OpenRA.Mods.Common/Traits/World/PathFinder.cs @@ -72,7 +72,7 @@ namespace OpenRA.Mods.Common.Traits } // If a water-land transition is required, bail early - if (domainIndex != null && !domainIndex.IsPassable(source, target, locomotor.Info)) + if (domainIndex != null && !domainIndex.IsPassable(source, target, locomotor)) return EmptyPath; var distance = source - target; @@ -119,7 +119,7 @@ namespace OpenRA.Mods.Common.Traits // Really, we only need to check the circle perimeter, but it's not clear that would be a performance win if (domainIndex != null) { - tilesInRange = new List(tilesInRange.Where(t => domainIndex.IsPassable(source, t, locomotor.Info))); + tilesInRange = new List(tilesInRange.Where(t => domainIndex.IsPassable(source, t, locomotor))); if (!tilesInRange.Any()) return EmptyPath; }