diff --git a/OpenRA.Game/Graphics/HardwareCursor.cs b/OpenRA.Game/Graphics/HardwareCursor.cs index 1d044bc3e5..373037ecfc 100644 --- a/OpenRA.Game/Graphics/HardwareCursor.cs +++ b/OpenRA.Game/Graphics/HardwareCursor.cs @@ -20,12 +20,26 @@ namespace OpenRA.Graphics { readonly Dictionary hardwareCursors = new Dictionary(); readonly CursorProvider cursorProvider; + readonly Dictionary sprites = new Dictionary(); + readonly SheetBuilder sheetBuilder; + readonly HardwarePalette hardwarePalette = new HardwarePalette(); + readonly Cache paletteReferences; + CursorSequence cursor; + bool isLocked = false; + int2 lockedPosition; public HardwareCursor(CursorProvider cursorProvider) { this.cursorProvider = cursorProvider; + paletteReferences = new Cache(CreatePaletteReference); + foreach (var p in cursorProvider.Palettes) + hardwarePalette.AddPalette(p.Key, p.Value, false); + + hardwarePalette.Initialize(); + + sheetBuilder = new SheetBuilder(SheetType.Indexed); foreach (var kv in cursorProvider.Cursors) { var palette = cursorProvider.Palettes[kv.Value.Palette]; @@ -34,11 +48,22 @@ namespace OpenRA.Graphics .ToArray(); hardwareCursors.Add(kv.Key, hc); + + var s = kv.Value.Frames.Select(a => sheetBuilder.Add(a)).ToArray(); + sprites.Add(kv.Key, s); } + sheetBuilder.Current.ReleaseBuffer(); + Update(); } + PaletteReference CreatePaletteReference(string name) + { + var pal = hardwarePalette.GetPalette(name); + return new PaletteReference(name, hardwarePalette.GetPaletteIndex(name), pal, hardwarePalette); + } + IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence) { var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2; @@ -96,6 +121,7 @@ namespace OpenRA.Graphics int frame; int ticks; + public void Tick() { if (cursor == null || cursor.Length == 1) @@ -123,9 +149,35 @@ namespace OpenRA.Graphics } } - public void Render(Renderer renderer) { } + public void Render(Renderer renderer) + { + if (cursor.Name == null || !isLocked) + return; - public int Frame { get { return frame; } } + var cursorSequence = cursorProvider.GetCursorSequence(cursor.Name); + var cursorSprite = sprites[cursor.Name][frame]; + + var cursorOffset = cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2(); + + renderer.SetPalette(hardwarePalette); + renderer.SpriteRenderer.DrawSprite(cursorSprite, + lockedPosition - cursorOffset, + paletteReferences[cursorSequence.Palette], + cursorSprite.Size); + } + + public void Lock() + { + lockedPosition = Viewport.LastMousePos; + Game.Renderer.Window.SetRelativeMouseMode(true); + isLocked = true; + } + + public void Unlock() + { + Game.Renderer.Window.SetRelativeMouseMode(false); + isLocked = false; + } public void Dispose() { @@ -133,6 +185,7 @@ namespace OpenRA.Graphics foreach (var cursor in cursors.Value) cursor.Dispose(); + sheetBuilder.Dispose(); hardwareCursors.Clear(); } } diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 37119ac9c8..a6aa16863b 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -52,6 +52,7 @@ namespace OpenRA IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot); void SetHardwareCursor(IHardwareCursor cursor); + void SetRelativeMouseMode(bool mode); } public interface IGraphicsContext : IDisposable diff --git a/OpenRA.Game/Graphics/SoftwareCursor.cs b/OpenRA.Game/Graphics/SoftwareCursor.cs index acdd2dd0e6..0e1808e8ed 100644 --- a/OpenRA.Game/Graphics/SoftwareCursor.cs +++ b/OpenRA.Game/Graphics/SoftwareCursor.cs @@ -21,7 +21,8 @@ namespace OpenRA.Graphics void Render(Renderer renderer); void SetCursor(string cursor); void Tick(); - int Frame { get; } + void Lock(); + void Unlock(); } public sealed class SoftwareCursor : ICursor @@ -32,6 +33,9 @@ namespace OpenRA.Graphics readonly CursorProvider cursorProvider; readonly SheetBuilder sheetBuilder; + bool isLocked = false; + int2 lockedPosition; + public SoftwareCursor(CursorProvider cursorProvider) { this.cursorProvider = cursorProvider; @@ -78,7 +82,7 @@ namespace OpenRA.Graphics return; var cursorSequence = cursorProvider.GetCursorSequence(cursorName); - var cursorSprite = sprites[cursorName][Frame]; + var cursorSprite = sprites[cursorName][(int)cursorFrame % cursorSequence.Length]; var cursorSize = CursorProvider.CursorViewportZoomed ? 2.0f * cursorSprite.Size : cursorSprite.Size; var cursorOffset = CursorProvider.CursorViewportZoomed ? @@ -86,19 +90,24 @@ namespace OpenRA.Graphics cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2(); renderer.SetPalette(palette); + var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos; renderer.SpriteRenderer.DrawSprite(cursorSprite, - Viewport.LastMousePos - cursorOffset, + mousePos - cursorOffset, paletteReferences[cursorSequence.Palette], cursorSize); } - public int Frame + public void Lock() { - get - { - var cursorSequence = cursorProvider.GetCursorSequence(cursorName); - return (int)cursorFrame % cursorSequence.Length; - } + Game.Renderer.Window.SetRelativeMouseMode(true); + lockedPosition = Viewport.LastMousePos; + isLocked = true; + } + + public void Unlock() + { + Game.Renderer.Window.SetRelativeMouseMode(false); + isLocked = false; } public void Dispose() diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs index 98e5df3c5f..31e48a0567 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/AirstrikePower.cs @@ -39,12 +39,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("Enables the player directional targeting")] public readonly bool UseDirectionalTarget = false; - [Desc("Placeholder cursor animation for the target cursor when using directional targeting.")] - public readonly string TargetPlaceholderCursorAnimation = null; - - [Desc("Palette for placeholder cursor animation.")] - public readonly string TargetPlaceholderCursorPalette = "chrome"; - [Desc("Animation used to render the direction arrows.")] public readonly string DirectionArrowAnimation = null; @@ -75,8 +69,7 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.SelectTargetSpeechNotification, self.Owner.Faction.InternalName); - self.World.OrderGenerator = new SelectDirectionalTarget(self.World, order, manager, Info.Cursor, - info.TargetPlaceholderCursorAnimation, info.DirectionArrowAnimation, info.TargetPlaceholderCursorPalette, info.DirectionArrowPalette); + self.World.OrderGenerator = new SelectDirectionalTarget(self.World, order, manager, Info.Cursor, info.DirectionArrowAnimation, info.DirectionArrowPalette); } else base.SelectTarget(self, order, manager); diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs index 432ad9ee28..3ec43af676 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/ParatroopersPower.cs @@ -53,12 +53,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("Enables the player directional targeting")] public readonly bool UseDirectionalTarget = false; - [Desc("Placeholder cursor animation for the target cursor when using directional targeting.")] - public readonly string TargetPlaceholderCursorAnimation = null; - - [Desc("Palette for placeholder cursor animation.")] - public readonly string TargetPlaceholderCursorPalette = "chrome"; - [Desc("Animation used to render the direction arrows.")] public readonly string DirectionArrowAnimation = null; @@ -89,8 +83,7 @@ namespace OpenRA.Mods.Common.Traits Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.SelectTargetSpeechNotification, self.Owner.Faction.InternalName); - self.World.OrderGenerator = new SelectDirectionalTarget(self.World, order, manager, Info.Cursor, - info.TargetPlaceholderCursorAnimation, info.DirectionArrowAnimation, info.TargetPlaceholderCursorPalette, info.DirectionArrowPalette); + self.World.OrderGenerator = new SelectDirectionalTarget(self.World, order, manager, Info.Cursor, info.DirectionArrowAnimation, info.DirectionArrowPalette); } else base.SelectTarget(self, order, manager); diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/SelectDirectionalTarget.cs b/OpenRA.Mods.Common/Traits/SupportPowers/SelectDirectionalTarget.cs index 556876eba8..8a03380482 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/SelectDirectionalTarget.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/SelectDirectionalTarget.cs @@ -22,9 +22,7 @@ namespace OpenRA.Mods.Common.Traits readonly string order; readonly SupportPowerManager manager; readonly string cursor; - readonly string targetPlaceholderCursorPalette; readonly string directionArrowPalette; - readonly Animation targetCursor; readonly string[] arrows = { "arrow-t", "arrow-tl", "arrow-l", "arrow-bl", "arrow-b", "arrow-br", "arrow-r", "arrow-tr" }; readonly Arrow[] directionArrows; @@ -34,24 +32,17 @@ namespace OpenRA.Mods.Common.Traits int2 dragLocation; bool activated; bool dragStarted; - bool hideMouse = true; Arrow currentArrow; - public SelectDirectionalTarget(World world, string order, SupportPowerManager manager, string cursor, string targetPlaceholderCursorAnimation, - string directionArrowAnimation, string targetPlaceholderCursorPalette, string directionArrowPalette) + public SelectDirectionalTarget(World world, string order, SupportPowerManager manager, string cursor, + string directionArrowAnimation, string directionArrowPalette) { this.order = order; this.manager = manager; this.cursor = cursor; - this.targetPlaceholderCursorPalette = targetPlaceholderCursorPalette; + this.directionArrowPalette = directionArrowPalette; - targetCursor = new Animation(world, targetPlaceholderCursorAnimation); - targetCursor.PlayRepeating("cursor"); - - for (var i = 0; i < Game.Cursor.Frame; i++) - targetCursor.Tick(); - directionArrows = LoadArrows(directionArrowAnimation, world, arrows.Length); } @@ -70,6 +61,7 @@ namespace OpenRA.Mods.Common.Traits targetCell = cell; targetLocation = mi.Location; activated = true; + Game.Cursor.Lock(); } yield break; @@ -101,8 +93,6 @@ namespace OpenRA.Mods.Common.Traits void IOrderGenerator.Tick(World world) { - targetCursor.Tick(); - // Cancel the OG if we can't use the power if (!manager.Powers.ContainsKey(order)) world.CancelInputMode(); @@ -118,28 +108,16 @@ namespace OpenRA.Mods.Common.Traits IEnumerable IOrderGenerator.RenderAboveShroud(WorldRenderer wr, World world) { if (!activated) - return Enumerable.Empty(); + yield break; - var targetPalette = wr.Palette(targetPlaceholderCursorPalette); - - var location = activated ? targetLocation : Viewport.LastMousePos; - var worldPx = wr.Viewport.ViewToWorldPx(location); + var worldPx = wr.Viewport.ViewToWorldPx(targetLocation); var worldPos = wr.ProjectedPosition(worldPx); - var renderables = new List(targetCursor.Render(worldPos, WVec.Zero, -511, targetPalette, 1 / wr.Viewport.Zoom)); if (IsOutsideDragZone) { var directionPalette = wr.Palette(directionArrowPalette); - renderables.Add(new SpriteRenderable(currentArrow.Sprite, worldPos, WVec.Zero, -511, directionPalette, 1 / wr.Viewport.Zoom, true)); + yield return new SpriteRenderable(currentArrow.Sprite, worldPos, WVec.Zero, -511, directionPalette, 1 / wr.Viewport.Zoom, true); } - - if (hideMouse) - { - hideMouse = false; - Game.RunAfterTick(() => Game.HideCursor = true); - } - - return renderables; } string IOrderGenerator.GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { return cursor; } @@ -149,7 +127,7 @@ namespace OpenRA.Mods.Common.Traits void IOrderGenerator.Deactivate() { if (activated) - Game.HideCursor = false; + Game.Cursor.Unlock(); } // Starting at (0, -1) and rotating in CCW diff --git a/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs b/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs index 570691adf2..652870fffd 100644 --- a/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs +++ b/OpenRA.Platforms.Default/Sdl2PlatformWindow.cs @@ -30,6 +30,7 @@ namespace OpenRA.Platforms.Default Size windowSize; Size surfaceSize; float windowScale; + int2 mousePosition; internal IntPtr Window { @@ -276,6 +277,22 @@ namespace OpenRA.Platforms.Default } } + public void SetRelativeMouseMode(bool mode) + { + if (mode) + { + int x, y; + SDL.SDL_GetMouseState(out x, out y); + mousePosition = new int2(x, y); + SDL.SDL_SetRelativeMouseMode(SDL.SDL_bool.SDL_TRUE); + } + else + { + SDL.SDL_SetRelativeMouseMode(SDL.SDL_bool.SDL_FALSE); + SDL.SDL_WarpMouseInWindow(window, mousePosition.X, mousePosition.Y); + } + } + internal void WindowSizeChanged() { // The ratio between pixels and points can change when moving between displays in OSX diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index 11f04f8337..f3723dfedc 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -655,7 +655,6 @@ HQ: ClockSequence: clock CircleSequence: circles UseDirectionalTarget: True - TargetPlaceholderCursorAnimation: airstriketarget DirectionArrowAnimation: airstrikedirection SupportPowerPaletteOrder: 10 SupportPowerChargeBar: diff --git a/mods/cnc/sequences/misc.yaml b/mods/cnc/sequences/misc.yaml index 49cccb9b0b..b2d2ccc7bd 100644 --- a/mods/cnc/sequences/misc.yaml +++ b/mods/cnc/sequences/misc.yaml @@ -430,12 +430,6 @@ smokland: Tick: 120 ZOffset: 1023 -airstriketarget: - cursor: mouse2 - Start: 88 - Length: 8 - Tick: 80 - airstrikedirection: arrow-t: mouse2 Start: 1 diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 15a5f224fc..c7b730a55a 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -1487,7 +1487,6 @@ AFLD: ClockSequence: clock CircleSequence: circles UseDirectionalTarget: True - TargetPlaceholderCursorAnimation: paratarget DirectionArrowAnimation: paradirection SupportPowerPaletteOrder: 60 ParatroopersPower@paratroopers: @@ -1509,7 +1508,6 @@ AFLD: ClockSequence: clock CircleSequence: circles UseDirectionalTarget: True - TargetPlaceholderCursorAnimation: paratarget DirectionArrowAnimation: paradirection SupportPowerPaletteOrder: 50 AirstrikePower@parabombs: @@ -1532,7 +1530,6 @@ AFLD: ClockSequence: clock CircleSequence: circles UseDirectionalTarget: True - TargetPlaceholderCursorAnimation: paratarget DirectionArrowAnimation: paradirection SupportPowerPaletteOrder: 40 ProductionBar: diff --git a/mods/ra/sequences/misc.yaml b/mods/ra/sequences/misc.yaml index 0691eef849..1ce80c60a9 100644 --- a/mods/ra/sequences/misc.yaml +++ b/mods/ra/sequences/misc.yaml @@ -623,12 +623,6 @@ ctflag: Length: * UseTilesetExtension: true -paratarget: - cursor: mouse - Start: 82 - Length: 8 - Tick: 80 - paradirection: arrow-t: mouse Start: 1