From aef65d353dbc465db911489514393660f3cf6840 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Mon, 18 Apr 2022 20:31:51 +0100 Subject: [PATCH] Replace DomainIndex internals with a lookup from HierarchicalPathFinder instead Teach HierarchicalPathFinder to keep a cache of domain indices, refreshing them only on demand and when invalidated by terrain changes. This provides an accurate and quick determination for checking if paths exist between given locations. By exposing PathExistsForLocomotor on the IPathFinder interface, we can remove the DomainIndex trait entirely. --- .../Pathfinder/HierarchicalPathFinder.cs | 91 ++++++- .../BotModules/Squads/States/NavyStates.cs | 5 +- OpenRA.Mods.Common/Traits/Buildings/Bridge.cs | 5 - .../Traits/Buildings/GroundLevelBridge.cs | 2 - .../Traits/ProductionFromMapEdge.cs | 6 +- .../Traits/World/DomainIndex.cs | 254 ------------------ .../Traits/World/ElevatedBridgeLayer.cs | 4 +- .../World/HierarchicalPathFinderOverlay.cs | 17 +- OpenRA.Mods.Common/Traits/World/PathFinder.cs | 14 +- .../Traits/World/TerrainTunnelLayer.cs | 4 +- OpenRA.Mods.Common/TraitsInterfaces.cs | 7 + mods/cnc/rules/world.yaml | 1 - mods/d2k/rules/world.yaml | 1 - mods/ra/rules/world.yaml | 1 - mods/ts/rules/world.yaml | 1 - 15 files changed, 130 insertions(+), 283 deletions(-) delete mode 100644 OpenRA.Mods.Common/Traits/World/DomainIndex.cs diff --git a/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs b/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs index ddfe4f5451..2cef1b7a9f 100644 --- a/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs +++ b/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs @@ -112,6 +112,14 @@ namespace OpenRA.Mods.Common.Pathfinder /// Dictionary> abstractGraph; + /// + /// The abstract domains are represented here. + /// An abstract node is the key, and a domain index is given. + /// If the domain index of two nodes is equal, a path exists between them (ignoring all blocking actors). + /// If unequal, no path is possible. + /// + readonly Dictionary abstractDomains; + /// /// Knows about the abstract nodes within a grid. Can map a local cell to its abstract node. /// @@ -227,20 +235,27 @@ namespace OpenRA.Mods.Common.Pathfinder BuildGrids(); BuildCostTable(); + abstractDomains = new Dictionary(gridXs * gridYs); + RebuildDomains(); // When we build the cost table, it depends on the movement costs of the cells at that time. // When this changes, we must update the cost table. locomotor.CellCostChanged += RequireCostRefreshInCell; } - public IReadOnlyDictionary> GetOverlayData() + public ( + IReadOnlyDictionary> AbstractGraph, + IReadOnlyDictionary AbstractDomains) GetOverlayData() { if (costEstimator == null) return default; - // Ensure the abstract graph is up to date when using the overlay. + // Ensure the abstract graph and domains are up to date when using the overlay. RebuildDirtyGrids(); - return new ReadOnlyDictionary>(abstractGraph); + RebuildDomains(); + return ( + new ReadOnlyDictionary>(abstractGraph), + new ReadOnlyDictionary(abstractDomains)); } /// @@ -704,6 +719,31 @@ namespace OpenRA.Mods.Common.Pathfinder } } + /// + /// Determines if a path exists between source and target. + /// Only terrain is taken into account, i.e. as if was given. + /// This would apply for any actor using the same as this . + /// + public bool PathExists(CPos source, CPos target) + { + if (costEstimator == null) + return false; + + RebuildDomains(); + + var sourceGridInfo = gridInfos[GridIndex(source)]; + var targetGridInfo = gridInfos[GridIndex(target)]; + var abstractSource = sourceGridInfo.AbstractCellForLocalCell(source); + if (abstractSource == null) + return false; + var abstractTarget = targetGridInfo.AbstractCellForLocalCell(target); + if (abstractTarget == null) + return false; + var sourceDomain = abstractDomains[abstractSource.Value]; + var targetDomain = abstractDomains[abstractTarget.Value]; + return sourceDomain == targetDomain; + } + /// /// The abstract graph can become out of date when reachability costs for terrain change. /// When this occurs, we must rebuild any affected parts of the abstract graph so it remains correct. @@ -713,6 +753,9 @@ namespace OpenRA.Mods.Common.Pathfinder if (dirtyGridIndexes.Count == 0) return; + // An empty domain indicates it is out of date and will require rebuilding when next accessed. + abstractDomains.Clear(); + var customMovementLayers = world.GetCustomMovementLayers(); foreach (var gridIndex in dirtyGridIndexes) { @@ -767,6 +810,48 @@ namespace OpenRA.Mods.Common.Pathfinder } } + /// + /// The abstract domains can become out of date when the abstract graph changes. + /// When this occurs, we must rebuild the domain cache. + /// + void RebuildDomains() + { + // First, rebuild the abstract graph if it is out of date. + RebuildDirtyGrids(); + + // Check if our domain cache is empty, if so this indicates it is out-of-date and needs rebuilding. + if (abstractDomains.Count != 0) + return; + + List AbstractEdge(CPos abstractCell) + { + if (abstractGraph.TryGetValue(abstractCell, out var abstractEdge)) + return abstractEdge; + return null; + } + + // As in BuildGrid, flood fill the search graph until all disjoint domains are discovered. + var domain = 0u; + var abstractCells = new HashSet(abstractGraph.Count); + foreach (var grid in gridInfos) + grid.CopyAbstractCellsInto(abstractCells); + while (abstractCells.Count > 0) + { + var searchCell = abstractCells.First(); + var search = PathSearch.ToTargetCellOverGraph( + AbstractEdge, + locomotor, + searchCell, + searchCell, + abstractGraph.Count / 8); + var searched = search.ExpandAll(); + foreach (var abstractCell in searched) + abstractDomains.Add(abstractCell, domain); + abstractCells.ExceptWith(searched); + domain++; + } + } + /// /// Maps a local cell to a abstract node in the graph. /// Returns null when the local cell is unreachable. diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs index 8b9e599778..0a2719fe31 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/NavyStates.cs @@ -28,12 +28,11 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads // Navy squad AI can exploit enemy naval production to find path, if any. // (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 locomotor = first.Trait().Locomotor; + var mobile = first.Trait(); var navalProductions = owner.World.ActorsHavingTrait().Where(a => owner.SquadManager.Info.NavalProductionTypes.Contains(a.Info.Name) - && domainIndex.IsPassable(first.Location, a.Location, locomotor) + && mobile.PathFinder.PathExistsForLocomotor(mobile.Locomotor, first.Location, a.Location) && a.AppearsHostileTo(first)); if (navalProductions.Any()) diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index 4bb1a38645..97c447a7bf 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -323,11 +323,6 @@ namespace OpenRA.Mods.Common.Traits radarSignature[i++] = (c, tileInfo.GetColor(self.World.LocalRandom)); } - // If this bridge repair operation connects two pathfinding domains, - // update the domain index. - var domainIndex = self.World.WorldActor.Trait(); - domainIndex.UpdateCells(self.World, footprint.Keys); - if (LongBridgeSegmentIsDead() && !killedUnits) { killedUnits = true; diff --git a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs index 11d37839df..af60e69a81 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/GroundLevelBridge.cs @@ -70,8 +70,6 @@ namespace OpenRA.Mods.Common.Traits { foreach (var cell in cells) self.World.Map.CustomTerrain[cell] = terrainIndex; - - self.World.WorldActor.Trait().UpdateCells(self.World, cells); } void INotifyAddedToWorld.AddedToWorld(Actor self) diff --git a/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs b/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs index 351b0faeaf..3b8ed8f930 100644 --- a/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs +++ b/OpenRA.Mods.Common/Traits/ProductionFromMapEdge.cs @@ -25,13 +25,13 @@ namespace OpenRA.Mods.Common.Traits class ProductionFromMapEdge : Production { readonly CPos? spawnLocation; - readonly DomainIndex domainIndex; + readonly IPathFinder pathFinder; RallyPoint rp; public ProductionFromMapEdge(ActorInitializer init, ProductionInfo info) : base(init, info) { - domainIndex = init.Self.World.WorldActor.Trait(); + pathFinder = init.Self.World.WorldActor.Trait(); var spawnLocationInit = init.GetOrDefault(info); if (spawnLocationInit != null) @@ -65,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits { 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], locomotor)); + c => mobileInfo.CanEnterCell(self.World, null, c) && pathFinder.PathExistsForLocomotor(locomotor, c, destinations[0])); } } diff --git a/OpenRA.Mods.Common/Traits/World/DomainIndex.cs b/OpenRA.Mods.Common/Traits/World/DomainIndex.cs deleted file mode 100644 index a13fb365cb..0000000000 --- a/OpenRA.Mods.Common/Traits/World/DomainIndex.cs +++ /dev/null @@ -1,254 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2022 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using System.Collections.Generic; -using System.Linq; -using OpenRA.Graphics; -using OpenRA.Support; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Traits -{ - [TraitLocation(SystemActors.World)] - [Desc("Identify untraversable regions of the map for faster pathfinding, especially with AI.", - "This trait is required. Every mod needs it attached to the world actor.")] - class DomainIndexInfo : TraitInfo { } - - public class DomainIndex : IWorldLoaded - { - Dictionary domainIndexes; - - public void WorldLoaded(World world, WorldRenderer wr) - { - domainIndexes = new Dictionary(); - var locomotors = world.WorldActor.TraitsImplementing().Where(l => !string.IsNullOrEmpty(l.Info.Name)); - 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, 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 (locomotor.Info.DisableDomainPassabilityCheck) - return true; - - return domainIndexes[locomotor.MovementClass].IsPassable(p1, p2); - } - - /// Regenerate the domain index for a group of cells. - public void UpdateCells(World world, IEnumerable cells) - { - var dirty = cells.ToHashSet(); - foreach (var index in domainIndexes) - index.Value.UpdateCells(world, dirty); - } - - public void AddFixedConnection(IEnumerable cells) - { - foreach (var index in domainIndexes) - index.Value.AddFixedConnection(cells); - } - } - - class MovementClassDomainIndex - { - readonly Map map; - readonly uint movementClass; - readonly CellLayer domains; - readonly Dictionary> transientConnections; - - public MovementClassDomainIndex(World world, uint movementClass) - { - map = world.Map; - this.movementClass = movementClass; - domains = new CellLayer(world.Map); - transientConnections = new Dictionary>(); - - using (new PerfTimer($"BuildDomains: {world.Map.Title} for movement class {movementClass}")) - BuildDomains(world); - } - - public bool IsPassable(CPos p1, CPos p2) - { - if (!domains.Contains(p1) || !domains.Contains(p2)) - return false; - - if (domains[p1] == domains[p2]) - return true; - - // Even though p1 and p2 are in different domains, it's possible - // that some dynamic terrain (i.e. bridges) may connect them. - return HasConnection(domains[p1], domains[p2]); - } - - public void UpdateCells(World world, HashSet dirtyCells) - { - var neighborDomains = new List(); - - foreach (var cell in dirtyCells) - { - // Select all neighbors inside the map boundaries - var thisCell = cell; // benign closure hazard - var neighbors = CVec.Directions.Select(d => d + thisCell) - .Where(c => map.Contains(c)); - - var found = false; - foreach (var n in neighbors) - { - if (!dirtyCells.Contains(n)) - { - var neighborDomain = domains[n]; - if (CanTraverseTile(world, n)) - { - neighborDomains.Add(neighborDomain); - - // Set ourselves to the first non-dirty neighbor we find. - if (!found) - { - domains[cell] = neighborDomain; - found = true; - } - } - } - } - } - - foreach (var c1 in neighborDomains) - foreach (var c2 in neighborDomains) - CreateConnection(c1, c2); - } - - public void AddFixedConnection(IEnumerable cells) - { - // HACK: this is a temporary workaround to add a permanent connection between the domains of the listed cells. - // This is sufficient for fixed point-to-point tunnels, but not for dynamically updating custom layers - // such as destroyable elevated bridges. - // To support those the domain index will need to learn about custom movement layers, but that then requires - // a complete refactor of the domain code to deal with MobileInfo or better a shared pathfinder class type. - var cellDomains = cells.Select(c => domains[c]).ToHashSet(); - foreach (var c1 in cellDomains) - foreach (var c2 in cellDomains.Where(c => c != c1)) - CreateConnection(c1, c2); - } - - bool HasConnection(ushort d1, ushort d2) - { - // Search our connections graph for a possible route - var visited = new HashSet(); - var toProcess = new Stack(); - toProcess.Push(d1); - - while (toProcess.Count > 0) - { - var current = toProcess.Pop(); - if (!transientConnections.ContainsKey(current)) - continue; - - foreach (var neighbor in transientConnections[current]) - { - if (neighbor == d2) - return true; - - if (!visited.Contains(neighbor)) - toProcess.Push(neighbor); - } - - visited.Add(current); - } - - return false; - } - - void CreateConnection(ushort d1, ushort d2) - { - if (!transientConnections.ContainsKey(d1)) - transientConnections[d1] = new HashSet(); - if (!transientConnections.ContainsKey(d2)) - transientConnections[d2] = new HashSet(); - - transientConnections[d1].Add(d2); - transientConnections[d2].Add(d1); - } - - bool CanTraverseTile(World world, CPos p) - { - if (!map.Contains(p)) - return false; - - var terrainOffset = world.Map.GetTerrainIndex(p); - return (movementClass & (1 << terrainOffset)) > 0; - } - - void BuildDomains(World world) - { - ushort domain = 1; - - var visited = new CellLayer(map); - - var toProcess = new Queue(); - toProcess.Enqueue(MPos.Zero.ToCPos(map)); - - // Flood-fill over each domain. - while (toProcess.Count != 0) - { - var start = toProcess.Dequeue(); - - // Technically redundant with the check in the inner loop, but prevents - // ballooning the domain counter. - if (visited[start]) - continue; - - var domainQueue = new Queue(); - domainQueue.Enqueue(start); - - var currentPassable = CanTraverseTile(world, start); - - // Add all contiguous cells to our domain, and make a note of - // any non-contiguous cells for future domains. - while (domainQueue.Count != 0) - { - var n = domainQueue.Dequeue(); - if (visited[n]) - continue; - - var candidatePassable = CanTraverseTile(world, n); - if (candidatePassable != currentPassable) - { - toProcess.Enqueue(n); - continue; - } - - visited[n] = true; - domains[n] = domain; - - // PERF: Avoid LINQ. - foreach (var direction in CVec.Directions) - { - // Don't crawl off the map, or add already-visited cells. - var neighbor = direction + n; - if (visited.Contains(neighbor) && !visited[neighbor]) - domainQueue.Enqueue(neighbor); - } - } - - domain += 1; - } - - Log.Write("debug", "Found {0} domains for movement class {1} on map {2}.", domain - 1, movementClass, map.Title); - } - } -} diff --git a/OpenRA.Mods.Common/Traits/World/ElevatedBridgeLayer.cs b/OpenRA.Mods.Common/Traits/World/ElevatedBridgeLayer.cs index 5d2aaa520d..db5e4b581b 100644 --- a/OpenRA.Mods.Common/Traits/World/ElevatedBridgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/ElevatedBridgeLayer.cs @@ -17,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [TraitLocation(SystemActors.World)] - public class ElevatedBridgeLayerInfo : TraitInfo, Requires, ILobbyCustomRulesIgnore, ICustomMovementLayerInfo + public class ElevatedBridgeLayerInfo : TraitInfo, ILobbyCustomRulesIgnore, ICustomMovementLayerInfo { [Desc("Terrain type used by cells outside any elevated bridge footprint.")] public readonly string ImpassableTerrainType = "Impassable"; @@ -44,7 +44,6 @@ namespace OpenRA.Mods.Common.Traits public void WorldLoaded(World world, WorldRenderer wr) { - var domainIndex = world.WorldActor.Trait(); var cellHeight = world.Map.CellHeightStep.Length; foreach (var tti in world.WorldActor.Info.TraitInfos()) { @@ -61,7 +60,6 @@ namespace OpenRA.Mods.Common.Traits } var end = tti.EndCells(); - domainIndex.AddFixedConnection(end); foreach (var c in end) { // Need to explicitly set both default and tunnel layers, otherwise the .Contains check will fail diff --git a/OpenRA.Mods.Common/Traits/World/HierarchicalPathFinderOverlay.cs b/OpenRA.Mods.Common/Traits/World/HierarchicalPathFinderOverlay.cs index d7cf89014e..ded78433ba 100644 --- a/OpenRA.Mods.Common/Traits/World/HierarchicalPathFinderOverlay.cs +++ b/OpenRA.Mods.Common/Traits/World/HierarchicalPathFinderOverlay.cs @@ -88,10 +88,10 @@ namespace OpenRA.Mods.Common.Traits : new[] { Locomotor }; foreach (var locomotor in locomotors) { - var abstractGraph = pathFinder.GetOverlayDataForLocomotor(locomotor); + var (abstractGraph, abstractDomains) = pathFinder.GetOverlayDataForLocomotor(locomotor); // Locomotor doesn't allow movement, nothing to display. - if (abstractGraph == null) + if (abstractGraph == null || abstractDomains == null) continue; foreach (var connectionsFromOneNode in abstractGraph) @@ -129,6 +129,19 @@ namespace OpenRA.Mods.Common.Traits yield return new TextAnnotationRenderable(font, centerPos, 0, lineColor, cost.Cost.ToString()); } } + + foreach (var domainForCell in abstractDomains) + { + var nodeCell = domainForCell.Key; + var srcUv = (PPos)nodeCell.ToMPos(self.World.Map); + if (!visibleRegion.Contains(srcUv)) + continue; + + // Show the abstract cell and its domain index. + var nodePos = self.World.Map.CenterOfSubCell(nodeCell, SubCell.FullCell); + yield return new TextAnnotationRenderable( + font, nodePos, 0, info.AbstractNodeColor, $"{domainForCell.Value}: {nodeCell}"); + } } } diff --git a/OpenRA.Mods.Common/Traits/World/PathFinder.cs b/OpenRA.Mods.Common/Traits/World/PathFinder.cs index cdff4f46fd..d32ca0cb87 100644 --- a/OpenRA.Mods.Common/Traits/World/PathFinder.cs +++ b/OpenRA.Mods.Common/Traits/World/PathFinder.cs @@ -46,7 +46,9 @@ namespace OpenRA.Mods.Common.Traits world = self.World; } - public IReadOnlyDictionary> GetOverlayDataForLocomotor(Locomotor locomotor) + public ( + IReadOnlyDictionary> AbstractGraph, + IReadOnlyDictionary AbstractDomains) GetOverlayDataForLocomotor(Locomotor locomotor) { return hierarchicalPathFindersByLocomotor[locomotor].GetOverlayData(); } @@ -133,6 +135,16 @@ namespace OpenRA.Mods.Common.Traits return search.FindPath(); } + /// + /// Determines if a path exists between source and target. + /// Only terrain is taken into account, i.e. as if was given. + /// This would apply for any actor using the given . + /// + public bool PathExistsForLocomotor(Locomotor locomotor, CPos source, CPos target) + { + return hierarchicalPathFindersByLocomotor[locomotor].PathExists(source, target); + } + static Locomotor GetActorLocomotor(Actor self) { // PERF: This PathFinder trait requires the use of Mobile, so we can be sure that is in use. diff --git a/OpenRA.Mods.Common/Traits/World/TerrainTunnelLayer.cs b/OpenRA.Mods.Common/Traits/World/TerrainTunnelLayer.cs index b1f69ef351..490727be26 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainTunnelLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainTunnelLayer.cs @@ -17,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [TraitLocation(SystemActors.World)] - public class TerrainTunnelLayerInfo : TraitInfo, Requires, ILobbyCustomRulesIgnore, ICustomMovementLayerInfo + public class TerrainTunnelLayerInfo : TraitInfo, ILobbyCustomRulesIgnore, ICustomMovementLayerInfo { [Desc("Terrain type used by cells outside any tunnel footprint.")] public readonly string ImpassableTerrainType = "Impassable"; @@ -43,7 +43,6 @@ namespace OpenRA.Mods.Common.Traits public void WorldLoaded(World world, WorldRenderer wr) { - var domainIndex = world.WorldActor.Trait(); var cellHeight = world.Map.CellHeightStep.Length; foreach (var tti in world.WorldActor.Info.TraitInfos()) { @@ -60,7 +59,6 @@ namespace OpenRA.Mods.Common.Traits } var portal = tti.PortalCells(); - domainIndex.AddFixedConnection(portal); foreach (var c in portal) { // Need to explicitly set both default and tunnel layers, otherwise the .Contains check will fail diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 785789e258..8be26d4280 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -815,5 +815,12 @@ namespace OpenRA.Mods.Common.Traits Func customCost = null, Actor ignoreActor = null, bool laneBias = true); + + /// + /// Determines if a path exists between source and target. + /// Only terrain is taken into account, i.e. as if was given. + /// This would apply for any actor using the given . + /// + bool PathExistsForLocomotor(Locomotor locomotor, CPos source, CPos target); } } diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index f65c682107..3b00d74c3e 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -162,7 +162,6 @@ World: Bridges: bridge1, bridge2, bridge3, bridge4 ProductionQueueFromSelection: ProductionTabsWidget: PRODUCTION_TABS - DomainIndex: SmudgeLayer@SCORCH: Type: Scorch Sequence: scorches diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index a68fc70ba5..ae58232430 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -137,7 +137,6 @@ World: ValidGround: Sand, Rock, Transition, Spice, SpiceSand, Dune, Concrete InitialSpawnDelay: 1500 CheckboxDisplayOrder: 1 - DomainIndex: WarheadDebugOverlay: BuildableTerrainLayer: ResourceLayer: diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index 3f1f03e732..73edffbada 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -184,7 +184,6 @@ World: WaterChance: 20 InitialSpawnDelay: 1500 CheckboxDisplayOrder: 1 - DomainIndex: SmudgeLayer@SCORCH: Type: Scorch Sequence: scorches diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index 45267b928e..6b5aa7b08e 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -241,7 +241,6 @@ World: BuildingInfluence: ProductionQueueFromSelection: ProductionPaletteWidget: PRODUCTION_PALETTE - DomainIndex: SmudgeLayer@SMALLSCORCH: Type: SmallScorch Sequence: smallscorches