Merge pull request #7175 from pchote/hardware-cursors

Closes #7015
This commit is contained in:
Matthias Mailänder
2014-12-24 09:37:17 +01:00
22 changed files with 1025 additions and 695 deletions

View File

@@ -29,6 +29,7 @@ namespace OpenRA
{ {
public static ModData modData; public static ModData modData;
public static Settings Settings; public static Settings Settings;
public static ICursor Cursor;
static WorldRenderer worldRenderer; static WorldRenderer worldRenderer;
internal static OrderManager orderManager; internal static OrderManager orderManager;
@@ -129,6 +130,7 @@ namespace OpenRA
public static event Action BeforeGameStart = () => { }; public static event Action BeforeGameStart = () => { };
internal static void StartGame(string mapUID, bool isShellmap) internal static void StartGame(string mapUID, bool isShellmap)
{ {
Cursor.SetCursor(null);
BeforeGameStart(); BeforeGameStart();
Map map; Map map;
@@ -157,6 +159,7 @@ namespace OpenRA
orderManager.LastTickTime = RunTime; orderManager.LastTickTime = RunTime;
orderManager.StartGame(); orderManager.StartGame();
worldRenderer.RefreshPalette(); worldRenderer.RefreshPalette();
Cursor.SetCursor("default");
GC.Collect(); GC.Collect();
} }
@@ -287,11 +290,33 @@ namespace OpenRA
Sound.Initialize(); Sound.Initialize();
modData = new ModData(mod, true); modData = new ModData(mod, true);
Renderer.InitializeFonts(modData.Manifest); Renderer.InitializeFonts(modData.Manifest);
modData.InitializeLoaders(); modData.InitializeLoaders();
using (new PerfTimer("LoadMaps")) using (new PerfTimer("LoadMaps"))
modData.MapCache.LoadMaps(); modData.MapCache.LoadMaps();
if (Settings.Graphics.HardwareCursors)
{
try
{
Cursor = new HardwareCursor(modData.CursorProvider);
}
catch (Exception e)
{
Log.Write("debug", "Failed to initialize hardware cursors. Falling back to software cursors.");
Log.Write("debug", "Error was: " + e.Message);
Console.WriteLine("Failed to initialize hardware cursors. Falling back to software cursors.");
Console.WriteLine("Error was: " + e.Message);
Cursor = new SoftwareCursor(modData.CursorProvider);
Settings.Graphics.HardwareCursors = false;
}
}
else
Cursor = new SoftwareCursor(modData.CursorProvider);
PerfHistory.items["render"].hasNormalTick = false; PerfHistory.items["render"].hasNormalTick = false;
PerfHistory.items["batches"].hasNormalTick = false; PerfHistory.items["batches"].hasNormalTick = false;
PerfHistory.items["render_widgets"].hasNormalTick = false; PerfHistory.items["render_widgets"].hasNormalTick = false;
@@ -401,8 +426,6 @@ namespace OpenRA
public static void RunAfterTick(Action a) { delayedActions.Add(a); } public static void RunAfterTick(Action a) { delayedActions.Add(a); }
public static void RunAfterDelay(int delay, Action a) { delayedActions.Add(a, delay); } public static void RunAfterDelay(int delay, Action a) { delayedActions.Add(a, delay); }
static float cursorFrame = 0f;
static void InnerLogicTick(OrderManager orderManager) static void InnerLogicTick(OrderManager orderManager)
{ {
var tick = RunTime; var tick = RunTime;
@@ -419,7 +442,7 @@ namespace OpenRA
Viewport.TicksSinceLastMove += uiTickDelta / Timestep; Viewport.TicksSinceLastMove += uiTickDelta / Timestep;
Sync.CheckSyncUnchanged(world, Ui.Tick); Sync.CheckSyncUnchanged(world, Ui.Tick);
cursorFrame += 0.5f; Cursor.Tick();
} }
var worldTimestep = world == null ? Timestep : world.Timestep; var worldTimestep = world == null ? Timestep : world.Timestep;
@@ -509,8 +532,8 @@ namespace OpenRA
if (modData != null && modData.CursorProvider != null) if (modData != null && modData.CursorProvider != null)
{ {
var cursorName = Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default"; Cursor.SetCursor(Ui.Root.GetCursorOuter(Viewport.LastMousePos) ?? "default");
modData.CursorProvider.DrawCursor(Renderer, cursorName, Viewport.LastMousePos, (int)cursorFrame); Cursor.Render(Renderer);
} }
} }

View File

@@ -10,26 +10,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using OpenRA.FileSystem; using OpenRA.FileSystem;
using OpenRA.Primitives; using OpenRA.Primitives;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
public sealed class CursorProvider : IDisposable public sealed class CursorProvider
{ {
readonly HardwarePalette palette = new HardwarePalette(); public readonly IReadOnlyDictionary<string, CursorSequence> Cursors;
readonly Dictionary<string, CursorSequence> cursors = new Dictionary<string, CursorSequence>(); public readonly IReadOnlyDictionary<string, ImmutablePalette> Palettes;
readonly Cache<string, PaletteReference> palettes;
readonly SheetBuilder sheetBuilder;
public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } }
public CursorProvider(ModData modData) public CursorProvider(ModData modData)
{ {
var sequenceFiles = modData.Manifest.Cursors; var sequenceFiles = modData.Manifest.Cursors;
palettes = new Cache<string, PaletteReference>(CreatePaletteReference);
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal)); var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
var shadowIndex = new int[] { }; var shadowIndex = new int[] { };
@@ -41,65 +36,35 @@ namespace OpenRA.Graphics
out shadowIndex[shadowIndex.Length - 1]); out shadowIndex[shadowIndex.Length - 1]);
} }
var palettes = new Dictionary<string, ImmutablePalette>();
foreach (var p in nodesDict["Palettes"].Nodes) 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); Palettes = palettes.AsReadOnly();
var spriteCache = new SpriteCache(modData.SpriteLoaders, new string[0], sheetBuilder);
var frameCache = new FrameCache(modData.SpriteLoaders, new string[0]);
var cursors = new Dictionary<string, CursorSequence>();
foreach (var s in nodesDict["Cursors"].Nodes) foreach (var s in nodesDict["Cursors"].Nodes)
LoadSequencesForCursor(spriteCache, s.Key, s.Value); foreach (var sequence in s.Value.Nodes)
sheetBuilder.Current.ReleaseBuffer(); 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) public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } }
{
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 bool HasCursorSequence(string cursor) public bool HasCursorSequence(string cursor)
{ {
return cursors.ContainsKey(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);
} }
public CursorSequence GetCursorSequence(string cursor) public CursorSequence GetCursorSequence(string cursor)
{ {
try { return cursors[cursor]; } try { return Cursors[cursor]; }
catch (KeyNotFoundException) catch (KeyNotFoundException)
{ {
throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor)); throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor));
} }
} }
public void Dispose()
{
palette.Dispose();
sheetBuilder.Dispose();
}
} }
} }

View File

@@ -8,47 +8,46 @@
*/ */
#endregion #endregion
using System.Linq;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
public class CursorSequence public class CursorSequence
{ {
readonly int start, length; public readonly string Name;
readonly string palette; public readonly int Start;
public readonly int Length;
public int Start { get { return start; } } public readonly string Palette;
public int End { get { return start + length; } }
public int Length { get { return length; } }
public string Palette { get { return palette; } }
public readonly int2 Hotspot; 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(); var d = info.ToDictionary();
start = Exts.ParseIntegerInvariant(d["start"].Value); Start = Exts.ParseIntegerInvariant(d["Start"].Value);
this.palette = palette; Palette = palette;
Name = name;
if ((d.ContainsKey("length") && d["length"].Value == "*") || (d.ContainsKey("end") && d["end"].Value == "*")) 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")) else if (d.ContainsKey("Length"))
length = Exts.ParseIntegerInvariant(d["length"].Value); Length = Exts.ParseIntegerInvariant(d["Length"].Value);
else if (d.ContainsKey("end")) else if (d.ContainsKey("End"))
length = Exts.ParseIntegerInvariant(d["end"].Value) - start; Length = Exts.ParseIntegerInvariant(d["End"].Value) - Start;
else else
length = 1; Length = 1;
if (d.ContainsKey("x")) Frames = cache[cursorSrc]
Exts.TryParseIntegerInvariant(d["x"].Value, out Hotspot.X); .Skip(Start)
if (d.ContainsKey("y")) .Take(Length)
Exts.TryParseIntegerInvariant(d["y"].Value, out Hotspot.Y); .ToArray();
}
public Sprite GetSprite(int frame) if (d.ContainsKey("X"))
{ Exts.TryParseIntegerInvariant(d["X"].Value, out Hotspot.X);
return sprites[(frame % length) + start]; if (d.ContainsKey("Y"))
Exts.TryParseIntegerInvariant(d["Y"].Value, out Hotspot.Y);
} }
} }
} }

View File

@@ -0,0 +1,138 @@
#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 class HardwareCursor : ICursor
{
readonly Dictionary<string, IHardwareCursor[]> hardwareCursors = new Dictionary<string, IHardwareCursor[]>();
readonly CursorProvider cursorProvider;
CursorSequence cursor;
public HardwareCursor(CursorProvider cursorProvider)
{
this.cursorProvider = cursorProvider;
foreach (var kv in cursorProvider.Cursors)
{
var palette = cursorProvider.Palettes[kv.Value.Palette];
var hc = kv.Value.Frames
.Select(f => CreateCursor(f, palette, kv.Key, kv.Value))
.ToArray();
hardwareCursors.Add(kv.Key, hc);
}
Update();
}
IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence)
{
var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2;
// Expand the frame if required to include the hotspot
var frameWidth = f.Size.Width;
var dataWidth = f.Size.Width;
var dataX = 0;
if (hotspot.X < 0)
{
dataX = -hotspot.X;
dataWidth += dataX;
hotspot.X = 0;
}
else if (hotspot.X >= frameWidth)
dataWidth = hotspot.X + 1;
var frameHeight = f.Size.Height;
var dataHeight = f.Size.Height;
var dataY = 0;
if (hotspot.Y < 0)
{
dataY = -hotspot.Y;
dataHeight += dataY;
hotspot.Y = 0;
}
else if (hotspot.Y >= frameHeight)
dataHeight = hotspot.Y + 1;
var data = new byte[4 * dataWidth * dataHeight];
for (var j = 0; j < frameHeight; j++)
{
for (var i = 0; i < frameWidth; i++)
{
var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]);
var start = 4 * ((j + dataY) * dataWidth + dataX + i);
for (var k = 0; k < 4; k++)
data[start + k] = bytes[k];
}
}
return Game.Renderer.Device.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot);
}
public void SetCursor(string cursorName)
{
if ((cursorName == null && cursor == null) || (cursor != null && cursorName == cursor.Name))
return;
if (cursorName == null || !cursorProvider.Cursors.TryGetValue(cursorName, out cursor))
cursor = null;
Update();
}
int frame;
int ticks;
public void Tick()
{
if (cursor == null || cursor.Length == 1)
return;
if (++ticks > 2)
{
ticks -= 2;
frame++;
Update();
}
}
void Update()
{
if (cursor == null)
Game.Renderer.Device.SetHardwareCursor(null);
else
{
if (frame >= cursor.Length)
frame = frame % cursor.Length;
Game.Renderer.Device.SetHardwareCursor(hardwareCursors[cursor.Name][frame]);
}
}
public void Render(Renderer renderer) { }
public void Dispose()
{
foreach (var cursors in hardwareCursors)
foreach (var cursor in cursors.Value)
cursor.Dispose();
hardwareCursors.Clear();
}
}
}

View File

@@ -32,6 +32,8 @@ namespace OpenRA
IGraphicsDevice Create(Size size, WindowMode windowMode); IGraphicsDevice Create(Size size, WindowMode windowMode);
} }
public interface IHardwareCursor : IDisposable { }
public enum BlendMode : byte { None, Alpha, Additive, Subtractive, Multiply } public enum BlendMode : byte { None, Alpha, Additive, Subtractive, Multiply }
public interface IGraphicsDevice : IDisposable public interface IGraphicsDevice : IDisposable
@@ -61,6 +63,9 @@ namespace OpenRA
void GrabWindowMouseFocus(); void GrabWindowMouseFocus();
void ReleaseWindowMouseFocus(); void ReleaseWindowMouseFocus();
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot);
void SetHardwareCursor(IHardwareCursor cursor);
} }
public interface IVertexBuffer<T> : IDisposable public interface IVertexBuffer<T> : IDisposable

View File

@@ -0,0 +1,102 @@
#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<string, PaletteReference> paletteReferences;
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
readonly CursorProvider cursorProvider;
readonly SheetBuilder sheetBuilder;
public SoftwareCursor(CursorProvider cursorProvider)
{
this.cursorProvider = cursorProvider;
paletteReferences = new Cache<string, PaletteReference>(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();
Game.Renderer.Device.SetHardwareCursor(null);
}
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();
}
}
}

View File

@@ -52,6 +52,8 @@ namespace OpenRA.Graphics
public FrameCache(ISpriteLoader[] loaders, string[] exts) public FrameCache(ISpriteLoader[] loaders, string[] exts)
{ {
// Include extension-less version
exts = exts.Append("").ToArray();
frames = new Cache<string, ISpriteFrame[]>(filename => SpriteLoader.GetFrames(filename, exts, loaders)); frames = new Cache<string, ISpriteFrame[]>(filename => SpriteLoader.GetFrames(filename, exts, loaders));
} }

View File

@@ -90,8 +90,6 @@ namespace OpenRA
VoxelLoader.Dispose(); VoxelLoader.Dispose();
VoxelLoader = new VoxelLoader(); VoxelLoader = new VoxelLoader();
if (CursorProvider != null)
CursorProvider.Dispose();
CursorProvider = new CursorProvider(this); CursorProvider = new CursorProvider(this);
} }
@@ -175,8 +173,6 @@ namespace OpenRA
MapCache.Dispose(); MapCache.Dispose();
if (VoxelLoader != null) if (VoxelLoader != null)
VoxelLoader.Dispose(); VoxelLoader.Dispose();
if (CursorProvider != null)
CursorProvider.Dispose();
} }
} }

View File

@@ -252,6 +252,8 @@
<Compile Include="Graphics\TargetLineRenderable.cs" /> <Compile Include="Graphics\TargetLineRenderable.cs" />
<Compile Include="Graphics\UISpriteRenderable.cs" /> <Compile Include="Graphics\UISpriteRenderable.cs" />
<Compile Include="GameRules\DamageWarhead.cs" /> <Compile Include="GameRules\DamageWarhead.cs" />
<Compile Include="Graphics\SoftwareCursor.cs" />
<Compile Include="Graphics\HardwareCursor.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="FileSystem\D2kSoundResources.cs" /> <Compile Include="FileSystem\D2kSoundResources.cs" />

View File

@@ -80,6 +80,7 @@ namespace OpenRA
public WindowMode Mode = WindowMode.PseudoFullscreen; public WindowMode Mode = WindowMode.PseudoFullscreen;
public int2 FullscreenSize = new int2(0, 0); public int2 FullscreenSize = new int2(0, 0);
public int2 WindowedSize = new int2(1024, 768); public int2 WindowedSize = new int2(1024, 768);
public bool HardwareCursors = true;
public bool PixelDouble = false; public bool PixelDouble = false;
public bool CursorDouble = false; public bool CursorDouble = false;
public bool CapFramerate = true; public bool CapFramerate = true;

View File

@@ -62,6 +62,17 @@ namespace OpenRA.Mods.Common.UtilityCommands
file.WriteLine(yaml.WriteToString()); file.WriteLine(yaml.WriteToString());
} }
Console.WriteLine("Processing Cursors:");
foreach (var filename in Game.modData.Manifest.Cursors)
{
Console.WriteLine("\t" + filename);
var yaml = MiniYaml.FromFile(filename);
UpgradeRules.UpgradeCursors(engineDate, ref yaml, null, 0);
using (var file = new StreamWriter(filename))
file.WriteLine(yaml.WriteToString());
}
Console.WriteLine("Processing Maps:"); Console.WriteLine("Processing Maps:");
var maps = Game.modData.MapCache var maps = Game.modData.MapCache
.Where(m => m.Status == MapStatus.Available) .Where(m => m.Status == MapStatus.Available)

View File

@@ -1041,5 +1041,27 @@ namespace OpenRA.Mods.Common.UtilityCommands
nodes.AddRange(addNodes); nodes.AddRange(addNodes);
} }
internal static void UpgradeCursors(int engineVersion, ref List<MiniYamlNode> nodes, MiniYamlNode parent, int depth)
{
foreach (var node in nodes)
{
if (engineVersion < 20141113 && depth == 3)
{
if (node.Key == "start")
node.Key = "Start";
else if (node.Key == "length")
node.Key = "Length";
else if (node.Key == "end")
node.Key = "End";
else if (node.Key == "x")
node.Key = "X";
else if (node.Key == "y")
node.Key = "Y";
}
UpgradeCursors(engineVersion, ref node.Value.Nodes, node, depth + 1);
}
}
} }
} }

View File

@@ -148,6 +148,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var ds = Game.Settings.Graphics; var ds = Game.Settings.Graphics;
var gs = Game.Settings.Game; var gs = Game.Settings.Game;
BindCheckboxPref(panel, "HARDWARECURSORS_CHECKBOX", ds, "HardwareCursors");
BindCheckboxPref(panel, "PIXELDOUBLE_CHECKBOX", ds, "PixelDouble"); BindCheckboxPref(panel, "PIXELDOUBLE_CHECKBOX", ds, "PixelDouble");
BindCheckboxPref(panel, "CURSORDOUBLE_CHECKBOX", ds, "CursorDouble"); BindCheckboxPref(panel, "CURSORDOUBLE_CHECKBOX", ds, "CursorDouble");
BindCheckboxPref(panel, "FRAME_LIMIT_CHECKBOX", ds, "CapFramerate"); BindCheckboxPref(panel, "FRAME_LIMIT_CHECKBOX", ds, "CapFramerate");
@@ -173,8 +174,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
worldRenderer.Viewport.Zoom = ds.PixelDouble ? 2 : 1; worldRenderer.Viewport.Zoom = ds.PixelDouble ? 2 : 1;
}; };
// Cursor doubling is only supported with software cursors and when pixel doubling is enabled
var cursorDoubleCheckbox = panel.Get<CheckboxWidget>("CURSORDOUBLE_CHECKBOX"); var cursorDoubleCheckbox = panel.Get<CheckboxWidget>("CURSORDOUBLE_CHECKBOX");
cursorDoubleCheckbox.IsDisabled = () => !ds.PixelDouble; cursorDoubleCheckbox.IsDisabled = () => !ds.PixelDouble || Game.Cursor is HardwareCursor;
var cursorDoubleIsChecked = cursorDoubleCheckbox.IsChecked;
cursorDoubleCheckbox.IsChecked = () => !cursorDoubleCheckbox.IsDisabled() && cursorDoubleIsChecked();
panel.Get("WINDOW_RESOLUTION").IsVisible = () => ds.Mode == WindowMode.Windowed; panel.Get("WINDOW_RESOLUTION").IsVisible = () => ds.Mode == WindowMode.Windowed;
var windowWidth = panel.Get<TextFieldWidget>("WINDOW_WIDTH"); var windowWidth = panel.Get<TextFieldWidget>("WINDOW_WIDTH");

View File

@@ -66,6 +66,9 @@ namespace OpenRA.Renderer.Null
public ITexture CreateTexture(Bitmap bitmap) { return new NullTexture(); } public ITexture CreateTexture(Bitmap bitmap) { return new NullTexture(); }
public IFrameBuffer CreateFrameBuffer(Size s) { return new NullFrameBuffer(); } public IFrameBuffer CreateFrameBuffer(Size s) { return new NullFrameBuffer(); }
public IShader CreateShader(string name) { return new NullShader(); } public IShader CreateShader(string name) { return new NullShader(); }
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot) { return null; }
public void SetHardwareCursor(IHardwareCursor cursor) { }
} }
public class NullShader : IShader public class NullShader : IShader

View File

@@ -10,6 +10,8 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using OpenRA; using OpenRA;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
@@ -80,7 +82,6 @@ namespace OpenRA.Renderer.Sdl2
SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
} }
SDL.SDL_ShowCursor(0);
context = SDL.SDL_GL_CreateContext(window); context = SDL.SDL_GL_CreateContext(window);
SDL.SDL_GL_MakeCurrent(window, context); SDL.SDL_GL_MakeCurrent(window, context);
GL.LoadAll(); GL.LoadAll();
@@ -103,10 +104,53 @@ namespace OpenRA.Renderer.Sdl2
input = new Sdl2Input(); input = new Sdl2Input();
} }
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot)
{
var c = new SDL2HardwareCursor(size, data, hotspot);
if (c.Cursor == IntPtr.Zero)
throw new InvalidDataException("Failed to create hardware cursor `{0}`: {1}".F(name, SDL.SDL_GetError()));
return c;
}
public void SetHardwareCursor(IHardwareCursor cursor)
{
var c = cursor as SDL2HardwareCursor;
if (c == null)
SDL.SDL_ShowCursor(0);
else
{
SDL.SDL_ShowCursor(1);
SDL.SDL_SetCursor(c.Cursor);
}
}
class SDL2HardwareCursor : IHardwareCursor
{
public readonly IntPtr Cursor;
readonly IntPtr surface;
public SDL2HardwareCursor(Size size, byte[] data, int2 hotspot)
{
surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface));
Marshal.Copy(data, 0, sur.pixels, data.Length);
Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y);
}
public void Dispose()
{
SDL.SDL_FreeCursor(Cursor);
SDL.SDL_FreeSurface(surface);
}
}
public void Dispose() public void Dispose()
{ {
if (disposed) if (disposed)
return; return;
disposed = true; disposed = true;
if (context != IntPtr.Zero) if (context != IntPtr.Zero)
{ {

View File

@@ -50,21 +50,21 @@ Container@SETTINGS_PANEL:
Text: Display Text: Display
Align: Center Align: Center
Label@MODE_LABEL: Label@MODE_LABEL:
X: 120 X: 110
Y: 39 Y: 39
Width: 45 Width: 45
Height: 25 Height: 25
Align: Right Align: Right
Text: Mode: Text: Mode:
DropDownButton@MODE_DROPDOWN: DropDownButton@MODE_DROPDOWN:
X: 170 X: 160
Y: 40 Y: 40
Width: 170 Width: 170
Height: 25 Height: 25
Font: Regular Font: Regular
Text: Windowed Text: Windowed
Container@WINDOW_RESOLUTION: Container@WINDOW_RESOLUTION:
X: 340 X: 330
Y: 40 Y: 40
Children: Children:
Label@At: Label@At:
@@ -92,62 +92,69 @@ Container@SETTINGS_PANEL:
Width: 45 Width: 45
Height: 25 Height: 25
MaxLength: 5 MaxLength: 5
Checkbox@HARDWARECURSORS_CHECKBOX:
X: 310
Y: 75
Width: 200
Height: 20
Font: Regular
Text: Use Hardware Cursors
Label@VIDEO_DESC: Label@VIDEO_DESC:
Y: 60 Y: 93
Width: PARENT_RIGHT Width: PARENT_RIGHT
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Center Align: Center
Text: Mode/Resolution changes will be applied after the game is restarted Text: Mode, resolution, and cursor changes will be applied after the game is restarted
Checkbox@FRAME_LIMIT_CHECKBOX: Checkbox@FRAME_LIMIT_CHECKBOX:
X: 15 X: 15
Y: 100 Y: 125
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Enable Frame Limiter Text: Enable Frame Limiter
Checkbox@PIXELDOUBLE_CHECKBOX: Checkbox@PIXELDOUBLE_CHECKBOX:
X: 310 X: 310
Y: 100 Y: 125
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Enable Pixel Doubling Text: Enable Pixel Doubling
Checkbox@CURSORDOUBLE_CHECKBOX: Checkbox@CURSORDOUBLE_CHECKBOX:
X: 355 X: 340
Y: 135 Y: 155
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Also Double Cursor Text: Also Double Cursor
Label@FRAME_LIMIT_DESC_A: Label@FRAME_LIMIT_DESC_A:
X: 45 X: 45
Y: 132 Y: 152
Width: 50 Width: 50
Height: 25 Height: 25
Text: Limit to Text: Limit to
Align: Right Align: Right
TextField@FRAME_LIMIT_TEXTFIELD: TextField@FRAME_LIMIT_TEXTFIELD:
X: 100 X: 100
Y: 133 Y: 153
Width: 45 Width: 45
Height: 25 Height: 25
MaxLength: 3 MaxLength: 3
Label@FRAME_LIMIT_DESC_B: Label@FRAME_LIMIT_DESC_B:
X: 150 X: 150
Y: 132 Y: 152
Height: 25 Height: 25
Text: FPS Text: FPS
Checkbox@TEAM_HEALTH_COLORS_CHECKBOX: Checkbox@TEAM_HEALTH_COLORS_CHECKBOX:
X: 310 X: 310
Y: 170 Y: 185
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Team Health Colors Text: Team Health Colors
Checkbox@SHOW_SHELLMAP: Checkbox@SHOW_SHELLMAP:
X: 15 X: 15
Y: 170 Y: 185
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
@@ -155,18 +162,18 @@ Container@SETTINGS_PANEL:
Label@PLAYER: Label@PLAYER:
Text: Player: Text: Player:
X: 15 X: 15
Y: 215 Y: 225
TextField@PLAYERNAME: TextField@PLAYERNAME:
Text: Name Text: Name
X: 65 X: 65
Y: 205 Y: 215
Width: 145 Width: 145
Height: 25 Height: 25
MaxLength: 16 MaxLength: 16
ColorPreviewManager@COLOR_MANAGER: ColorPreviewManager@COLOR_MANAGER:
DropDownButton@PLAYERCOLOR: DropDownButton@PLAYERCOLOR:
X: 215 X: 215
Y: 205 Y: 215
Width: 70 Width: 70
Height: 25 Height: 25
IgnoreChildMouseOver: true IgnoreChildMouseOver: true
@@ -178,13 +185,13 @@ Container@SETTINGS_PANEL:
Height: PARENT_BOTTOM-12 Height: PARENT_BOTTOM-12
Checkbox@ALWAYS_SHOW_STATUS_BARS_CHECKBOX: Checkbox@ALWAYS_SHOW_STATUS_BARS_CHECKBOX:
X: 310 X: 310
Y: 205 Y: 215
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Always Show Status Bars Text: Always Show Status Bars
Label@LOCALIZATION_TITLE: Label@LOCALIZATION_TITLE:
Y: 225 Y: 265
Width: PARENT_RIGHT Width: PARENT_RIGHT
Font: Bold Font: Bold
Text: Localization Text: Localization
@@ -192,7 +199,7 @@ Container@SETTINGS_PANEL:
Visible: false Visible: false
Label@LANGUAGE_LABEL: Label@LANGUAGE_LABEL:
X: 230 - WIDTH - 5 X: 230 - WIDTH - 5
Y: 244 Y: 284
Width: 75 Width: 75
Height: 25 Height: 25
Align: Right Align: Right
@@ -200,13 +207,13 @@ Container@SETTINGS_PANEL:
Visible: false Visible: false
DropDownButton@LANGUAGE_DROPDOWNBUTTON: DropDownButton@LANGUAGE_DROPDOWNBUTTON:
X: 230 X: 230
Y: 245 Y: 285
Width: 200 Width: 200
Height: 25 Height: 25
Font: Regular Font: Regular
Visible: false Visible: false
Label@LANGUAGE_DESC_A: Label@LANGUAGE_DESC_A:
Y: 265 Y: 310
Width: PARENT_RIGHT Width: PARENT_RIGHT
Height: 25 Height: 25
Font: Tiny Font: Tiny
@@ -214,7 +221,7 @@ Container@SETTINGS_PANEL:
Text: Language changes will be applied after the game is restarted Text: Language changes will be applied after the game is restarted
Visible: false Visible: false
Label@LANGUAGE_DESC_B: Label@LANGUAGE_DESC_B:
Y: 280 Y: 325
Width: PARENT_RIGHT Width: PARENT_RIGHT
Height: 25 Height: 25
Font: Tiny Font: Tiny

View File

@@ -1,181 +1,180 @@
Palettes: Palettes:
cursor: cursor.pal cursor: cursor.pal
Cursors: Cursors:
mouse2.shp: cursor mouse2.shp: cursor
scroll-t: scroll-t:
start:1 Start: 1
scroll-tr: scroll-tr:
start:2 Start: 2
scroll-r: scroll-r:
start:3 Start: 3
scroll-br: scroll-br:
start:4 Start: 4
scroll-b: scroll-b:
start:5 Start: 5
scroll-bl: scroll-bl:
start:6 Start: 6
scroll-l: scroll-l:
start:7 Start: 7
scroll-tl: scroll-tl:
start:8 Start: 8
scroll-t-blocked: scroll-t-blocked:
start:130 Start: 130
scroll-tr-blocked: scroll-tr-blocked:
start:131 Start: 131
scroll-r-blocked: scroll-r-blocked:
start:132 Start: 132
scroll-br-blocked: scroll-br-blocked:
start:133 Start: 133
scroll-b-blocked: scroll-b-blocked:
start:134 Start: 134
scroll-bl-blocked: scroll-bl-blocked:
start:135 Start: 135
scroll-l-blocked: scroll-l-blocked:
start:136 Start: 136
scroll-tl-blocked: scroll-tl-blocked:
start:137 Start: 137
select: select:
start:12 Start: 12
length:6 Length: 6
default-minimap: default-minimap:
start:86 Start: 86
length:1 Length: 1
x: -16 X: -16
y: -12 Y: -12
generic-blocked: generic-blocked:
start:9 Start: 9
generic-blocked-minimap: generic-blocked-minimap:
start:27 Start: 27
attack: attack:
start:18 Start: 18
length:8 Length: 8
attack-minimap: attack-minimap:
start:140 Start: 140
length:8 Length: 8
harvest: harvest:
start:18 Start: 18
length:8 Length: 8
harvest-minimap: harvest-minimap:
start:140 Start: 140
length:8 Length: 8
enter: enter:
start:119 Start: 119
length:3 Length: 3
enter-minimap: enter-minimap:
start:148 Start: 148
length:3 Length: 3
c4: c4:
start:122 Start: 122
length:3 Length: 3
c4-minimap: c4-minimap:
start:127 Start: 127
length:3 Length: 3
# Cursors that need minimap variants # Cursors that need minimap variants
deploy: deploy:
start:53 Start: 53
length:9 Length: 9
repair: repair:
start:29 Start: 29
length:24 Length: 24
repair-blocked: repair-blocked:
start:126 Start: 126
length:1 Length: 1
sell: sell:
start:62 Start: 62
length:24 Length: 24
sell-blocked: sell-blocked:
start:125 Start: 125
length:1 Length: 1
ability: ability:
start:88 Start: 88
length:8 Length: 8
nuke: nuke:
start:96 Start: 96
length:7 Length: 7
ioncannon: ioncannon:
start:103 Start: 103
length:16 Length: 16
sell-vehicle: sell-vehicle:
start:154 Start: 154
length:24 Length: 24
mouse3.shp: cursor mouse3.shp: cursor
default: default:
start:0 Start: 0
x: -16 X: -16
y: -12 Y: -12
deploy-blocked: deploy-blocked:
start:1 Start: 1
length:1 Length: 1
mouse4.shp: cursor
mouse4.shp:cursor
move: move:
start:0 Start: 0
length:8 Length: 8
move-minimap: move-minimap:
start:8 Start: 8
length:4 Length: 4
move-rough: move-rough:
start:0 Start: 0
length: 8 Length: 8
attackmove: attackmove:
start:12 Start: 12
length:8 Length: 8
attackmove-minimap: attackmove-minimap:
start:20 Start: 20
length:4 Length: 4
move-blocked: move-blocked:
start:24 Start: 24
length:1 Length: 1
move-blocked-minimap: move-blocked-minimap:
start:25 Start: 25
length:1 Length: 1
mouse5.shp: cursor mouse5.shp: cursor
guard: guard:
start:0 Start: 0
length:8 Length: 8
guard-minimap: guard-minimap:
start:8 Start: 8
length:8 Length: 8
mouse6.shp: cursor mouse6.shp: cursor
goldwrench: goldwrench:
start:0 Start: 0
length:3 Length: 3
goldwrench-minimap: goldwrench-minimap:
start:3 Start: 3
length:3 Length: 3
goldwrench-blocked: goldwrench-blocked:
start:6 Start: 6
length:1 Length: 1
goldwrench-blocked-minimap: goldwrench-blocked-minimap:
start:7 Start: 7
length:1 Length: 1
capture: capture:
start:8 Start: 8
length:3 Length: 3
capture-minimap: capture-minimap:
start:11 Start: 11
length:3 Length: 3
capture-blocked: capture-blocked:
start:14 Start: 14
length:1 Length: 1
capture-blocked-minimap: capture-blocked-minimap:
start:15 Start: 15
length:1 Length: 1
enter-blocked: enter-blocked:
start:16 Start: 16
length:1 Length: 1
enter-blocked-minimap: enter-blocked-minimap:
start:17 Start: 17
length:1 Length: 1
mouse7.shp: cursor mouse7.shp: cursor
attackoutsiderange: attackoutsiderange:
start:0 Start: 0
length:8 Length: 8
attackoutsiderange-minimap: attackoutsiderange-minimap:
start:8 Start: 8

View File

@@ -6,269 +6,268 @@ Palettes:
Cursors: Cursors:
mouse.r8: mouse mouse.r8: mouse
scroll-t: scroll-t:
start:112 Start: 112
x: 24 X: 24
y: 24 Y: 24
scroll-tr: scroll-tr:
start: 120 Start: 120
x: 24 X: 24
y: 24 Y: 24
scroll-r: scroll-r:
start: 128 Start: 128
x: 24 X: 24
y: 24 Y: 24
scroll-br: scroll-br:
start: 136 Start: 136
x: 24 X: 24
y: 24 Y: 24
scroll-b: scroll-b:
start:148 Start: 148
x: 24 X: 24
y: 24 Y: 24
scroll-bl: scroll-bl:
start:156 Start: 156
x: 24 X: 24
y: 24 Y: 24
scroll-l: scroll-l:
start:164 Start: 164
x: 24 X: 24
y: 24 Y: 24
scroll-tl: scroll-tl:
start:172 Start: 172
x: 24 X: 24
y: 24 Y: 24
scroll-t-blocked: scroll-t-blocked:
start:180 Start: 180
x: 24 X: 24
y: 24 Y: 24
scroll-tr-blocked: scroll-tr-blocked:
start:188 Start: 188
x: 24 X: 24
y: 24 Y: 24
scroll-r-blocked: scroll-r-blocked:
start:196 Start: 196
x: 24 X: 24
y: 24 Y: 24
scroll-br-blocked: scroll-br-blocked:
start:204 Start: 204
x: 24 X: 24
y: 24 Y: 24
scroll-b-blocked: scroll-b-blocked:
start:212 Start: 212
x: 24 X: 24
y: 24 Y: 24
scroll-bl-blocked: scroll-bl-blocked:
start:220 Start: 220
x: 24 X: 24
y: 24 Y: 24
scroll-l-blocked: scroll-l-blocked:
start:228 Start: 228
x: 24 X: 24
y: 24 Y: 24
scroll-tl-blocked: scroll-tl-blocked:
start:236 Start: 236
x: 24 X: 24
y: 24 Y: 24
select: select:
start:40 Start: 40
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
default: default:
start:0 Start: 0
x: 24 X: 24
y: 24 Y: 24
default-minimap: default-minimap:
start:0 Start: 0
x: 24 X: 24
y: 24 Y: 24
generic-blocked: generic-blocked:
start:24 Start: 24
x: 24 X: 24
y: 24 Y: 24
generic-blocked-minimap: generic-blocked-minimap:
start:24 Start: 24
x: 24 X: 24
y: 24 Y: 24
move: move:
start:8 Start: 8
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
move-minimap: move-minimap:
start:48 Start: 48
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
move-blocked: move-blocked:
start:24 Start: 24
x: 24 X: 24
y: 24 Y: 24
move-blocked-minimap: move-blocked-minimap:
start:24 Start: 24
x: 24 X: 24
y: 24 Y: 24
attack: attack:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
attack-minimap: attack-minimap:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
attackoutsiderange: attackoutsiderange:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
attackoutsiderange-minimap: attackoutsiderange-minimap:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
attackmove: attackmove:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
attackmove-minimap: attackmove-minimap:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
harvest: harvest:
start:16 Start: 16
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
harvest-minimap: harvest-minimap:
start:203 Start: 203
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
enter: enter:
start:32 Start: 32
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
enter-minimap: enter-minimap:
start:32 Start: 32
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
enter-blocked: enter-blocked:
start:104 Start: 104
length: 1 Length: 1
x: 24 X: 24
y: 24 Y: 24
enter-blocked-minimap: enter-blocked-minimap:
start:104 Start: 104
x: 24 X: 24
y: 24 Y: 24
c4: c4:
start:248 Start: 248
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
c4-minimap: c4-minimap:
start:248 Start: 248
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
guard: guard:
start:72 Start: 72
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
guard-minimap: guard-minimap:
start:72 Start: 72
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
capture: capture:
start:32 Start: 32
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
capture-minimap: capture-minimap:
start:32 Start: 32
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
ability: ability:
start:72 Start: 72
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
ability-minimap: ability-minimap:
start:72 Start: 72
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
# Cursors that need minimap variants # Cursors that need minimap variants
deploy: deploy:
start:96 Start: 96
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
deploy-blocked: deploy-blocked:
start:104 Start: 104
length: 1 Length: 1
x: 24 X: 24
y: 24 Y: 24
nuke: nuke:
start:240 Start: 240
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
sell: sell:
start:80 Start: 80
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
sell-blocked: sell-blocked:
start:56 Start: 56
length: 1 Length: 1
x: 24 X: 24
y: 24 Y: 24
repair: repair:
start:88 Start: 88
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
repair-blocked: repair-blocked:
start:64 Start: 64
length: 1 Length: 1
x: 24 X: 24
y: 24 Y: 24
goldwrench: goldwrench:
start:88 Start: 88
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
goldwrench-blocked: goldwrench-blocked:
start:64 Start: 64
length: 1 Length: 1
x: 24 X: 24
y: 24 Y: 24
move-rough: move-rough:
start:256 Start: 256
length: 8 Length: 8
x: 24 X: 24
y: 24 Y: 24
nopower.shp: mouse nopower.shp: mouse
powerdown-blocked: powerdown-blocked:
start:0 Start: 0
length: 1 Length: 1
x: 12 X: 12
y: 12 Y: 12
powerdown: powerdown:
start:1 Start: 1
length: 3 Length: 3
x: 12 X: 12
y: 12 Y: 12

View File

@@ -4,6 +4,6 @@
Cursors: Cursors:
cursor.shp: cursor cursor.shp: cursor
default: default:
start:0 Start:0
x: -4 X: -4
y: -7 Y: -7

View File

@@ -63,21 +63,21 @@ Background@SETTINGS_PANEL:
Height: PARENT_BOTTOM Height: PARENT_BOTTOM
Children: Children:
Label@MODE_LABEL: Label@MODE_LABEL:
X: 120 X: 110
Y: 39 Y: 39
Width: 45 Width: 45
Height: 25 Height: 25
Align: Right Align: Right
Text: Mode: Text: Mode:
DropDownButton@MODE_DROPDOWN: DropDownButton@MODE_DROPDOWN:
X: 170 X: 160
Y: 40 Y: 40
Width: 170 Width: 170
Height: 25 Height: 25
Font: Regular Font: Regular
Text: Windowed Text: Windowed
Container@WINDOW_RESOLUTION: Container@WINDOW_RESOLUTION:
X: 340 X: 330
Y: 40 Y: 40
Children: Children:
Label@At: Label@At:
@@ -105,62 +105,69 @@ Background@SETTINGS_PANEL:
Width: 45 Width: 45
Height: 25 Height: 25
MaxLength: 5 MaxLength: 5
Checkbox@HARDWARECURSORS_CHECKBOX:
X: 310
Y: 75
Width: 200
Height: 20
Font: Regular
Text: Use Hardware Cursors
Label@VIDEO_DESC: Label@VIDEO_DESC:
Y: 60 Y: 93
Width: PARENT_RIGHT Width: PARENT_RIGHT
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Center Align: Center
Text: Mode/Resolution changes will be applied after the game is restarted Text: Mode, resolution, and cursor changes will be applied after the game is restarted
Checkbox@FRAME_LIMIT_CHECKBOX: Checkbox@FRAME_LIMIT_CHECKBOX:
X: 15 X: 15
Y: 100 Y: 125
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Enable Frame Limiter Text: Enable Frame Limiter
Checkbox@PIXELDOUBLE_CHECKBOX: Checkbox@PIXELDOUBLE_CHECKBOX:
X: 310 X: 310
Y: 100 Y: 125
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Enable Pixel Doubling Text: Enable Pixel Doubling
Checkbox@CURSORDOUBLE_CHECKBOX: Checkbox@CURSORDOUBLE_CHECKBOX:
X: 355 X: 340
Y: 135 Y: 160
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Also Double Cursor Text: Also Double Cursor
Label@FRAME_LIMIT_DESC_A: Label@FRAME_LIMIT_DESC_A:
X: 45 X: 45
Y: 132 Y: 157
Width: 50 Width: 50
Height: 25 Height: 25
Text: Limit to Text: Limit to
Align: Right Align: Right
TextField@FRAME_LIMIT_TEXTFIELD: TextField@FRAME_LIMIT_TEXTFIELD:
X: 100 X: 100
Y: 133 Y: 158
Width: 45 Width: 45
Height: 25 Height: 25
MaxLength: 3 MaxLength: 3
Label@FRAME_LIMIT_DESC_B: Label@FRAME_LIMIT_DESC_B:
X: 150 X: 150
Y: 132 Y: 157
Height: 25 Height: 25
Text: FPS Text: FPS
Checkbox@TEAM_HEALTH_COLORS_CHECKBOX: Checkbox@TEAM_HEALTH_COLORS_CHECKBOX:
X: 310 X: 310
Y: 170 Y: 195
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Team Health Colors Text: Team Health Colors
Checkbox@SHOW_SHELLMAP: Checkbox@SHOW_SHELLMAP:
X: 15 X: 15
Y: 170 Y: 195
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
@@ -168,18 +175,18 @@ Background@SETTINGS_PANEL:
Label@PLAYER: Label@PLAYER:
Text: Player: Text: Player:
X: 15 X: 15
Y: 215 Y: 240
TextField@PLAYERNAME: TextField@PLAYERNAME:
Text: Name Text: Name
X: 65 X: 65
Y: 205 Y: 230
Width: 145 Width: 145
Height: 25 Height: 25
MaxLength: 16 MaxLength: 16
ColorPreviewManager@COLOR_MANAGER: ColorPreviewManager@COLOR_MANAGER:
DropDownButton@PLAYERCOLOR: DropDownButton@PLAYERCOLOR:
X: 215 X: 215
Y: 205 Y: 230
Width: 70 Width: 70
Height: 25 Height: 25
IgnoreChildMouseOver: true IgnoreChildMouseOver: true
@@ -191,13 +198,13 @@ Background@SETTINGS_PANEL:
Height: PARENT_BOTTOM-12 Height: PARENT_BOTTOM-12
Checkbox@ALWAYS_SHOW_STATUS_BARS_CHECKBOX: Checkbox@ALWAYS_SHOW_STATUS_BARS_CHECKBOX:
X: 310 X: 310
Y: 205 Y: 230
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Always Show Status Bars Text: Always Show Status Bars
Label@LOCALIZATION_TITLE: Label@LOCALIZATION_TITLE:
Y: 225 Y: 270
Width: PARENT_RIGHT Width: PARENT_RIGHT
Font: Bold Font: Bold
Text: Localization Text: Localization
@@ -205,7 +212,7 @@ Background@SETTINGS_PANEL:
Visible: false Visible: false
Label@LANGUAGE_LABEL: Label@LANGUAGE_LABEL:
X: 230 - WIDTH - 5 X: 230 - WIDTH - 5
Y: 244 Y: 289
Width: 75 Width: 75
Height: 25 Height: 25
Align: Right Align: Right
@@ -213,12 +220,12 @@ Background@SETTINGS_PANEL:
Visible: false Visible: false
DropDownButton@LANGUAGE_DROPDOWNBUTTON: DropDownButton@LANGUAGE_DROPDOWNBUTTON:
X: 230 X: 230
Y: 245 Y: 290
Width: 200 Width: 200
Height: 25 Height: 25
Visible: false Visible: false
Label@LANGUAGE_DESC_A: Label@LANGUAGE_DESC_A:
Y: 265 Y: 310
Width: PARENT_RIGHT Width: PARENT_RIGHT
Height: 25 Height: 25
Font: Tiny Font: Tiny
@@ -226,7 +233,7 @@ Background@SETTINGS_PANEL:
Text: Language changes will be applied after the game is restarted Text: Language changes will be applied after the game is restarted
Visible: false Visible: false
Label@LANGUAGE_DESC_B: Label@LANGUAGE_DESC_B:
Y: 280 Y: 325
Width: PARENT_RIGHT Width: PARENT_RIGHT
Height: 25 Height: 25
Font: Tiny Font: Tiny

View File

@@ -1,180 +1,179 @@
Palettes: Palettes:
cursor: cursor.pal cursor: cursor.pal
Cursors: Cursors:
mouse.shp: cursor mouse.shp: cursor
scroll-t: scroll-t:
start:1 Start: 1
scroll-tr: scroll-tr:
start:2 Start: 2
scroll-r: scroll-r:
start:3 Start: 3
scroll-br: scroll-br:
start:4 Start: 4
scroll-b: scroll-b:
start:5 Start: 5
scroll-bl: scroll-bl:
start:6 Start: 6
scroll-l: scroll-l:
start:7 Start: 7
scroll-tl: scroll-tl:
start:8 Start: 8
scroll-t-blocked: scroll-t-blocked:
start:124 Start: 124
scroll-tr-blocked: scroll-tr-blocked:
start:125 Start: 125
scroll-r-blocked: scroll-r-blocked:
start:126 Start: 126
scroll-br-blocked: scroll-br-blocked:
start:127 Start: 127
scroll-b-blocked: scroll-b-blocked:
start:128 Start: 128
scroll-bl-blocked: scroll-bl-blocked:
start:129 Start: 129
scroll-l-blocked: scroll-l-blocked:
start:130 Start: 130
scroll-tl-blocked: scroll-tl-blocked:
start:131 Start: 131
select: select:
start:15 Start: 15
length: 6 Length: 6
default: default:
start:0 Start: 0
x: -16 X: -16
y: -12 Y: -12
default-minimap: default-minimap:
start:80 Start: 80
x: -16 X: -16
y: -12 Y: -12
generic-blocked: generic-blocked:
start:9 Start: 9
generic-blocked-minimap: generic-blocked-minimap:
start:33 Start: 33
move: move:
start:10 Start: 10
length: 4 Length: 4
move-minimap: move-minimap:
start:29 Start: 29
length: 4 Length: 4
move-rough: move-rough:
start:10 Start: 10
length: 4 Length: 4
move-blocked: move-blocked:
start:14 Start: 14
move-blocked-minimap: move-blocked-minimap:
start:33 Start: 33
attack: attack:
start:195 Start: 195
length: 8 Length: 8
attack-minimap: attack-minimap:
start:203 Start: 203
length: 8 Length: 8
attackoutsiderange: attackoutsiderange:
start:21 Start: 21
length: 8 Length: 8
attackoutsiderange-minimap: attackoutsiderange-minimap:
start:134 Start: 134
length: 8 Length: 8
harvest: harvest:
start:21 Start: 21
length: 8 Length: 8
harvest-minimap: harvest-minimap:
start:134 Start: 134
length: 8 Length: 8
enter: enter:
start:113 Start: 113
length: 3 Length: 3
enter-minimap: enter-minimap:
start:139 Start: 139
length: 3 Length: 3
enter-blocked: enter-blocked:
start:212 Start: 212
length: 1 Length: 1
enter-blocked-minimap: enter-blocked-minimap:
start:33 Start: 33
c4: c4:
start:116 Start: 116
length: 3 Length: 3
c4-minimap: c4-minimap:
start:121 Start: 121
length: 3 Length: 3
guard: guard:
start:147 Start: 147
length: 1 Length: 1
guard-minimap: guard-minimap:
start:146 Start: 146
length: 1 Length: 1
capture: capture:
start:164 Start: 164
length: 3 Length: 3
capture-minimap: capture-minimap:
start:167 Start: 167
length: 3 Length: 3
heal: heal:
start:160 Start: 160
length: 4 Length: 4
heal-minimap: heal-minimap:
start:194 Start: 194
length: 1 Length: 1
ability: ability:
start:82 Start: 82
length: 8 Length: 8
ability-minimap: ability-minimap:
start:214 Start: 214
length: 8 Length: 8
# Cursors that need minimap variants # Cursors that need minimap variants
deploy: deploy:
start:59 Start: 59
length: 9 Length: 9
deploy-blocked: deploy-blocked:
start:211 Start: 211
length: 1 Length: 1
goldwrench: goldwrench:
start:170 Start: 170
length: 24 Length: 24
goldwrench-blocked: goldwrench-blocked:
start:213 Start: 213
length: 1 Length: 1
nuke: nuke:
start:90 Start: 90
length: 7 Length: 7
chrono-select: chrono-select:
start:97 Start: 97
length: 8 Length: 8
chrono-target: chrono-target:
start:105 Start: 105
length: 8 Length: 8
sell: sell:
start:68 Start: 68
length: 12 Length: 12
sell-blocked: sell-blocked:
start:119 Start: 119
length: 1 Length: 1
repair: repair:
start:35 Start: 35
length: 24 Length: 24
repair-blocked: repair-blocked:
start:120 Start: 120
length: 1 Length: 1
sell2: sell2:
start:148 Start: 148
length: 12 Length: 12
nopower.shp: cursor nopower.shp: cursor
powerdown-blocked: powerdown-blocked:
start:0 Start: 0
length: 1 Length: 1
powerdown: powerdown:
start:1 Start: 1
length: 3 Length: 3
attackmove.shp: cursor attackmove.shp: cursor
attackmove: attackmove:
start:0 Start: 0
length: 4 Length: 4
attackmove-minimap: attackmove-minimap:
start:4 Start: 4
length: 4 Length: 4

View File

@@ -1,175 +1,176 @@
Palettes: Palettes:
cursor: mousepal.pal cursor: mousepal.pal
Cursors: Cursors:
mouse.shp: cursor mouse.shp: cursor
scroll-t: #TODO scroll-t:
start: 2 Start: 2
scroll-tr: #TODO scroll-tr:
start: 3 Start: 3
scroll-r: #TODO scroll-r:
start: 4 Start: 4
scroll-br: #TODO scroll-br:
start: 5 Start: 5
scroll-b: #TODO scroll-b:
start: 6 Start: 6
scroll-bl: #TODO scroll-bl:
start: 7 Start: 7
scroll-l: #TODO scroll-l:
start: 8 Start: 8
scroll-tl: #TODO scroll-tl:
start: 9 Start: 9
scroll-t-blocked: #TODO scroll-t-blocked:
start: 10 Start: 10
scroll-tr-blocked: #TODO scroll-tr-blocked:
start: 11 Start: 11
scroll-r-blocked: #TODO scroll-r-blocked:
start: 12 Start: 12
scroll-br-blocked: #TODO scroll-br-blocked:
start: 13 Start: 13
scroll-b-blocked: #TODO scroll-b-blocked:
start: 14 Start: 14
scroll-bl-blocked: #TODO scroll-bl-blocked:
start: 15 Start: 15
scroll-l-blocked: #TODO scroll-l-blocked:
start: 16 Start: 16
scroll-tl-blocked: #TODO scroll-tl-blocked:
start: 17 Start: 17
select: select:
start:18 Start: 18
length: 12 Length: 12
default: default:
start:0 Start: 0
x: -26 X: -26
y: -20 Y: -20
default-minimap: default-minimap:
start:1 Start: 1
x: -26 X: -26
y: -20 Y: -20
generic-blocked: generic-blocked:
start:9 Start: 9
generic-blocked-minimap: generic-blocked-minimap:
start:52 Start: 52
move: move:
start:31 Start: 31
length: 10 Length: 10
move-minimap: move-minimap:
start:42 Start: 42
length: 10 Length: 10
move-rough: move-rough:
start:31 Start: 31
length: 10 Length: 10
move-blocked: move-blocked:
start:41 Start: 41
move-blocked-minimap: move-blocked-minimap:
start:52 Start: 52
attack: attack:
start: 53 Start: 53
length: 5 Length: 5
attack-blocked: # TODO: unused attack-blocked: # TODO: unused
start: 41 Start: 41
length: 1 Length: 1
attack-minimap: attack-minimap:
start: 63 Start: 63
length: 5 Length: 5
attackoutsiderange: attackoutsiderange:
start: 58 Start: 58
length: 5 Length: 5
attackoutsiderange-minimap: attackoutsiderange-minimap:
start: 63 Start: 63
length: 5 Length: 5
attackmove: #TODO attackmove:
start: 58 Start: 58
length: 5 Length: 5
attackmove-minimap: #TODO attackmove-minimap:
start:68 Start: 68
length: 5 Length: 5
harvest: #TODO harvest:
start:53 Start: 53
length: 5 Length: 5
harvest-minimap: #TODO harvest-minimap:
start:134 Start: 134
length: 8 Length: 8
enter: enter:
start: 89 Start: 89
length: 10 Length: 10
enter-minimap: enter-minimap:
start: 100 Start: 100
length: 10 Length: 10
enter-blocked: enter-blocked:
start: 99 Start: 99
length: 1 Length: 1
enter-blocked-minimap: #TODO enter-blocked-minimap:
start:33 Start: 33
c4: c4:
start: 309 Start: 309
length: 10 Length: 10
c4-minimap: #TODO c4-minimap:
start:121 Start: 121
length: 3 Length: 3
guard: guard:
start: 68 Start: 68
length: 5 Length: 5
guard-minimap: guard-minimap:
start: 73 Start: 73
length: 5 Length: 5
capture: capture:
start: 89 Start: 89
length: 10 Length: 10
capture-minimap: capture-minimap:
start: 100 Start: 100
length: 10 Length: 10
heal: heal:
start: 346 Start: 346
length: 10 Length: 10
heal-minimap: #TODO heal-minimap:
start:194 Start: 194
length: 1 Length: 1
ability: ability:
start: 78 Start: 78
length: 10 Length: 10
ability-minimap: #TODO ability-minimap:
start:214 Start: 214
length: 8 Length: 8
deploy: deploy:
start: 110 Start: 110
length: 9 Length: 9
deploy-blocked: deploy-blocked:
start: 119 Start: 119
length: 1 Length: 1
undeploy: #TODO: unused undeploy: #TODO: unused
start:120 Start: 120
length: 9 Length: 9
goldwrench: #TODO goldwrench:
start:170 Start: 170
length: 24 Length: 24
goldwrench-blocked: #TODO goldwrench-blocked: #TODO
start:213 Start: 213
length: 1 Length: 1
nuke: #TODO nuke:
start:90 Start: 90
length: 7 Length: 7
sell: sell:
start:129 Start: 129
length: 10 Length: 10
sell-minimap: #TODO: unused sell-minimap: #TODO: unused
start:139 Start: 139
length: 10 Length: 10
sell-blocked: sell-blocked:
start:149 Start: 149
length: 1 Length: 1
repair: repair:
start:170 Start: 170
length: 20 Length: 20
repair-blocked: repair-blocked:
start:190 Start: 190
length: 1 Length: 1
sell2: sell2:
start:139 Start: 139
length: 10 Length: 10
powerdown-blocked: powerdown-blocked:
start:345 Start: 345
length: 1 Length: 1
powerdown: powerdown:
start:329 Start: 329
length: 15 Length: 15