diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index 05d72b0a93..a0a0fd5fb0 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -260,8 +260,6 @@ namespace OpenRA [FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds; [FieldLoader.Ignore] public CellRegion AllCells; - readonly Func containsTest; - void AssertExists(string filename) { using (var s = Container.GetContent(filename)) @@ -275,8 +273,6 @@ namespace OpenRA /// public Map(TileSet tileset, int width, int height) { - containsTest = Contains; - var size = new Size(width, height); var tileShape = Game.ModData.Manifest.TileShape; var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0); @@ -316,8 +312,6 @@ namespace OpenRA /// Initializes a map loaded from disk. public Map(string path) { - containsTest = Contains; - Path = path; Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue); @@ -448,34 +442,40 @@ namespace OpenRA initializedCellProjection = true; - if (MaximumTerrainHeight != 0) + cellProjection = new CellLayer(this); + inverseCellProjection = new CellLayer>(this); + + // Initialize collections + foreach (var cell in AllCells) { - cellProjection = new CellLayer(this); - inverseCellProjection = new CellLayer>(this); - - // Initialize collections - foreach (var cell in AllCells) - { - var uv = cell.ToMPos(TileShape); - cellProjection[uv] = new PPos[0]; - inverseCellProjection[uv] = new List(); - } - - // Initialize projections - foreach (var cell in AllCells) - UpdateProjection(cell); + var uv = cell.ToMPos(TileShape); + cellProjection[uv] = new PPos[0]; + inverseCellProjection[uv] = new List(); } + + // Initialize projections + foreach (var cell in AllCells) + UpdateProjection(cell); } void UpdateProjection(CPos cell) { + MPos uv; + if (MaximumTerrainHeight == 0) + { + uv = cell.ToMPos(TileShape); + cellProjection[cell] = new[] { (PPos)uv }; + var inverse = inverseCellProjection[uv]; + inverse.Clear(); + inverse.Add(uv); return; + } if (!initializedCellProjection) InitializeCellProjection(); - var uv = cell.ToMPos(TileShape); + uv = cell.ToMPos(TileShape); // Remove old reverse projection foreach (var puv in cellProjection[uv]) @@ -630,7 +630,8 @@ namespace OpenRA } } - tiles.CellEntryChanged += UpdateProjection; + if (MaximumTerrainHeight > 0) + tiles.CellEntryChanged += UpdateProjection; return tiles; } @@ -650,7 +651,8 @@ namespace OpenRA } } - tiles.CellEntryChanged += UpdateProjection; + if (MaximumTerrainHeight > 0) + tiles.CellEntryChanged += UpdateProjection; return tiles; } @@ -755,7 +757,15 @@ namespace OpenRA // The first check ensures that the cell is within the valid map region, avoiding // potential crashes in deeper code. All CellLayers have the same geometry, and // CustomTerrain is convenient (cellProjection may be null and others are Lazy). - return CustomTerrain.Contains(uv) && ProjectedCellsCovering(uv).All(containsTest); + return CustomTerrain.Contains(uv) && ContainsAllProjectedCellsCovering(uv); + } + + bool ContainsAllProjectedCellsCovering(MPos uv) + { + foreach (var puv in ProjectedCellsCovering(uv)) + if (!Contains(puv)) + return false; + return true; } public bool Contains(PPos puv) @@ -822,10 +832,6 @@ namespace OpenRA static readonly PPos[] NoProjectedCells = { }; public PPos[] ProjectedCellsCovering(MPos uv) { - // Shortcut for mods that don't use heightmaps - if (MaximumTerrainHeight == 0) - return new[] { (PPos)uv }; - if (!initializedCellProjection) InitializeCellProjection(); @@ -835,21 +841,17 @@ namespace OpenRA return cellProjection[uv]; } - public MPos[] Unproject(PPos puv) + public List Unproject(PPos puv) { var uv = (MPos)puv; - // Shortcut for mods that don't use heightmaps - if (MaximumTerrainHeight == 0) - return new[] { uv }; - if (!initializedCellProjection) InitializeCellProjection(); if (!inverseCellProjection.Contains(uv)) - return new MPos[0]; + return new List(); - return inverseCellProjection[uv].ToArray(); + return inverseCellProjection[uv]; } public int FacingBetween(CPos cell, CPos towards, int fallbackfacing) @@ -966,7 +968,7 @@ namespace OpenRA return (MPos)Clamp((PPos)uv); // Already in bounds, so don't need to do anything. - if (ProjectedCellsCovering(uv).Any(containsTest)) + if (ContainsAllProjectedCellsCovering(uv)) return uv; // Clamping map coordinates is trickier than it might first look! @@ -1028,7 +1030,7 @@ namespace OpenRA public CPos ChooseRandomCell(MersenneTwister rand) { - MPos[] cells; + List cells; do { var u = rand.Next(Bounds.Left, Bounds.Right); @@ -1093,7 +1095,7 @@ namespace OpenRA public CPos ChooseRandomEdgeCell(MersenneTwister rand) { - MPos[] cells; + List cells; do { var isU = rand.Next(2) == 0; diff --git a/OpenRA.Game/Traits/World/Shroud.cs b/OpenRA.Game/Traits/World/Shroud.cs index 5c60f66dee..4545983cff 100644 --- a/OpenRA.Game/Traits/World/Shroud.cs +++ b/OpenRA.Game/Traits/World/Shroud.cs @@ -270,7 +270,11 @@ namespace OpenRA.Traits if (!map.Contains(uv)) return false; - return map.ProjectedCellsCovering(uv).Any(isExploredTest); + foreach (var puv in map.ProjectedCellsCovering(uv)) + if (IsExplored(puv)) + return true; + + return false; } public bool IsExplored(PPos puv) @@ -316,7 +320,11 @@ namespace OpenRA.Traits if (!visibleCount.Contains(uv)) return false; - return map.ProjectedCellsCovering(uv).Any(isVisibleTest); + foreach (var puv in map.ProjectedCellsCovering(uv)) + if (IsVisible(puv)) + return true; + + return false; } // In internal shroud coords diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 4a9bac7ccd..5f674cd543 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -189,6 +189,7 @@ namespace OpenRA.Mods.Common.AI public Player Player { get; private set; } + readonly Func isEnemyUnit; Dictionary waitingPowers = new Dictionary(); Dictionary powerDecisions = new Dictionary(); @@ -229,6 +230,8 @@ namespace OpenRA.Mods.Common.AI { Info = info; World = init.World; + isEnemyUnit = unit => + Player.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait() && unit.HasTrait(); foreach (var decision in info.PowerDecisions) powerDecisions.Add(decision.OrderName, decision); @@ -580,20 +583,12 @@ namespace OpenRA.Mods.Common.AI internal Actor FindClosestEnemy(WPos pos) { - var allEnemyUnits = World.Actors - .Where(unit => Player.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait() && - unit.HasTrait()); - - return allEnemyUnits.ClosestTo(pos); + return World.Actors.Where(isEnemyUnit).ClosestTo(pos); } internal Actor FindClosestEnemy(WPos pos, WDist radius) { - var enemyUnits = World.FindActorsInCircle(pos, radius) - .Where(unit => Player.Stances[unit.Owner] == Stance.Enemy && - !unit.HasTrait() && unit.HasTrait()); - - return enemyUnits.ClosestTo(pos); + return World.FindActorsInCircle(pos, radius).Where(isEnemyUnit).ClosestTo(pos); } List FindEnemyConstructionYards() diff --git a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs index c3a92eb91d..aee1b86731 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs @@ -40,7 +40,17 @@ namespace OpenRA.Mods.Common.Traits public class RepairableBuilding : UpgradableTrait, ITick { [Sync] - public int RepairersHash { get { return Repairers.Aggregate(0, (code, player) => code ^ Sync.HashPlayer(player)); } } + public int RepairersHash + { + get + { + var hash = 0; + foreach (var player in Repairers) + hash ^= Sync.HashPlayer(player); + return hash; + } + } + public readonly List Repairers = new List(); readonly Health health; @@ -94,7 +104,9 @@ namespace OpenRA.Mods.Common.Traits Repairers.RemoveAll(isNotActiveAlly); // If after the previous operation there's no repairers left, stop - if (!Repairers.Any()) return; + if (Repairers.Count == 0) + return; + var buildingValue = self.GetSellValue(); // The cost is the same regardless of the amount of people repairing diff --git a/OpenRA.Mods.Common/Traits/Harvester.cs b/OpenRA.Mods.Common/Traits/Harvester.cs index f2e4c49c09..992f82a261 100644 --- a/OpenRA.Mods.Common/Traits/Harvester.cs +++ b/OpenRA.Mods.Common/Traits/Harvester.cs @@ -70,6 +70,7 @@ namespace OpenRA.Mods.Common.Traits readonly HarvesterInfo info; readonly Mobile mobile; Dictionary contents = new Dictionary(); + bool idleSmart = true; [Sync] public Actor OwnerLinkedProc = null; [Sync] public Actor LastLinkedProc = null; @@ -77,8 +78,17 @@ namespace OpenRA.Mods.Common.Traits [Sync] int currentUnloadTicks; public CPos? LastHarvestedCell = null; public CPos? LastOrderLocation = null; - [Sync] public int ContentValue { get { return contents.Sum(c => c.Key.ValuePerUnit * c.Value); } } - bool idleSmart = true; + [Sync] + public int ContentValue + { + get + { + var value = 0; + foreach (var c in contents) + value += c.Key.ValuePerUnit * c.Value; + return value; + } + } public Harvester(Actor self, HarvesterInfo info) { diff --git a/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs b/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs index 2b48951965..1e13cd0eff 100644 --- a/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs +++ b/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs @@ -58,7 +58,16 @@ namespace OpenRA.Mods.Common.Traits public ReadOnlyList Objectives; [Sync] - public int ObjectivesHash { get { return Objectives.Aggregate(0, (code, objective) => code ^ Sync.Hash(objective.State)); } } + public int ObjectivesHash + { + get + { + var hash = 0; + foreach (var objective in objectives) + hash ^= Sync.Hash(objective.State); + return hash; + } + } // This property is used as a flag in 'Cooperative' games to mark that the player has completed all his objectives. // The player's WinState is only updated when his allies have all completed their objective as well. diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index 1b34ee0779..f041e8ac6d 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -68,15 +68,17 @@ namespace OpenRA.Mods.Common.Traits public readonly ProductionQueueInfo Info; readonly Actor self; + // A list of things we could possibly build + readonly Dictionary produceable = new Dictionary(); + readonly List queue = new List(); + readonly IEnumerable allProduceables; + readonly IEnumerable buildableProduceables; + // Will change if the owner changes PowerManager playerPower; PlayerResources playerResources; protected DeveloperMode developerMode; - // A list of things we could possibly build - Dictionary produceable; - List queue = new List(); - public Actor Actor { get { return self; } } [Sync] public int QueueLength { get { return queue.Count; } } @@ -101,6 +103,8 @@ namespace OpenRA.Mods.Common.Traits Enabled = !info.Factions.Any() || info.Factions.Contains(Faction); CacheProduceables(playerActor); + allProduceables = produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); + buildableProduceables = produceable.Where(a => a.Value.Buildable).Select(a => a.Key); } void ClearQueue() @@ -143,7 +147,7 @@ namespace OpenRA.Mods.Common.Traits void CacheProduceables(Actor playerActor) { - produceable = new Dictionary(); + produceable.Clear(); if (!Enabled) return; @@ -200,9 +204,9 @@ namespace OpenRA.Mods.Common.Traits public virtual IEnumerable AllItems() { if (self.World.AllowDevCommands && developerMode.AllTech) - return produceable.Select(a => a.Key); + return produceable.Keys; - return produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); + return allProduceables; } public virtual IEnumerable BuildableItems() @@ -210,9 +214,9 @@ namespace OpenRA.Mods.Common.Traits if (!Enabled) return Enumerable.Empty(); if (self.World.AllowDevCommands && developerMode.AllTech) - return produceable.Select(a => a.Key); + return produceable.Keys; - return produceable.Where(a => a.Value.Buildable).Select(a => a.Key); + return buildableProduceables; } public bool CanBuild(ActorInfo actor)