diff --git a/OpenRA.Game/Map/TileSet.cs b/OpenRA.Game/Map/TileSet.cs index 6b4afbba9c..38891a2d1b 100644 --- a/OpenRA.Game/Map/TileSet.cs +++ b/OpenRA.Game/Map/TileSet.cs @@ -35,6 +35,12 @@ namespace OpenRA float MaxHeightColorBrightness { get; } } + public interface ITemplatedTerrainInfo : ITerrainInfo + { + string[] EditorTemplateOrder { get; } + IReadOnlyDictionary Templates { get; } + } + public interface ITerrainInfoNotifyMapCreated : ITerrainInfo { void MapCreated(Map map); @@ -146,7 +152,7 @@ namespace OpenRA } } - public class TileSet : ITerrainInfo, ITerrainInfoNotifyMapCreated + public class TileSet : ITemplatedTerrainInfo, ITerrainInfoNotifyMapCreated { public const string TerrainPaletteInternalName = "terrain"; @@ -277,6 +283,9 @@ namespace OpenRA float ITerrainInfo.MaxHeightColorBrightness { get { return MaxHeightColorBrightness; } } TerrainTile ITerrainInfo.DefaultTerrainTile { get { return new TerrainTile(Templates.First().Key, 0); } } + string[] ITemplatedTerrainInfo.EditorTemplateOrder { get { return EditorTemplateOrder; } } + IReadOnlyDictionary ITemplatedTerrainInfo.Templates { get { return Templates; } } + void ITerrainInfoNotifyMapCreated.MapCreated(Map map) { // Randomize PickAny tile variants diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs index a870cb4afb..ac1e860754 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -23,6 +24,7 @@ namespace OpenRA.Mods.Common.Widgets readonly WorldRenderer worldRenderer; readonly World world; + readonly ITemplatedTerrainInfo terrainInfo; readonly EditorViewportControllerWidget editorWidget; readonly EditorActionManager editorActionManager; readonly EditorCursorLayer editorCursor; @@ -35,6 +37,10 @@ namespace OpenRA.Mods.Common.Widgets this.editorWidget = editorWidget; worldRenderer = wr; world = wr.World; + terrainInfo = world.Map.Rules.TerrainInfo as ITemplatedTerrainInfo; + if (terrainInfo == null) + throw new InvalidDataException("EditorTileBrush can only be used with template-based tilesets"); + editorActionManager = world.WorldActor.Trait(); editorCursor = world.WorldActor.Trait(); @@ -42,7 +48,7 @@ namespace OpenRA.Mods.Common.Widgets worldRenderer = wr; world = wr.World; - var template = world.Map.Rules.TileSet.Templates.First(t => t.Value.Id == id).Value; + var template = terrainInfo.Templates.First(t => t.Value.Id == id).Value; cursorToken = editorCursor.SetTerrainTemplate(wr, template); } @@ -96,14 +102,11 @@ namespace OpenRA.Mods.Common.Widgets void PaintCell(CPos cell, bool isMoving) { - var map = world.Map; - var tileset = map.Rules.TileSet; - var template = tileset.Templates[Template]; - + var template = terrainInfo.Templates[Template]; if (isMoving && PlacementOverlapsSameTemplate(template, cell)) return; - editorActionManager.Add(new PaintTileEditorAction(Template, map, cell)); + editorActionManager.Add(new PaintTileEditorAction(Template, world.Map, cell)); } void FloodFillWithBrush(CPos cell, bool isMoving) @@ -164,8 +167,8 @@ namespace OpenRA.Mods.Common.Widgets this.map = map; this.cell = cell; - var tileset = map.Rules.TileSet; - terrainTemplate = tileset.Templates[template]; + var terrainInfo = (ITemplatedTerrainInfo)map.Rules.TerrainInfo; + terrainTemplate = terrainInfo.Templates[template]; Text = "Added tile {0}".F(terrainTemplate.Id); } @@ -233,8 +236,8 @@ namespace OpenRA.Mods.Common.Widgets this.map = map; this.cell = cell; - var tileset = map.Rules.TileSet; - terrainTemplate = tileset.Templates[template]; + var terrainInfo = (ITemplatedTerrainInfo)map.Rules.TerrainInfo; + terrainTemplate = terrainInfo.Templates[template]; Text = "Filled with tile {0}".F(terrainTemplate.Id); } diff --git a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs index 6cfdc15dca..1d4355fc2c 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Bridge.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using OpenRA.Effects; using OpenRA.GameRules; @@ -97,6 +98,7 @@ namespace OpenRA.Mods.Common.Traits readonly Bridge[] neighbours = new Bridge[2]; readonly LegacyBridgeHut[] huts = new LegacyBridgeHut[2]; // Huts before this / first & after this / last readonly ITiledTerrainRenderer terrainRenderer; + readonly ITemplatedTerrainInfo terrainInfo; readonly Health health; readonly Actor self; readonly BridgeInfo info; @@ -118,7 +120,11 @@ namespace OpenRA.Mods.Common.Traits type = self.Info.Name; isDangling = new Lazy(() => huts[0] == huts[1] && (neighbours[0] == null || neighbours[1] == null)); buildingInfo = self.Info.TraitInfo(); + terrainRenderer = self.World.WorldActor.Trait(); + terrainInfo = self.World.Map.Rules.TerrainInfo as ITemplatedTerrainInfo; + if (terrainInfo == null) + throw new InvalidDataException("Bridge requires a template-based tileset."); } public Bridge Neighbour(int direction) { return neighbours[direction]; } @@ -149,9 +155,8 @@ namespace OpenRA.Mods.Common.Traits byte GetTerrainType(CPos cell) { var dx = cell - self.Location; - var tileSet = self.World.Map.Rules.TileSet; - var index = dx.X + tileSet.Templates[template].Size.X * dx.Y; - return tileSet.GetTerrainIndex(new TerrainTile(template, (byte)index)); + var index = dx.X + terrainInfo.Templates[template].Size.X * dx.Y; + return terrainInfo.GetTerrainIndex(new TerrainTile(template, (byte)index)); } public void LinkNeighbouringBridges(World world, LegacyBridgeLayer bridges) diff --git a/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs b/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs index 1d36dcc2ac..4beee9851a 100644 --- a/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/LegacyBridgeLayer.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.IO; using System.Linq; using OpenRA.Graphics; using OpenRA.Primitives; @@ -29,12 +30,16 @@ namespace OpenRA.Mods.Common.Traits { readonly LegacyBridgeLayerInfo info; readonly Dictionary bridgeTypes = new Dictionary(); + readonly ITemplatedTerrainInfo terrainInfo; CellLayer bridges; public LegacyBridgeLayer(Actor self, LegacyBridgeLayerInfo info) { this.info = info; + terrainInfo = self.World.Map.Rules.TerrainInfo as ITemplatedTerrainInfo; + if (terrainInfo == null) + throw new InvalidDataException("LegacyBridgeLayer requires a template-based tileset."); } public void WorldLoaded(World w, WorldRenderer wr) @@ -68,7 +73,7 @@ namespace OpenRA.Mods.Common.Traits var ti = w.Map.Tiles[cell]; var tile = ti.Type; var index = ti.Index; - var template = w.Map.Rules.TileSet.Templates[tile]; + var template = terrainInfo.Templates[tile]; var ni = cell.X - index % template.Size.X; var nj = cell.Y - index / template.Size.X; diff --git a/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs b/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs index 8e3557b2a1..34ac57b391 100644 --- a/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/TerrainRenderer.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.IO; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; @@ -25,18 +26,21 @@ namespace OpenRA.Mods.Common.Traits { readonly Map map; readonly Dictionary spriteLayers = new Dictionary(); + readonly TileSet terrainInfo; readonly Theater theater; bool disposed; public TerrainRenderer(World world) { map = world.Map; - theater = new Theater(world.Map.Rules.TileSet); + + terrainInfo = map.Rules.TileSet; + theater = new Theater(terrainInfo); } void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) { - foreach (var template in map.Rules.TileSet.Templates) + foreach (var template in terrainInfo.Templates) { var palette = template.Value.Palette ?? TileSet.TerrainPaletteInternalName; spriteLayers.GetOrAdd(palette, pal => @@ -54,8 +58,8 @@ namespace OpenRA.Mods.Common.Traits { var tile = map.Tiles[cell]; var palette = TileSet.TerrainPaletteInternalName; - if (map.Rules.TileSet.Templates.ContainsKey(tile.Type)) - palette = map.Rules.TileSet.Templates[tile.Type].Palette ?? palette; + if (terrainInfo.Templates.TryGetValue(tile.Type, out var template)) + palette = template.Palette ?? palette; foreach (var kv in spriteLayers) kv.Value.Update(cell, palette == kv.Key ? theater.TileSprite(tile) : null, false); @@ -103,7 +107,7 @@ namespace OpenRA.Mods.Common.Traits for (var x = 0; x < template.Size.X; x++) { var tile = new TerrainTile(template.Id, (byte)(i++)); - if (!map.Rules.TileSet.TryGetTileInfo(tile, out var tileInfo)) + if (!terrainInfo.TryGetTileInfo(tile, out var tileInfo)) continue; var sprite = theater.TileSprite(tile); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs index 76f214179a..b54d0a5019 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.IO; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; @@ -36,7 +37,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } - readonly TileSet tileset; + readonly ITemplatedTerrainInfo terrainInfo; readonly ITiledTerrainRenderer terrainRenderer; readonly TileSelectorTemplate[] allTemplates; readonly EditorCursorLayer editorCursor; @@ -45,12 +46,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic public TileSelectorLogic(Widget widget, World world, WorldRenderer worldRenderer) : base(widget, world, worldRenderer, "TILETEMPLATE_LIST", "TILEPREVIEW_TEMPLATE") { - tileset = world.Map.Rules.TileSet; + terrainInfo = world.Map.Rules.TerrainInfo as ITemplatedTerrainInfo; + if (terrainInfo == null) + throw new InvalidDataException("TileSelectorLogic requires a template-based tileset."); + terrainRenderer = world.WorldActor.TraitOrDefault(); if (terrainRenderer == null) - throw new YamlException("TileSelectorLogic requires a tile-based terrain renderer."); + throw new InvalidDataException("TileSelectorLogic requires a tile-based terrain renderer."); - allTemplates = tileset.Templates.Values.Select(t => new TileSelectorTemplate(t)).ToArray(); + allTemplates = terrainInfo.Templates.Values.Select(t => new TileSelectorTemplate(t)).ToArray(); editorCursor = world.WorldActor.Trait(); allCategories = allTemplates.SelectMany(t => t.Categories) @@ -87,7 +91,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic int CategoryOrder(string category) { - var i = tileset.EditorTemplateOrder.IndexOf(category); + var i = terrainInfo.EditorTemplateOrder.IndexOf(category); return i >= 0 ? i : int.MaxValue; } @@ -111,7 +115,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic () => Editor.SetBrush(new EditorTileBrush(Editor, tileId, WorldRenderer))); var preview = item.Get("TILE_PREVIEW"); - var template = tileset.Templates[tileId]; + var template = terrainInfo.Templates[tileId]; var bounds = terrainRenderer.TemplateBounds(template); // Scale templates to fit within the panel diff --git a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs index ce6d93d7d4..a471266855 100644 --- a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs +++ b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.IO; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -24,7 +25,7 @@ namespace OpenRA.Mods.Common.Widgets readonly ITiledTerrainRenderer terrainRenderer; readonly WorldRenderer worldRenderer; - readonly TileSet tileset; + readonly ITerrainInfo terrainInfo; TerrainTemplateInfo template; Rectangle bounds; @@ -50,7 +51,7 @@ namespace OpenRA.Mods.Common.Widgets public TerrainTemplatePreviewWidget(WorldRenderer worldRenderer, World world) { this.worldRenderer = worldRenderer; - tileset = world.Map.Rules.TileSet; + terrainInfo = world.Map.Rules.TerrainInfo; terrainRenderer = world.WorldActor.TraitOrDefault(); if (terrainRenderer == null) throw new YamlException("TerrainTemplatePreviewWidget requires a tile-based terrain renderer."); @@ -60,7 +61,7 @@ namespace OpenRA.Mods.Common.Widgets : base(other) { worldRenderer = other.worldRenderer; - tileset = other.worldRenderer.World.Map.Rules.TileSet; + terrainInfo = other.worldRenderer.World.Map.Rules.TerrainInfo; terrainRenderer = other.terrainRenderer; Template = other.Template; GetScale = other.GetScale; @@ -87,7 +88,7 @@ namespace OpenRA.Mods.Common.Widgets for (var x = 0; x < Template.Size.X; x++) { var tile = new TerrainTile(Template.Id, (byte)(i++)); - if (!tileset.TryGetTileInfo(tile, out var tileInfo)) + if (!terrainInfo.TryGetTerrainInfo(tile, out var tileInfo)) continue; var sprite = terrainRenderer.TileSprite(tile, 0); diff --git a/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs b/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs index a6fc592c58..e007967d7f 100644 --- a/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs +++ b/OpenRA.Mods.D2k/Traits/Buildings/D2kBuilding.cs @@ -9,6 +9,7 @@ */ #endregion +using System.IO; using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; @@ -80,7 +81,12 @@ namespace OpenRA.Mods.D2k.Traits.Buildings if (layer != null && (!info.ConcretePrerequisites.Any() || techTree == null || techTree.HasPrerequisites(info.ConcretePrerequisites))) { var map = self.World.Map; - var template = map.Rules.TileSet.Templates[info.ConcreteTemplate]; + + var terrainInfo = self.World.Map.Rules.TerrainInfo as ITemplatedTerrainInfo; + if (terrainInfo == null) + throw new InvalidDataException("D2kBuilding requires a template-based tileset."); + + var template = terrainInfo.Templates[info.ConcreteTemplate]; if (template.PickAny) { // Fill the footprint with random variants diff --git a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs index e25a03bb02..2dfa6492b1 100644 --- a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs +++ b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs @@ -260,7 +260,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands Map map; Size mapSize; - TileSet tileSet; + ITemplatedTerrainInfo terrainInfo; List tileSetsFromYaml; int playerCount; @@ -306,10 +306,9 @@ namespace OpenRA.Mods.D2k.UtilityCommands void Initialize(string mapFile) { mapSize = new Size(stream.ReadUInt16(), stream.ReadUInt16()); + terrainInfo = Game.ModData.DefaultTileSets["ARRAKIS"]; - tileSet = Game.ModData.DefaultTileSets["ARRAKIS"]; - - map = new Map(Game.ModData, tileSet, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth) + map = new Map(Game.ModData, terrainInfo, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth) { Title = Path.GetFileNameWithoutExtension(mapFile), Author = "Westwood Studios" @@ -321,7 +320,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands // Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset // Each frame is a tile from the Dune 2000 tileset files, with the Frame ID being the index of the tile in the original file - tileSetsFromYaml = tileSet.Templates.Where(t => t.Value.Frames != null + tileSetsFromYaml = terrainInfo.Templates.Where(t => t.Value.Frames != null && t.Value.Images[0].ToLowerInvariant() == tilesetName.ToLowerInvariant()).Select(ts => ts.Value).ToList(); var players = new MapPlayers(map.Rules, playerCount); @@ -469,7 +468,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands if (template == null) { // Just get a template that contains a tile with the same ID as requested - var templates = tileSet.Templates.Where(t => t.Value.Frames != null && t.Value.Frames.Contains(tileIndex)); + var templates = terrainInfo.Templates.Where(t => t.Value.Frames != null && t.Value.Frames.Contains(tileIndex)); if (templates.Any()) template = templates.First().Value; }