diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index e0f52eb772..6b2096016a 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -29,6 +29,7 @@ namespace OpenRA { public static ModData modData; public static Settings Settings; + public static ICursor Cursor; static WorldRenderer worldRenderer; internal static OrderManager orderManager; @@ -129,6 +130,7 @@ namespace OpenRA public static event Action BeforeGameStart = () => { }; internal static void StartGame(string mapUID, bool isShellmap) { + Cursor.SetCursor(null); BeforeGameStart(); Map map; @@ -157,6 +159,7 @@ namespace OpenRA orderManager.LastTickTime = RunTime; orderManager.StartGame(); worldRenderer.RefreshPalette(); + Cursor.SetCursor("default"); GC.Collect(); } @@ -287,11 +290,14 @@ namespace OpenRA Sound.Initialize(); modData = new ModData(mod, true); + Renderer.InitializeFonts(modData.Manifest); modData.InitializeLoaders(); using (new PerfTimer("LoadMaps")) modData.MapCache.LoadMaps(); + Cursor = new SoftwareCursor(modData.CursorProvider); + PerfHistory.items["render"].hasNormalTick = false; PerfHistory.items["batches"].hasNormalTick = false; PerfHistory.items["render_widgets"].hasNormalTick = false; @@ -401,8 +407,6 @@ namespace OpenRA public static void RunAfterTick(Action a) { delayedActions.Add(a); } public static void RunAfterDelay(int delay, Action a) { delayedActions.Add(a, delay); } - static float cursorFrame = 0f; - static void InnerLogicTick(OrderManager orderManager) { var tick = RunTime; @@ -419,7 +423,7 @@ namespace OpenRA Viewport.TicksSinceLastMove += uiTickDelta / Timestep; Sync.CheckSyncUnchanged(world, Ui.Tick); - cursorFrame += 0.5f; + Cursor.Tick(); } var worldTimestep = world == null ? Timestep : world.Timestep; @@ -509,8 +513,8 @@ namespace OpenRA if (modData != null && modData.CursorProvider != null) { - var cursorName = Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default"; - modData.CursorProvider.DrawCursor(Renderer, cursorName, Viewport.LastMousePos, (int)cursorFrame); + Cursor.SetCursor(Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default"); + Cursor.Render(Renderer); } } diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index 1f1d8bdb79..f6e4143557 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -10,26 +10,21 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using OpenRA.FileSystem; using OpenRA.Primitives; namespace OpenRA.Graphics { - public sealed class CursorProvider : IDisposable + public sealed class CursorProvider { - readonly HardwarePalette palette = new HardwarePalette(); - readonly Dictionary cursors = new Dictionary(); - readonly Cache palettes; - readonly SheetBuilder sheetBuilder; - - public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } } + public readonly IReadOnlyDictionary Cursors; + public readonly IReadOnlyDictionary Palettes; public CursorProvider(ModData modData) { var sequenceFiles = modData.Manifest.Cursors; - - palettes = new Cache(CreatePaletteReference); var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal)); var shadowIndex = new int[] { }; @@ -41,65 +36,35 @@ namespace OpenRA.Graphics out shadowIndex[shadowIndex.Length - 1]); } + var palettes = new Dictionary(); foreach (var p in nodesDict["Palettes"].Nodes) - palette.AddPalette(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex), false); + palettes.Add(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex)); - sheetBuilder = new SheetBuilder(SheetType.Indexed); - var spriteCache = new SpriteCache(modData.SpriteLoaders, new string[0], sheetBuilder); + Palettes = palettes.AsReadOnly(); + + var frameCache = new FrameCache(modData.SpriteLoaders, new string[0]); + var cursors = new Dictionary(); foreach (var s in nodesDict["Cursors"].Nodes) - LoadSequencesForCursor(spriteCache, s.Key, s.Value); - sheetBuilder.Current.ReleaseBuffer(); + foreach (var sequence in s.Value.Nodes) + cursors.Add(sequence.Key, new CursorSequence(frameCache, sequence.Key, s.Key, s.Value.Value, sequence.Value)); - palette.Initialize(); + Cursors = cursors.AsReadOnly(); } - PaletteReference CreatePaletteReference(string name) - { - var pal = palette.GetPalette(name); - return new PaletteReference(name, palette.GetPaletteIndex(name), pal); - } - - void LoadSequencesForCursor(SpriteCache cache, string cursorSrc, MiniYaml cursor) - { - foreach (var sequence in cursor.Nodes) - cursors.Add(sequence.Key, new CursorSequence(cache, cursorSrc, cursor.Value, sequence.Value)); - } + public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } } public bool HasCursorSequence(string cursor) { - return cursors.ContainsKey(cursor); - } - - public void DrawCursor(Renderer renderer, string cursorName, int2 lastMousePos, int cursorFrame) - { - var cursorSequence = GetCursorSequence(cursorName); - var cursorSprite = cursorSequence.GetSprite(cursorFrame); - var cursorSize = CursorViewportZoomed ? 2.0f * cursorSprite.size : cursorSprite.size; - - var cursorOffset = CursorViewportZoomed ? - (2 * cursorSequence.Hotspot) + cursorSprite.size.ToInt2() : - cursorSequence.Hotspot + (0.5f * cursorSprite.size).ToInt2(); - - renderer.SetPalette(palette); - renderer.SpriteRenderer.DrawSprite(cursorSprite, - lastMousePos - cursorOffset, - palettes[cursorSequence.Palette], - cursorSize); + return Cursors.ContainsKey(cursor); } public CursorSequence GetCursorSequence(string cursor) { - try { return cursors[cursor]; } + try { return Cursors[cursor]; } catch (KeyNotFoundException) { throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor)); } } - - public void Dispose() - { - palette.Dispose(); - sheetBuilder.Dispose(); - } } } diff --git a/OpenRA.Game/Graphics/CursorSequence.cs b/OpenRA.Game/Graphics/CursorSequence.cs index f825d8b7fa..dececbdae9 100644 --- a/OpenRA.Game/Graphics/CursorSequence.cs +++ b/OpenRA.Game/Graphics/CursorSequence.cs @@ -8,47 +8,46 @@ */ #endregion +using System.Linq; + namespace OpenRA.Graphics { public class CursorSequence { - readonly int start, length; - readonly string palette; - - public int Start { get { return start; } } - public int End { get { return start + length; } } - public int Length { get { return length; } } - public string Palette { get { return palette; } } + public readonly string Name; + public readonly int Start; + public readonly int Length; + public readonly string Palette; public readonly int2 Hotspot; - Sprite[] sprites; + public readonly ISpriteFrame[] Frames; - public CursorSequence(SpriteCache cache, string cursorSrc, string palette, MiniYaml info) + public CursorSequence(FrameCache cache, string name, string cursorSrc, string palette, MiniYaml info) { - sprites = cache[cursorSrc]; var d = info.ToDictionary(); - start = Exts.ParseIntegerInvariant(d["Start"].Value); - this.palette = palette; + Start = Exts.ParseIntegerInvariant(d["Start"].Value); + Palette = palette; + Name = name; if ((d.ContainsKey("Length") && d["Length"].Value == "*") || (d.ContainsKey("End") && d["End"].Value == "*")) - length = sprites.Length - start; + Length = Frames.Length - Start; else if (d.ContainsKey("Length")) - length = Exts.ParseIntegerInvariant(d["Length"].Value); + Length = Exts.ParseIntegerInvariant(d["Length"].Value); else if (d.ContainsKey("End")) - length = Exts.ParseIntegerInvariant(d["End"].Value) - start; + Length = Exts.ParseIntegerInvariant(d["End"].Value) - Start; else - length = 1; + Length = 1; + + Frames = cache[cursorSrc] + .Skip(Start) + .Take(Length) + .ToArray(); if (d.ContainsKey("X")) Exts.TryParseIntegerInvariant(d["X"].Value, out Hotspot.X); if (d.ContainsKey("Y")) Exts.TryParseIntegerInvariant(d["Y"].Value, out Hotspot.Y); } - - public Sprite GetSprite(int frame) - { - return sprites[(frame % length) + start]; - } } } diff --git a/OpenRA.Game/Graphics/SoftwareCursor.cs b/OpenRA.Game/Graphics/SoftwareCursor.cs new file mode 100644 index 0000000000..5a4de6c9e5 --- /dev/null +++ b/OpenRA.Game/Graphics/SoftwareCursor.cs @@ -0,0 +1,100 @@ +#region Copyright & License Information +/* +* Copyright 2007-2014 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. For more information, +* see COPYING. +*/ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Primitives; + +namespace OpenRA.Graphics +{ + public interface ICursor : IDisposable + { + void Render(Renderer renderer); + void SetCursor(string cursor); + void Tick(); + } + + public class SoftwareCursor : ICursor + { + readonly HardwarePalette palette = new HardwarePalette(); + readonly Cache paletteReferences; + readonly Dictionary sprites = new Dictionary(); + readonly CursorProvider cursorProvider; + readonly SheetBuilder sheetBuilder; + + public SoftwareCursor(CursorProvider cursorProvider) + { + this.cursorProvider = cursorProvider; + + paletteReferences = new Cache(CreatePaletteReference); + foreach (var p in cursorProvider.Palettes) + palette.AddPalette(p.Key, p.Value, false); + + palette.Initialize(); + + sheetBuilder = new SheetBuilder(SheetType.Indexed); + foreach (var kv in cursorProvider.Cursors) + { + var s = kv.Value.Frames.Select(a => sheetBuilder.Add(a)).ToArray(); + sprites.Add(kv.Key, s); + } + + sheetBuilder.Current.ReleaseBuffer(); + } + + PaletteReference CreatePaletteReference(string name) + { + var pal = palette.GetPalette(name); + return new PaletteReference(name, palette.GetPaletteIndex(name), pal); + } + + string cursorName; + public void SetCursor(string cursor) + { + cursorName = cursor; + } + + float cursorFrame; + public void Tick() + { + cursorFrame += 0.5f; + } + + public void Render(Renderer renderer) + { + if (cursorName == null) + return; + + var cursorSequence = cursorProvider.GetCursorSequence(cursorName); + var cursorSprite = sprites[cursorName][((int)cursorFrame % cursorSequence.Length)]; + var cursorSize = CursorProvider.CursorViewportZoomed ? 2.0f * cursorSprite.size : cursorSprite.size; + + var cursorOffset = CursorProvider.CursorViewportZoomed ? + (2 * cursorSequence.Hotspot) + cursorSprite.size.ToInt2() : + cursorSequence.Hotspot + (0.5f * cursorSprite.size).ToInt2(); + + renderer.SetPalette(palette); + renderer.SpriteRenderer.DrawSprite(cursorSprite, + Viewport.LastMousePos - cursorOffset, + paletteReferences[cursorSequence.Palette], + cursorSize); + } + + public void Dispose() + { + palette.Dispose(); + sheetBuilder.Dispose(); + } + } +} + diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 3715a3fbe6..d2e114f517 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -90,8 +90,6 @@ namespace OpenRA VoxelLoader.Dispose(); VoxelLoader = new VoxelLoader(); - if (CursorProvider != null) - CursorProvider.Dispose(); CursorProvider = new CursorProvider(this); } @@ -175,8 +173,6 @@ namespace OpenRA MapCache.Dispose(); if (VoxelLoader != null) VoxelLoader.Dispose(); - if (CursorProvider != null) - CursorProvider.Dispose(); } } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 3765eefdeb..a1381d9978 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -252,6 +252,7 @@ +