diff --git a/OpenRA.Game/GameRules/Ruleset.cs b/OpenRA.Game/GameRules/Ruleset.cs index 01a2e5fa93..588abf8a08 100644 --- a/OpenRA.Game/GameRules/Ruleset.cs +++ b/OpenRA.Game/GameRules/Ruleset.cs @@ -24,8 +24,8 @@ namespace OpenRA public readonly IReadOnlyDictionary Voices; public readonly IReadOnlyDictionary Notifications; public readonly IReadOnlyDictionary Music; - public readonly IReadOnlyDictionary TileSets; - public readonly IReadOnlyDictionary Sequences; + public readonly TileSet TileSet; + public readonly SequenceProvider Sequences; public Ruleset( IDictionary actors, @@ -33,16 +33,16 @@ namespace OpenRA IDictionary voices, IDictionary notifications, IDictionary music, - IDictionary tileSets, - IDictionary sequences) + TileSet tileSet, + SequenceProvider sequences) { Actors = new ReadOnlyDictionary(actors); Weapons = new ReadOnlyDictionary(weapons); Voices = new ReadOnlyDictionary(voices); Notifications = new ReadOnlyDictionary(notifications); Music = new ReadOnlyDictionary(music); - TileSets = new ReadOnlyDictionary(tileSets); - Sequences = new ReadOnlyDictionary(sequences); + TileSet = tileSet; + Sequences = sequences; foreach (var a in Actors.Values) { diff --git a/OpenRA.Game/GameRules/RulesetCache.cs b/OpenRA.Game/GameRules/RulesetCache.cs index b308b18a8e..d5d386e3c0 100644 --- a/OpenRA.Game/GameRules/RulesetCache.cs +++ b/OpenRA.Game/GameRules/RulesetCache.cs @@ -28,7 +28,6 @@ namespace OpenRA readonly Dictionary voiceCache = new Dictionary(); readonly Dictionary notificationCache = new Dictionary(); readonly Dictionary musicCache = new Dictionary(); - readonly Dictionary tileSetCache = new Dictionary(); public event EventHandler LoadingProgress; void RaiseProgress() @@ -43,6 +42,7 @@ namespace OpenRA } public Ruleset Load(IReadOnlyFileSystem fileSystem, + TileSet tileSet, MiniYaml additionalRules, MiniYaml additionalWeapons, MiniYaml additionalVoices, @@ -57,7 +57,6 @@ namespace OpenRA Dictionary voices; Dictionary notifications; Dictionary music; - Dictionary tileSets; using (new PerfTimer("Actors")) actors = LoadYamlRules(fileSystem, actorCache, m.Rules, additionalRules, @@ -79,12 +78,8 @@ namespace OpenRA music = LoadYamlRules(fileSystem, musicCache, m.Music, additionalMusic, k => new MusicInfo(k.Key, k.Value)); - using (new PerfTimer("TileSets")) - tileSets = LoadTileSets(fileSystem, tileSetCache, m.TileSets); - - // TODO: only initialize, and then cache, the provider for the given map - var sequences = tileSets.ToDictionary(t => t.Key, t => new SequenceProvider(fileSystem, modData, t.Value, additionalSequences)); - return new Ruleset(actors, weapons, voices, notifications, music, tileSets, sequences); + var sequences = tileSet != null ? new SequenceProvider(fileSystem, modData, tileSet, additionalSequences) : null; + return new Ruleset(actors, weapons, voices, notifications, music, tileSet, sequences); } /// @@ -93,9 +88,9 @@ namespace OpenRA /// public Ruleset Load(IReadOnlyFileSystem fileSystem, Map map = null) { - return map != null ? Load(fileSystem, map.RuleDefinitions, map.WeaponDefinitions, - map.VoiceDefinitions, map.NotificationDefinitions, map.MusicDefinitions, - map.SequenceDefinitions) : Load(fileSystem, null, null, null, null, null, null); + return map != null ? Load(fileSystem, modData.DefaultTileSets[map.Tileset], map.RuleDefinitions, + map.WeaponDefinitions,map.VoiceDefinitions, map.NotificationDefinitions, map.MusicDefinitions, + map.SequenceDefinitions) : Load(fileSystem, null, null, null, null, null, null, null); } Dictionary LoadYamlRules(IReadOnlyFileSystem fileSystem, diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index a0bc1b02f3..93d46a5996 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -169,10 +169,9 @@ namespace OpenRA [FieldLoader.Ignore] CellLayer cellProjection; [FieldLoader.Ignore] CellLayer> inverseCellProjection; - [FieldLoader.Ignore] Lazy cachedTileSet; [FieldLoader.Ignore] Lazy rules; public Ruleset Rules { get { return rules != null ? rules.Value : null; } } - public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } } + public SequenceProvider SequenceProvider { get { return Rules.Sequences; } } [FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds; [FieldLoader.Ignore] public CellRegion AllCells; @@ -307,8 +306,6 @@ namespace OpenRA return modData.DefaultRules; }); - cachedTileSet = Exts.Lazy(() => Rules.TileSets[Tileset]); - var tl = new MPos(0, 0).ToCPos(this); var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this); AllCells = new CellRegion(Grid.Type, tl, br); @@ -388,7 +385,7 @@ namespace OpenRA // Odd-height ramps get bumped up a level to the next even height layer if ((height & 1) == 1) { - var ti = cachedTileSet.Value.GetTileInfo(MapTiles.Value[uv]); + var ti = Rules.TileSet.GetTileInfo(MapTiles.Value[uv]); if (ti != null && ti.RampType != 0) height += 1; } @@ -624,7 +621,7 @@ namespace OpenRA public byte[] SavePreview() { - var tileset = Rules.TileSets[Tileset]; + var tileset = Rules.TileSet; var resources = Rules.Actors["world"].TraitInfos() .ToDictionary(r => r.ResourceType, r => r.TerrainType); @@ -875,7 +872,7 @@ namespace OpenRA public void FixOpenAreas() { var r = new Random(); - var tileset = Rules.TileSets[Tileset]; + var tileset = Rules.TileSet; for (var j = Bounds.Top; j < Bounds.Bottom; j++) { @@ -923,7 +920,7 @@ namespace OpenRA { var custom = CustomTerrain[uv]; terrainIndex = cachedTerrainIndexes[uv] = - custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]); + custom != byte.MaxValue ? custom : Rules.TileSet.GetTerrainIndex(MapTiles.Value[uv]); } return (byte)terrainIndex; @@ -931,7 +928,7 @@ namespace OpenRA public TerrainTypeInfo GetTerrainInfo(CPos cell) { - return cachedTileSet.Value[GetTerrainIndex(cell)]; + return Rules.TileSet[GetTerrainIndex(cell)]; } public CPos Clamp(CPos cell) diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index b20b31e25c..41da95519e 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -35,10 +35,16 @@ namespace OpenRA public VoxelLoader VoxelLoader { get; private set; } public CursorProvider CursorProvider { get; private set; } public FS ModFiles = new FS(); + public IReadOnlyFileSystem DefaultFileSystem { get { return ModFiles; } } readonly Lazy defaultRules; public Ruleset DefaultRules { get { return defaultRules.Value; } } - public IReadOnlyFileSystem DefaultFileSystem { get { return ModFiles; } } + + readonly Lazy> defaultTileSets; + public IReadOnlyDictionary DefaultTileSets { get { return defaultTileSets.Value; } } + + readonly Lazy> defaultSequences; + public IReadOnlyDictionary DefaultSequences { get { return defaultSequences.Value; } } public ModData(string mod, bool useLoadScreen = false) { @@ -74,6 +80,24 @@ namespace OpenRA SpriteSequenceLoader.OnMissingSpriteError = s => Log.Write("debug", s); defaultRules = Exts.Lazy(() => RulesetCache.Load(DefaultFileSystem)); + defaultTileSets = Exts.Lazy(() => + { + var items = new Dictionary(); + + foreach (var file in Manifest.TileSets) + { + var t = new TileSet(DefaultFileSystem, file); + items.Add(t.Id, t); + } + + return (IReadOnlyDictionary)(new ReadOnlyDictionary(items)); + }); + + defaultSequences = Exts.Lazy(() => + { + var items = DefaultTileSets.ToDictionary(t => t.Key, t => new SequenceProvider(DefaultFileSystem, this, t.Value, null)); + return (IReadOnlyDictionary)(new ReadOnlyDictionary(items)); + }); initialThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; } diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 98bffa816a..23ef101885 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -160,7 +160,7 @@ namespace OpenRA Map = map; Timestep = orderManager.LobbyInfo.GlobalSettings.Timestep; - TileSet = map.Rules.TileSets[Map.Tileset]; + TileSet = map.Rules.TileSet; SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed); var worldActorType = type == WorldType.Editor ? "EditorWorld" : "World"; diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs index 47fdd2b85c..85f9a92f72 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorResourceBrush.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Widgets preview.IsVisible = () => editorWidget.CurrentBrush == this; var variant = resource.Variants.FirstOrDefault(); - var sequenceProvider = wr.World.Map.Rules.Sequences[world.TileSet.Id]; + var sequenceProvider = wr.World.Map.Rules.Sequences; var sequence = sequenceProvider.GetSequence("resources", variant); var sprite = sequence.GetSprite(resource.MaxDensity - 1); preview.GetSprite = () => sprite; diff --git a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs index 1f1796d231..5c2ab6f272 100644 --- a/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs +++ b/OpenRA.Mods.Common/EditorBrushes/EditorTileBrush.cs @@ -100,8 +100,7 @@ namespace OpenRA.Mods.Common.Widgets var mapTiles = map.MapTiles.Value; var mapHeight = map.MapHeight.Value; - var rules = map.Rules; - var tileset = rules.TileSets[map.Tileset]; + var tileset = map.Rules.TileSet; var template = tileset.Templates[Template]; var baseHeight = mapHeight.Contains(cell) ? mapHeight[cell] : (byte)0; @@ -139,8 +138,7 @@ namespace OpenRA.Mods.Common.Widgets var queue = new Queue(); var touched = new CellLayer(map); - var rules = map.Rules; - var tileset = rules.TileSets[map.Tileset]; + var tileset = map.Rules.TileSet; var template = tileset.Templates[Template]; Action maybeEnqueue = newCell => diff --git a/OpenRA.Mods.Common/Lint/CheckSequences.cs b/OpenRA.Mods.Common/Lint/CheckSequences.cs index d1ed7d0e72..166d25c1e9 100644 --- a/OpenRA.Mods.Common/Lint/CheckSequences.cs +++ b/OpenRA.Mods.Common/Lint/CheckSequences.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Lint var rules = map.Rules; var factions = rules.Actors["world"].TraitInfos().Select(f => f.InternalName).ToArray(); - var sequenceProviders = new[] { rules.Sequences[map.Tileset] }; + var sequenceProviders = new[] { rules.Sequences }; foreach (var actorInfo in rules.Actors) { diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 0285393c24..39ac629eb5 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -308,7 +308,7 @@ namespace OpenRA.Mods.Common.Server // Pick a random color for the bot var validator = server.ModData.Manifest.Get(); - var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; + var tileset = server.Map.Rules.TileSet; var terrainColors = tileset.TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color); var playerColors = server.LobbyInfo.Clients.Select(c => c.Color.RGB) .Concat(server.MapPlayers.Players.Values.Select(p => p.Color.RGB)); @@ -1064,7 +1064,7 @@ namespace OpenRA.Mods.Common.Server server.SendOrderTo(connectionToEcho, "Message", message); }; - var tileset = server.Map.Rules.TileSets[server.Map.Tileset]; + var tileset = server.Map.Rules.TileSet; var terrainColors = tileset.TerrainInfo.Where(ti => ti.RestrictPlayerColor).Select(ti => ti.Color).ToList(); var playerColors = server.LobbyInfo.Clients.Where(c => c.Index != playerIndex).Select(c => c.Color.RGB) .Concat(server.MapPlayers.Players.Values.Select(p => p.Color.RGB)).ToList(); diff --git a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs index 28ee6125a2..2728cddcef 100644 --- a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs @@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.UtilityCommands ValidateMapFormat(format); var tileset = GetTileset(mapSection); - Map = new Map(modData, modData.DefaultRules.TileSets[tileset], MapSize, MapSize) + Map = new Map(modData, modData.DefaultTileSets[tileset], MapSize, MapSize) { Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)), Author = "Westwood Studios", diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs index 63c697dde6..985eb1fa77 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/LayerSelectorLogic.cs @@ -19,7 +19,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic public class LayerSelectorLogic : ChromeLogic { readonly EditorViewportControllerWidget editor; - readonly World world; readonly WorldRenderer worldRenderer; readonly ScrollPanelWidget layerTemplateList; @@ -29,8 +28,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic public LayerSelectorLogic(Widget widget, WorldRenderer worldRenderer) { this.worldRenderer = worldRenderer; - world = worldRenderer.World; - editor = widget.Parent.Get("MAP_EDITOR"); layerTemplateList = widget.Get("LAYERTEMPLATE_LIST"); @@ -60,7 +57,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic layerPreview.GetPalette = () => resource.Palette; var variant = resource.Variants.FirstOrDefault(); - var sequenceProvider = rules.Sequences[world.TileSet.Id]; + var sequenceProvider = rules.Sequences; var sequence = sequenceProvider.GetSequence("resources", variant); var frame = sequence.Frames != null ? sequence.Frames.Last() : resource.MaxDensity - 1; layerPreview.GetSprite = () => sequence.GetSprite(frame); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs index 45fe326b1e..00067aa452 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/NewMapLogic.cs @@ -20,14 +20,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic Widget panel; [ObjectCreator.UseCtor] - public NewMapLogic(Action onExit, Action onSelect, Widget widget, World world) + public NewMapLogic(Action onExit, Action onSelect, Widget widget, World world, ModData modData) { panel = widget; panel.Get("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; var tilesetDropDown = panel.Get("TILESET"); - var tilesets = world.Map.Rules.TileSets.Select(t => t.Key).ToList(); + var tilesets = modData.DefaultTileSets.Select(t => t.Key).ToList(); Func setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, @@ -55,7 +55,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic height = Math.Max(2, height); var maxTerrainHeight = world.Map.Grid.MaximumTerrainHeight; - var tileset = world.Map.Rules.TileSets[tilesetDropDown.Text]; + var tileset = modData.DefaultTileSets[tilesetDropDown.Text]; var map = new Map(Game.ModData, tileset, width + 2, height + maxTerrainHeight + 2); var tl = new PPos(1, 1); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs index 257e61aa8f..6ca7384cdd 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/TileSelectorLogic.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic public TileSelectorLogic(Widget widget, WorldRenderer worldRenderer) { var rules = worldRenderer.World.Map.Rules; - var tileset = rules.TileSets[worldRenderer.World.Map.Tileset]; + var tileset = rules.TileSet; editor = widget.Parent.Get("MAP_EDITOR"); panel = widget.Get("TILETEMPLATE_LIST"); diff --git a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs index 777f477171..b34764539f 100644 --- a/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs +++ b/OpenRA.Mods.Common/Widgets/TerrainTemplatePreviewWidget.cs @@ -48,14 +48,14 @@ namespace OpenRA.Mods.Common.Widgets public TerrainTemplatePreviewWidget(WorldRenderer worldRenderer, World world) { this.worldRenderer = worldRenderer; - tileset = world.Map.Rules.TileSets[world.Map.Tileset]; + tileset = world.Map.Rules.TileSet; } protected TerrainTemplatePreviewWidget(TerrainTemplatePreviewWidget other) : base(other) { worldRenderer = other.worldRenderer; - tileset = other.worldRenderer.World.Map.Rules.TileSets[other.worldRenderer.World.Map.Tileset]; + tileset = other.worldRenderer.World.Map.Rules.TileSet; Template = other.Template; GetScale = other.GetScale; } diff --git a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs index d15333cc62..3656ad15fd 100644 --- a/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs +++ b/OpenRA.Mods.D2k/UtilityCommands/D2kMapImporter.cs @@ -308,7 +308,7 @@ namespace OpenRA.Mods.D2k.UtilityCommands { mapSize = new Size(stream.ReadUInt16(), stream.ReadUInt16()); - tileSet = rules.TileSets["ARRAKIS"]; + tileSet = Game.ModData.DefaultTileSets["ARRAKIS"]; map = new Map(Game.ModData, tileSet, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth) { diff --git a/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs b/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs index 16d4477e55..c8f2c1f53b 100644 --- a/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs +++ b/OpenRA.Mods.TS/UtilityCommands/ImportTSMapCommand.cs @@ -228,7 +228,7 @@ namespace OpenRA.Mods.TS.UtilityCommands fullSize = new int2(iniSize[2], iniSize[3]); - var map = new Map(Game.ModData, modData.DefaultRules.TileSets[tileset], size.Width, size.Height); + var map = new Map(Game.ModData, modData.DefaultTileSets[tileset], size.Width, size.Height); map.Title = basic.GetValue("Name", Path.GetFileNameWithoutExtension(filename)); map.Author = "Westwood Studios"; map.Bounds = new Rectangle(iniBounds[0], iniBounds[1], iniBounds[2], 2 * iniBounds[3] + 2 * iniBounds[1]); @@ -243,7 +243,7 @@ namespace OpenRA.Mods.TS.UtilityCommands void ReadTiles(Map map, IniFile file) { - var tileset = Game.ModData.DefaultRules.TileSets[map.Tileset]; + var tileset = Game.ModData.DefaultTileSets[map.Tileset]; var mapSection = file.GetSection("IsoMapPack5"); var data = Convert.FromBase64String(mapSection.Aggregate(string.Empty, (a, b) => a + b.Value));