From 8894be7f45d381c0df7c3a5c18cb83370dd18887 Mon Sep 17 00:00:00 2001 From: Pyrdacor Date: Fri, 11 Sep 2020 10:54:59 +0200 Subject: [PATCH] Added decision popup, improved text popup positioning --- Ambermoon.Core/Game.cs | 113 ++++++++----- Ambermoon.Core/MapExtensions.cs | 53 ++++-- Ambermoon.Core/Render/Layer.cs | 6 +- Ambermoon.Core/Render/TextureAtlasManager.cs | 14 +- Ambermoon.Core/UI/Button.cs | 50 ++++-- Ambermoon.Core/UI/Graphics.cs | 21 ++- Ambermoon.Core/UI/ItemGrid.cs | 6 +- Ambermoon.Core/UI/Layout.cs | 159 +++++++++++------- Ambermoon.Core/UI/Popup.cs | 55 +++++- Ambermoon.Core/UI/Scrollbar.cs | 16 +- Ambermoon.Data.Common/MapEvent.cs | 17 +- .../Serialization/MapReader.cs | 16 +- Ambermoon.Renderer.OpenGL/Context.cs | 2 +- Ambermoon.Renderer.OpenGL/RenderLayer.cs | 43 ++--- Ambermoon.Renderer.OpenGL/RenderView.cs | 5 + 15 files changed, 374 insertions(+), 202 deletions(-) diff --git a/Ambermoon.Core/Game.cs b/Ambermoon.Core/Game.cs index b36c484e..6d7bd380 100644 --- a/Ambermoon.Core/Game.cs +++ b/Ambermoon.Core/Game.cs @@ -76,7 +76,7 @@ class TimedGameEvent // Note: These are not meant for ingame stuff but for fade effects etc that use real time. readonly List timedEvents = new List(); readonly Movement movement; - uint currentTicks = 0; + internal uint CurrentTicks { get; private set; } = 0; uint lastMapTicksReset = 0; uint lastMoveTicksReset = 0; readonly NameProvider nameProvider; @@ -234,10 +234,10 @@ public void Update(double deltaTime) uint add = (uint)Util.Round(TicksPerSecond * (float)deltaTime); - if (currentTicks <= uint.MaxValue - add) - currentTicks += add; + if (CurrentTicks <= uint.MaxValue - add) + CurrentTicks += add; else - currentTicks = (uint)(((long)currentTicks + add) % uint.MaxValue); + CurrentTicks = (uint)(((long)CurrentTicks + add) % uint.MaxValue); if (ingame) { @@ -247,12 +247,12 @@ public void Update(double deltaTime) } else // 2D { - var animationTicks = currentTicks >= lastMapTicksReset ? currentTicks - lastMapTicksReset : (uint)((long)currentTicks + uint.MaxValue - lastMapTicksReset); + var animationTicks = CurrentTicks >= lastMapTicksReset ? CurrentTicks - lastMapTicksReset : (uint)((long)CurrentTicks + uint.MaxValue - lastMapTicksReset); renderMap2D.UpdateAnimations(animationTicks); } } - var moveTicks = currentTicks >= lastMoveTicksReset ? currentTicks - lastMoveTicksReset : (uint)((long)currentTicks + uint.MaxValue - lastMoveTicksReset); + var moveTicks = CurrentTicks >= lastMoveTicksReset ? CurrentTicks - lastMoveTicksReset : (uint)((long)CurrentTicks + uint.MaxValue - lastMoveTicksReset); if (moveTicks >= movement.MovementTicks(is3D)) { @@ -261,10 +261,10 @@ public void Update(double deltaTime) else Move(); - lastMoveTicksReset = currentTicks; + lastMoveTicksReset = CurrentTicks; } - layout.Update(currentTicks); + layout.Update(CurrentTicks); } Position GetMousePosition(Position position) @@ -296,7 +296,7 @@ void ResetMoveKeys() keys[(int)Key.Down] = false; keys[(int)Key.Left] = false; keys[(int)Key.Right] = false; - lastMoveTicksReset = currentTicks; + lastMoveTicksReset = CurrentTicks; } public Color GetTextColor(TextColor textColor) => GetPaletteColor(51, (int)textColor); @@ -350,7 +350,7 @@ internal void Start2D(Map map, uint playerX, uint playerY, CharacterDirection di } player2D.Visible = true; - player2D.MoveTo(map, playerX, playerY, currentTicks, true, direction); + player2D.MoveTo(map, playerX, playerY, CurrentTicks, true, direction); var mapOffset = map.MapOffset; player.Position.X = mapOffset.X + (int)playerX - (int)renderMap2D.ScrollX; @@ -376,7 +376,7 @@ internal void Start3D(Map map, uint playerX, uint playerY, CharacterDirection di is3D = true; renderMap2D.Destroy(); renderMap3D.SetMap(map, playerX, playerY, direction); - player3D.SetPosition((int)playerX, (int)playerY, currentTicks); + player3D.SetPosition((int)playerX, (int)playerY, CurrentTicks); if (player2D != null) player2D.Visible = false; player.Position.X = (int)playerX; @@ -531,32 +531,32 @@ internal void Move(CursorType cursorType) switch (cursorType) { case CursorType.ArrowForward: - player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile, currentTicks); + player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile, CurrentTicks); break; case CursorType.ArrowBackward: - player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile, currentTicks); + player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile, CurrentTicks); break; case CursorType.ArrowStrafeLeft: - player3D.MoveLeft(movement.MoveSpeed3D * Global.DistancePerTile, currentTicks); + player3D.MoveLeft(movement.MoveSpeed3D * Global.DistancePerTile, CurrentTicks); break; case CursorType.ArrowStrafeRight: - player3D.MoveRight(movement.MoveSpeed3D * Global.DistancePerTile, currentTicks); + player3D.MoveRight(movement.MoveSpeed3D * Global.DistancePerTile, CurrentTicks); break; case CursorType.ArrowTurnLeft: player3D.TurnLeft(movement.TurnSpeed3D * 0.7f); - player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, currentTicks); + player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, CurrentTicks); break; case CursorType.ArrowTurnRight: player3D.TurnRight(movement.TurnSpeed3D * 0.7f); - player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, currentTicks); + player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, CurrentTicks); break; case CursorType.ArrowRotateLeft: player3D.TurnLeft(movement.TurnSpeed3D * 0.7f); - player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, currentTicks); + player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, CurrentTicks); break; case CursorType.ArrowRotateRight: player3D.TurnRight(movement.TurnSpeed3D * 0.7f); - player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, currentTicks); + player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile * 0.75f, CurrentTicks); break; default: clickMoveActive = false; @@ -602,15 +602,15 @@ bool Move2D(int x, int y) { bool diagonal = x != 0 && y != 0; - if (!player2D.Move(x, y, currentTicks)) + if (!player2D.Move(x, y, CurrentTicks)) { if (!diagonal) return false; var prevDirection = player2D.Direction; - if (!player2D.Move(0, y, currentTicks, prevDirection)) - return player2D.Move(x, 0, currentTicks, prevDirection); + if (!player2D.Move(0, y, CurrentTicks, prevDirection)) + return player2D.Move(x, 0, CurrentTicks, prevDirection); } return true; @@ -652,7 +652,7 @@ void Move() Move2D(x, -1); } else - player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile, currentTicks); + player3D.MoveForward(movement.MoveSpeed3D * Global.DistancePerTile, CurrentTicks); } if (keys[(int)Key.Down] && !keys[(int)Key.Up]) { @@ -663,7 +663,7 @@ void Move() Move2D(x, 1); } else - player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile, currentTicks); + player3D.MoveBackward(movement.MoveSpeed3D * Global.DistancePerTile, CurrentTicks); } } @@ -719,7 +719,7 @@ public void OnKeyDown(Key key, KeyModifiers modifiers) int index = key - Key.Num1; int column = index % 3; int row = 2 - index / 3; - var newCursorType = layout.PressButton(column + row * 3, currentTicks); + var newCursorType = layout.PressButton(column + row * 3, CurrentTicks); if (newCursorType != null) CursorType = newCursorType.Value; @@ -732,7 +732,7 @@ public void OnKeyDown(Key key, KeyModifiers modifiers) break; } - lastMoveTicksReset = currentTicks; + lastMoveTicksReset = CurrentTicks; } public void OnKeyUp(Key key, KeyModifiers modifiers) @@ -784,7 +784,7 @@ public void OnMouseUp(Position position, MouseButtons buttons) if (buttons.HasFlag(MouseButtons.Right)) { - layout.RightMouseUp(renderView.ScreenToGame(position), out CursorType? cursorType, currentTicks); + layout.RightMouseUp(renderView.ScreenToGame(position), out CursorType? cursorType, CurrentTicks); if (cursorType != null) CursorType = cursorType.Value; @@ -794,7 +794,7 @@ public void OnMouseUp(Position position, MouseButtons buttons) leftMouseDown = false; clickMoveActive = false; - layout.LeftMouseUp(renderView.ScreenToGame(position), out CursorType? cursorType, currentTicks); + layout.LeftMouseUp(renderView.ScreenToGame(position), out CursorType? cursorType, CurrentTicks); if (cursorType != null && cursorType != CursorType.None) CursorType = cursorType.Value; @@ -843,7 +843,7 @@ public void OnMouseDown(Position position, MouseButtons buttons) else { var cursorType = CursorType.Sword; - layout.Click(relativePosition, buttons, ref cursorType, currentTicks); + layout.Click(relativePosition, buttons, ref cursorType, CurrentTicks); CursorType = cursorType; if (InputEnable) @@ -1042,7 +1042,7 @@ void TriggerMapEvents(MapEventTrigger trigger, Position position) else // 2D { var tilePosition = renderMap2D.PositionToTile(position); - renderMap2D.TriggerEvents(player2D, trigger, (uint)tilePosition.X, (uint)tilePosition.Y, mapManager, currentTicks); + renderMap2D.TriggerEvents(player2D, trigger, (uint)tilePosition.X, (uint)tilePosition.Y, mapManager, CurrentTicks); } } @@ -1142,8 +1142,8 @@ internal void OpenPartyMember(int slot) #endregion #region Character info layout.FillArea(new Rect(208, 49, 96, 80), Color.LightGray, false); - layout.AddSprite(new Rect(208, 49, 32, 34), Graphics.UIElementOffset + (uint)UICustomGraphic.PortraitBackground, 50, true, 1); - layout.AddSprite(new Rect(208, 49, 32, 34), Graphics.PortraitOffset + partyMember.PortraitIndex - 1, 49, false, 2); + layout.AddSprite(new Rect(208, 49, 32, 34), Graphics.UICustomGraphicOffset + (uint)UICustomGraphic.PortraitBackground, 50, 1); + layout.AddSprite(new Rect(208, 49, 32, 34), Graphics.PortraitOffset + partyMember.PortraitIndex - 1, 49, 2); layout.AddText(new Rect(242, 49, 62, 7), DataNameProvider.GetRaceName(partyMember.Race)); layout.AddText(new Rect(242, 56, 62, 7), DataNameProvider.GetGenderName(partyMember.Gender)); layout.AddText(new Rect(242, 63, 62, 7), string.Format(DataNameProvider.CharacterInfoAgeString.Replace("000", "0"), @@ -1199,7 +1199,7 @@ internal void Teleport(MapChangeEvent mapChangeEvent) // in non-world 2D so subtract another 1 from y. player.MoveTo(newMap, mapChangeEvent.X - 1, mapChangeEvent.Y - (newMap.Type == MapType.Map2D && !newMap.IsWorldMap ? 2u : 1u), - currentTicks, true, mapChangeEvent.Direction); + CurrentTicks, true, mapChangeEvent.Direction); ShowMap(true); }); } @@ -1278,25 +1278,57 @@ internal void ShowChest(ChestMapEvent chestMapEvent) }); } - internal void ShowTextPopup(PopupTextEvent popupTextEvent) + internal void ShowTextPopup(PopupTextEvent popupTextEvent, Action responseHandler) { if (popupTextEvent.HasImage) { // Those always use a custom layout + layout.SetLayout(LayoutType.Event); + + // TODO ... } else { // Simple text popup - /*var popup = layout.OpenPopup(mapViewArea.Position, (mapViewArea.Width - 32) / 7, 10, true, true); // TODO: sizes and position - popup.AddText(new Rect(mapViewArea.Position + new Position(16, 16), new Size(mapViewArea.Width - 32, 10 * 7)), - Map.Texts[(int)popupTextEvent.TextIndex], TextColor.Gray);*/ var text = renderView.TextProcessor.ProcessText(Map.Texts[(int)popupTextEvent.TextIndex], nameProvider, dictionary); - layout.OpenTextPopup(text, () => InputEnable = true, true, true); + layout.OpenTextPopup(text, () => + { + InputEnable = true; + responseHandler?.Invoke(PopupTextEvent.Response.Close); + }, true, true); InputEnable = false; CursorType = CursorType.Click; } } + internal void ShowDecisionPopup(DecisionEvent decisionEvent, Action responseHandler) + { + var text = renderView.TextProcessor.ProcessText(Map.Texts[(int)decisionEvent.TextIndex], nameProvider, dictionary); + layout.OpenYesNoPopup + ( + text, + () => + { + layout.ClosePopup(false); + InputEnable = true; + responseHandler?.Invoke(PopupTextEvent.Response.Yes); + }, + () => + { + layout.ClosePopup(false); + InputEnable = true; + responseHandler?.Invoke(PopupTextEvent.Response.No); + }, + () => + { + InputEnable = true; + responseHandler?.Invoke(PopupTextEvent.Response.Close); + } + ); + InputEnable = false; + CursorType = CursorType.Sword; + } + internal void SetActivePartyMember(int index) { var partyMember = GetPartyMember(index); @@ -1366,7 +1398,10 @@ internal void CloseWindow() if (!WindowActive) return; - currentWindow = lastWindow; + if (currentWindow.Window == lastWindow.Window) + currentWindow = DefaultWindow; + else + currentWindow = lastWindow; switch (currentWindow.Window) { diff --git a/Ambermoon.Core/MapExtensions.cs b/Ambermoon.Core/MapExtensions.cs index bc78a877..1cce0561 100644 --- a/Ambermoon.Core/MapExtensions.cs +++ b/Ambermoon.Core/MapExtensions.cs @@ -5,6 +5,7 @@ namespace Ambermoon { public enum MapEventTrigger { + Always, Move, Hand, Eye, @@ -48,8 +49,35 @@ public static class MapExtensions if (!(mapEvent is PopupTextEvent popupTextEvent)) throw new AmbermoonException(ExceptionScope.Data, "Invalid text popup event."); - game.ShowTextPopup(popupTextEvent); - break; + game.ShowTextPopup(popupTextEvent, _ => + { + TriggerEventChain(map, game, player, MapEventTrigger.Always, x, y, mapManager, + game.CurrentTicks, mapEvent.Next); + }); + return null; // next event is only executed after popup response + } + case MapEventType.Decision: + { + if (!(mapEvent is DecisionEvent decisionEvent)) + throw new AmbermoonException(ExceptionScope.Data, "Invalid decision event."); + + game.ShowDecisionPopup(decisionEvent, response => + { + if (response == PopupTextEvent.Response.Yes) + { + TriggerEventChain(map, game, player, MapEventTrigger.Always, x, y, mapManager, + game.CurrentTicks, mapEvent.Next); + } + else // Close and No have the same meaning here + { + if (decisionEvent.NoEventIndex != 0xffff) + { + TriggerEventChain(map, game, player, MapEventTrigger.Always, x, y, mapManager, + game.CurrentTicks, map.Events[(int)decisionEvent.NoEventIndex]); + } + } + }); + return null; // next event is only executed after popup response } case MapEventType.ChangeTile: { @@ -136,6 +164,18 @@ public static class MapExtensions return mapEvent.Next; } + static void TriggerEventChain(Map map, Game game, IRenderPlayer player, MapEventTrigger trigger, uint x, uint y, + IMapManager mapManager, uint ticks, MapEvent firstMapEvent) + { + bool lastEventStatus = false; + var mapEvent = firstMapEvent; + + while (mapEvent != null) + { + mapEvent = ExecuteEvent(map, game, player, trigger, x, y, mapManager, ticks, mapEvent, ref lastEventStatus); + } + } + public static void TriggerEvents(this Map map, Game game, IRenderPlayer player, MapEventTrigger trigger, uint x, uint y, IMapManager mapManager, uint ticks) { @@ -150,14 +190,7 @@ public static class MapExtensions if (mapEventId == 0) return; // no map events at this position - bool lastEventStatus = false; - var mapEvent = map.EventLists[(int)mapEventId - 1]; - - while (mapEvent != null) - { - mapEvent = ExecuteEvent(map, game, player, trigger, x, y, mapManager, ticks, mapEvent, ref lastEventStatus); - } + TriggerEventChain(map, game, player, trigger, x, y, mapManager, ticks, map.EventLists[(int)mapEventId - 1]); } - } } diff --git a/Ambermoon.Core/Render/Layer.cs b/Ambermoon.Core/Render/Layer.cs index 91482ead..c5e569f0 100644 --- a/Ambermoon.Core/Render/Layer.cs +++ b/Ambermoon.Core/Render/Layer.cs @@ -22,16 +22,16 @@ public enum Layer MapForeground6, MapForeground7, MapForeground8, - UIBackground, + CombatBackground, BattleMonsterRowFarthest, BattleMonsterRowFar, BattleMonsterRowCenter, BattleMonsterRowNear, BattleMonsterRowNearest, - UIForeground, // including borders + UI, Items, - Popup, Text, + Effects, Cursor } diff --git a/Ambermoon.Core/Render/TextureAtlasManager.cs b/Ambermoon.Core/Render/TextureAtlasManager.cs index 2b9eda9f..a53e8b71 100644 --- a/Ambermoon.Core/Render/TextureAtlasManager.cs +++ b/Ambermoon.Core/Render/TextureAtlasManager.cs @@ -177,16 +177,12 @@ static Graphic Shrink(Graphic graphic) var layoutGraphics = graphicProvider.GetGraphics(GraphicType.Layout); for (int i = 0; i < layoutGraphics.Count; ++i) - AddTexture(Layer.UIBackground, UI.Graphics.LayoutOffset + (uint)i, layoutGraphics[i]); + AddTexture(Layer.UI, UI.Graphics.LayoutOffset + (uint)i, layoutGraphics[i]); var uiElementGraphics = graphicProvider.GetGraphics(GraphicType.UIElements); for (int i = 0; i < uiElementGraphics.Count; ++i) - AddTexture(Layer.UIBackground, UI.Graphics.UIElementOffset + (uint)i, uiElementGraphics[i]); - - int offset = Enum.NameCount(); // the UIGraphics follow the UICustomGraphics. - for (int i = 0; i <= (int)UIGraphic.FrameLowerRight; ++i) - AddTexture(Layer.Popup, (uint)i, uiElementGraphics[offset + i]); + AddTexture(Layer.UI, UI.Graphics.UICustomGraphicOffset + (uint)i, uiElementGraphics[i]); #endregion @@ -195,7 +191,7 @@ static Graphic Shrink(Graphic graphic) var portraits = graphicProvider.GetGraphics(GraphicType.Portrait); for (int i = 0; i < portraits.Count; ++i) - AddTexture(Layer.UIForeground, UI.Graphics.PortraitOffset + (uint)i, portraits[i]); + AddTexture(Layer.UI, UI.Graphics.PortraitOffset + (uint)i, portraits[i]); #endregion @@ -204,7 +200,7 @@ static Graphic Shrink(Graphic graphic) var pics80x80Graphics = graphicProvider.GetGraphics(GraphicType.Pics80x80); for (int i = 0; i < pics80x80Graphics.Count; ++i) - AddTexture(Layer.UIForeground, UI.Graphics.Pics80x80Offset + (uint)i, pics80x80Graphics[i]); + AddTexture(Layer.UI, UI.Graphics.Pics80x80Offset + (uint)i, pics80x80Graphics[i]); #endregion @@ -213,7 +209,7 @@ static Graphic Shrink(Graphic graphic) var eventGraphics = graphicProvider.GetGraphics(GraphicType.EventPictures); for (int i = 0; i < eventGraphics.Count; ++i) - AddTexture(Layer.Popup, UI.Graphics.EventPictureOffset + (uint)i, eventGraphics[i]); + AddTexture(Layer.UI, UI.Graphics.EventPictureOffset + (uint)i, eventGraphics[i]); #endregion diff --git a/Ambermoon.Core/UI/Button.cs b/Ambermoon.Core/UI/Button.cs index 7fb0214b..6aa1d47b 100644 --- a/Ambermoon.Core/UI/Button.cs +++ b/Ambermoon.Core/UI/Button.cs @@ -10,8 +10,8 @@ internal class Button public const int ButtonReleaseTime = 250; public const int Width = 32; public const int Height = 17; - readonly Rect area; - ButtonType buttonType; + public Rect Area { get; } + ButtonType buttonType = ButtonType.Empty; readonly ILayerSprite frameSprite; // 32x17 readonly ILayerSprite disableOverlay; readonly ILayerSprite iconSprite; // 32x13 @@ -23,25 +23,25 @@ internal class Button public Button(IRenderView renderView, Position position) { - area = new Rect(position, new Size(Width, Height)); + Area = new Rect(position, new Size(Width, Height)); frameSprite = renderView.SpriteFactory.Create(Width, Height, false, true, 3) as ILayerSprite; - disableOverlay = renderView.SpriteFactory.Create(Width - 8, Height - 6, false, true, 4) as ILayerSprite; - iconSprite = renderView.SpriteFactory.Create(Width, Height - 4, false, true, 2) as ILayerSprite; + disableOverlay = renderView.SpriteFactory.Create(Width - 8, Height - 6, false, true, 5) as ILayerSprite; + iconSprite = renderView.SpriteFactory.Create(Width, Height - 4, false, true, 4) as ILayerSprite; - var layer = renderView.GetLayer(Layer.UIBackground); + var layer = renderView.GetLayer(Layer.UI); frameSprite.Layer = layer; disableOverlay.Layer = layer; iconSprite.Layer = layer; - textureAtlas = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground); + textureAtlas = TextureAtlasManager.Instance.GetOrCreate(Layer.UI); frameSprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.ButtonFrame)); disableOverlay.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetCustomUIGraphicIndex(UICustomGraphic.ButtonDisableOverlay)); iconSprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetButtonGraphicIndex(ButtonType.Empty)); frameSprite.PaletteIndex = 50; disableOverlay.PaletteIndex = 50; - iconSprite.PaletteIndex = 49; + iconSprite.PaletteIndex = 0; frameSprite.X = position.X; frameSprite.Y = position.Y; @@ -55,6 +55,24 @@ public Button(IRenderView renderView, Position position) iconSprite.Visible = true; } + public byte DisplayLayer + { + get => (byte)(frameSprite.DisplayLayer - 3); + set + { + frameSprite.DisplayLayer = (byte)(value + 3); + iconSprite.DisplayLayer = (byte)(value + 4); + disableOverlay.DisplayLayer = (byte)(value + 5); + } + } + + public void Destroy() + { + frameSprite?.Delete(); + disableOverlay?.Delete(); + iconSprite?.Delete(); + } + public ButtonType ButtonType { get => buttonType; @@ -130,12 +148,18 @@ public bool Disabled set => disableOverlay.Visible = value; } + public void LeftMouseUp(Position position, uint currentTicks) + { + CursorType? cursorType = null; + LeftMouseUp(position, ref cursorType, currentTicks); + } + public void LeftMouseUp(Position position, ref CursorType? cursorType, uint currentTicks) { if (Disabled) return; - if (Pressed && area.Contains(position)) + if (Pressed && Area.Contains(position)) { released = true; cursorType = ExecuteActions(currentTicks); @@ -144,12 +168,18 @@ public void LeftMouseUp(Position position, ref CursorType? cursorType, uint curr Pressed = false; } + public bool LeftMouseDown(Position position, uint currentTicks) + { + CursorType? cursorType = null; + return LeftMouseDown(position, ref cursorType, currentTicks); + } + public bool LeftMouseDown(Position position, ref CursorType? cursorType, uint currentTicks) { if (Disabled) return false; - if (area.Contains(position)) + if (Area.Contains(position)) { pressedTime = DateTime.Now; Pressed = true; diff --git a/Ambermoon.Core/UI/Graphics.cs b/Ambermoon.Core/UI/Graphics.cs index c5799c50..083eba23 100644 --- a/Ambermoon.Core/UI/Graphics.cs +++ b/Ambermoon.Core/UI/Graphics.cs @@ -4,26 +4,25 @@ namespace Ambermoon.UI { internal static class Graphics { - // Background layer + // UI layer public const uint LayoutOffset = 0u; - public const uint UIElementOffset = 20u; - // Foreground layer - public const uint PortraitOffset = 0u; - public const uint Pics80x80Offset = 120u; - // Popup layer - public const uint EventPictureOffset = 20u; + public const uint PortraitOffset = 20u; + public const uint Pics80x80Offset = 150u; + public const uint EventPictureOffset = 200u; + public const uint UICustomGraphicOffset = 250u; // We load 3 things into the same layer -> GraphicType.UIElements // 1. Our own UI elements like scrollbars, etc (see UICustomGraphic) // 2. Game UI elements from the executable (see UIGraphic) // 3. Game button graphics from the executable (see ButtonType) - static readonly uint UIGraphicOffset = UIElementOffset + (uint)Enum.NameCount(); + static readonly uint UIGraphicOffset = UICustomGraphicOffset + (uint)Enum.NameCount(); static readonly uint ButtonOffset = UIGraphicOffset + (uint)Enum.NameCount(); + static readonly uint PopupFrameOffset = UIGraphicOffset; - public static uint GetScrollbarGraphicIndex(ScrollbarType scrollbarType) => UIElementOffset + (uint)scrollbarType; - public static uint GetCustomUIGraphicIndex(UICustomGraphic customGraphic) => UIElementOffset + (uint)customGraphic; + public static uint GetScrollbarGraphicIndex(ScrollbarType scrollbarType) => UICustomGraphicOffset + (uint)scrollbarType; + public static uint GetCustomUIGraphicIndex(UICustomGraphic customGraphic) => UICustomGraphicOffset + (uint)customGraphic; public static uint GetUIGraphicIndex(UIGraphic graphic) => UIGraphicOffset + (uint)graphic; public static uint GetButtonGraphicIndex(ButtonType buttonType) => ButtonOffset + (uint)buttonType; - public static uint GetPopupFrameGraphicIndex(PopupFrame frame) => (uint)frame; + public static uint GetPopupFrameGraphicIndex(PopupFrame frame) => PopupFrameOffset + (uint)frame; } } diff --git a/Ambermoon.Core/UI/ItemGrid.cs b/Ambermoon.Core/UI/ItemGrid.cs index 49a0ab71..3e49b999 100644 --- a/Ambermoon.Core/UI/ItemGrid.cs +++ b/Ambermoon.Core/UI/ItemGrid.cs @@ -52,7 +52,7 @@ public bool Disabled item?.Destroy(); } - var slotTexCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + + var slotTexCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UI).GetOffset(Graphics.UICustomGraphicOffset + (disabled ? (uint)UICustomGraphic.ItemSlotDisabled : (uint)UICustomGraphic.ItemSlotBackground)); foreach (var background in slotBackgrounds) background.TextureAtlasOffset = slotTexCoords; @@ -84,8 +84,8 @@ public bool Disabled void CreateSlotBackgrounds() { - var layer = renderView.GetLayer(Layer.UIBackground); - var texCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + (uint)UICustomGraphic.ItemSlotBackground); + var layer = renderView.GetLayer(Layer.UI); + var texCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UI).GetOffset(Graphics.UICustomGraphicOffset + (uint)UICustomGraphic.ItemSlotBackground); for (int i = 0; i < slotBackgrounds.Length; ++i) { diff --git a/Ambermoon.Core/UI/Layout.cs b/Ambermoon.Core/UI/Layout.cs index b51210e0..903392a6 100644 --- a/Ambermoon.Core/UI/Layout.cs +++ b/Ambermoon.Core/UI/Layout.cs @@ -298,8 +298,9 @@ public static DraggedItem FromExternal(ItemGrid itemGrid, int slotIndex, UIItem public LayoutType Type { get; private set; } readonly Game game; readonly ILayerSprite sprite; - readonly ITextureAtlas textureAtlasBackground; - readonly ITextureAtlas textureAtlasForeground; + readonly ITextureAtlas textureAtlas; + readonly IRenderLayer renderLayer; + readonly IRenderLayer textLayer; readonly List portraitBorders = new List(); readonly ISprite[] portraitBackgrounds = new ISprite[Game.MaxPartyMembers]; readonly ILayerSprite[] portraitBarBackgrounds = new ILayerSprite[Game.MaxPartyMembers]; @@ -328,11 +329,12 @@ public Layout(Game game, IRenderView renderView) { this.game = game; RenderView = renderView; - textureAtlasBackground = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground); - textureAtlasForeground = TextureAtlasManager.Instance.GetOrCreate(Layer.UIForeground); + textureAtlas = TextureAtlasManager.Instance.GetOrCreate(Layer.UI); + renderLayer = renderView.GetLayer(Layer.UI); + textLayer = renderView.GetLayer(Layer.Text); sprite = RenderView.SpriteFactory.Create(320, 163, false, true) as ILayerSprite; - sprite.Layer = RenderView.GetLayer(Layer.UIBackground); + sprite.Layer = renderLayer; sprite.X = Global.LayoutX; sprite.Y = Global.LayoutY; sprite.DisplayLayer = 1; @@ -361,13 +363,11 @@ void ButtonGrid_RightMouseClicked() void AddStaticSprites() { - var backgroundLayer = RenderView.GetLayer(Layer.UIBackground); - - var barBackgroundTexCoords = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.CharacterValueBarFrames)); + var barBackgroundTexCoords = textureAtlas.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.CharacterValueBarFrames)); for (int i = 0; i < Game.MaxPartyMembers; ++i) { var barBackgroundSprite = portraitBarBackgrounds[i] = RenderView.SpriteFactory.Create(16, 36, false, true) as ILayerSprite; - barBackgroundSprite.Layer = backgroundLayer; + barBackgroundSprite.Layer = renderLayer; barBackgroundSprite.PaletteIndex = 49; barBackgroundSprite.TextureAtlasOffset = barBackgroundTexCoords; barBackgroundSprite.X = Global.PartyMemberPortraitAreas[i].Left + 33; @@ -377,9 +377,9 @@ void AddStaticSprites() // Left portrait border var sprite = RenderView.SpriteFactory.Create(16, 36, false, true); - sprite.Layer = backgroundLayer; + sprite.Layer = renderLayer; sprite.PaletteIndex = 49; - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.LeftPortraitBorder)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.LeftPortraitBorder)); sprite.X = 0; sprite.Y = 0; sprite.Visible = true; @@ -387,9 +387,9 @@ void AddStaticSprites() // Right portrait border sprite = RenderView.SpriteFactory.Create(16, 36, false, true); - sprite.Layer = backgroundLayer; + sprite.Layer = renderLayer; sprite.PaletteIndex = 49; - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.RightPortraitBorder)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.RightPortraitBorder)); sprite.X = Global.VirtualScreenWidth - 16; sprite.Y = 0; sprite.Visible = true; @@ -399,18 +399,18 @@ void AddStaticSprites() for (int i = 0; i < Game.MaxPartyMembers; ++i) { sprite = RenderView.SpriteFactory.Create(32, 1, false, true); - sprite.Layer = backgroundLayer; + sprite.Layer = renderLayer; sprite.PaletteIndex = 49; - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetCustomUIGraphicIndex(UICustomGraphic.PortraitBorder)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetCustomUIGraphicIndex(UICustomGraphic.PortraitBorder)); sprite.X = 16 + i * 48; sprite.Y = 0; sprite.Visible = true; portraitBorders.Add(sprite); sprite = RenderView.SpriteFactory.Create(32, 1, false, true); - sprite.Layer = backgroundLayer; + sprite.Layer = renderLayer; sprite.PaletteIndex = 49; - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetCustomUIGraphicIndex(UICustomGraphic.PortraitBorder)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetCustomUIGraphicIndex(UICustomGraphic.PortraitBorder)); sprite.X = 16 + i * 48; sprite.Y = 35; sprite.Visible = true; @@ -418,16 +418,16 @@ void AddStaticSprites() // LP shadow characterBars[i * 4 + 0] = new Bar(barAreas, CreateArea(new Rect((i + 1) * 48 + 2, 19, 1, 16), - game.GetPaletteColor(50, (int)NamedPaletteColors.LPBarShadow), false, FilledAreaType.CharacterBar)); + game.GetPaletteColor(50, (int)NamedPaletteColors.LPBarShadow), 1, FilledAreaType.CharacterBar)); // LP fill characterBars[i * 4 + 1] = new Bar(barAreas, CreateArea(new Rect((i + 1) * 48 + 3, 19, 3, 16), - game.GetPaletteColor(50, (int)NamedPaletteColors.LPBar), false, FilledAreaType.CharacterBar)); + game.GetPaletteColor(50, (int)NamedPaletteColors.LPBar), 1, FilledAreaType.CharacterBar)); // SP shadow characterBars[i * 4 + 2] = new Bar(barAreas, CreateArea(new Rect((i + 1) * 48 + 10, 19, 1, 16), - game.GetPaletteColor(50, (int)NamedPaletteColors.SPBarShadow), false, FilledAreaType.CharacterBar)); + game.GetPaletteColor(50, (int)NamedPaletteColors.SPBarShadow), 1, FilledAreaType.CharacterBar)); // SP fill characterBars[i * 4 + 3] = new Bar(barAreas, CreateArea(new Rect((i + 1) * 48 + 11, 19, 3, 16), - game.GetPaletteColor(50, (int)NamedPaletteColors.SPBar), false, FilledAreaType.CharacterBar)); + game.GetPaletteColor(50, (int)NamedPaletteColors.SPBar), 1, FilledAreaType.CharacterBar)); } } @@ -442,7 +442,7 @@ public void SetLayout(LayoutType layoutType, uint? ticksPerMovement = null) } else { - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.LayoutOffset + (uint)(layoutType - 1)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.LayoutOffset + (uint)(layoutType - 1)); sprite.Visible = true; } @@ -458,8 +458,8 @@ public void OpenOptionMenu() LayoutType.Map3D => new Rect(Global.Map3DViewX, Global.Map3DViewY, Global.Map3DViewWidth, Global.Map3DViewHeight), _ => throw new AmbermoonException(ExceptionScope.Application, "Open option menu from another open window is not supported.") }; - AddSprite(area, Graphics.GetCustomUIGraphicIndex(UICustomGraphic.MapDisableOverlay), 49, true, 1); - AddSprite(new Rect(32, 82, 144, 26), Graphics.GetCustomUIGraphicIndex(UICustomGraphic.BiggerInfoBox), 49, true, 2); + AddSprite(area, Graphics.GetCustomUIGraphicIndex(UICustomGraphic.MapDisableOverlay), 49, 1); + AddSprite(new Rect(32, 82, 144, 26), Graphics.GetCustomUIGraphicIndex(UICustomGraphic.BiggerInfoBox), 49, 2); var version = System.Reflection.Assembly.GetEntryAssembly().GetName().Version; AddText(new Rect(32, 84, 144, 26), $"Ambermoon.net V{version.Major}.{version.Minor}.{version.Build:00}^{game.DataNameProvider.DataVersionString}^{game.DataNameProvider.DataInfoString}", @@ -493,35 +493,71 @@ internal Popup OpenPopup(Position position, int columns, int rows, bool disableB return activePopup; } - internal Popup OpenTextPopup(IText text, Action closeAction, bool disableButtons = false, bool closeOnClick = true) + Popup OpenTextPopup(IText text, Position position, int maxWidth, int maxTextHeight, + bool disableButtons = false, bool closeOnClick = true) { - // min text position = 32, 68 - // max text size = 256, 112 - // max 16 text rows - const int maxTextWidth = 256; - const int maxTextHeight = 112; var processedText = RenderView.TextProcessor.WrapText(text, - new Rect((Global.VirtualScreenWidth - maxTextWidth) / 2, 0, maxTextWidth, int.MaxValue), + new Rect(0, 0, maxWidth, int.MaxValue), new Size(Global.GlyphWidth, Global.GlyphLineHeight)); - var textBounds = new Rect(32, 69, maxTextWidth, Math.Min(processedText.LineCount * Global.GlyphLineHeight, maxTextHeight)); - var renderText = RenderView.RenderTextFactory.Create(RenderView.GetLayer(Layer.Text), + var textBounds = new Rect(position.X + 16, position.Y + 16, maxWidth, + Math.Min(processedText.LineCount * Global.GlyphLineHeight, maxTextHeight)); + int popupRows = Math.Max(4, 2 + (textBounds.Height + 15) / 16); // at least 4 rows + textBounds.Position.Y += ((popupRows - 2) * 16 - textBounds.Height) / 2; + var renderText = RenderView.RenderTextFactory.Create(textLayer, processedText, TextColor.Gray, true, textBounds); - int popupColumns = 2 + (textBounds.Width + 15) / 16; - int popupRows = 2 + (textBounds.Height + 15) / 16; - var popupArea = Rect.Create(textBounds.Center, new Size(popupColumns * 16, popupRows * 16)); - activePopup = new Popup(game, RenderView, popupArea.Position, popupColumns, popupRows) + activePopup = new Popup(game, RenderView, position, 18, popupRows) { DisableButtons = disableButtons, CloseOnClick = closeOnClick }; activePopup.AddText(renderText); + return activePopup; + } + + internal Popup OpenTextPopup(IText text, Action closeAction, bool disableButtons = false, bool closeOnClick = true) + { + const int maxTextWidth = 256; + const int maxTextHeight = 112; + var popup = OpenTextPopup(text, new Position(16, 53), maxTextWidth, maxTextHeight, disableButtons, closeOnClick); + popup.Closed += closeAction; + return popup; + } + + internal Popup OpenYesNoPopup(IText text, Action yesAction, Action noAction, Action closeAction) + { + const int maxTextWidth = 192; + var processedText = RenderView.TextProcessor.WrapText(text, + new Rect(48, 0, maxTextWidth, int.MaxValue), + new Size(Global.GlyphWidth, Global.GlyphLineHeight)); + var renderText = RenderView.RenderTextFactory.Create(textLayer, + processedText, TextColor.Gray, true, new Rect(48, 95, maxTextWidth, 28)); + activePopup = new Popup(game, RenderView, new Position(32, 74), 14, 5) + { + DisableButtons = true, + CloseOnClick = false + }; + activePopup.AddText(renderText); activePopup.Closed += closeAction; + + var yesButton = activePopup.AddButton(new Position(111, 121)); + var noButton = activePopup.AddButton(new Position(143, 121)); + + yesButton.DisplayLayer = 245; + noButton.DisplayLayer = 250; + + yesButton.ButtonType = ButtonType.Yes; + noButton.ButtonType = ButtonType.No; + + yesButton.Action = yesAction; + noButton.Action = noAction; + return activePopup; } - internal void ClosePopup() + internal void ClosePopup(bool raiseEvent = true) { - activePopup?.OnClosed(); + if (raiseEvent) + activePopup?.OnClosed(); activePopup?.Destroy(); activePopup = null; } @@ -665,15 +701,15 @@ public void SetActiveCharacter(int slot, List partyMembers) public void SetCharacter(int slot, PartyMember partyMember) { var sprite = portraits[slot] ??= RenderView.SpriteFactory.Create(32, 34, false, true, 1); - sprite.Layer = RenderView.GetLayer(partyMember == null || !partyMember.Alive ? Layer.UIBackground : Layer.UIForeground); + sprite.Layer = renderLayer; sprite.X = Global.PartyMemberPortraitAreas[slot].Left + 1; sprite.Y = Global.PartyMemberPortraitAreas[slot].Top + 1; if (partyMember == null) - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.EmptyCharacterSlot)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.EmptyCharacterSlot)); else if (!partyMember.Alive) - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.Skull)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.Skull)); else - sprite.TextureAtlasOffset = textureAtlasForeground.GetOffset(Graphics.PortraitOffset + partyMember.PortraitIndex - 1); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.PortraitOffset + partyMember.PortraitIndex - 1); sprite.PaletteIndex = 49; sprite.Visible = true; @@ -691,14 +727,14 @@ public void SetCharacter(int slot, PartyMember partyMember) else { sprite = portraitBackgrounds[slot] ??= RenderView.SpriteFactory.Create(32, 34, false, true, 0); - sprite.Layer = RenderView.GetLayer(Layer.UIBackground); + sprite.Layer = renderLayer; sprite.X = Global.PartyMemberPortraitAreas[slot].Left + 1; sprite.Y = Global.PartyMemberPortraitAreas[slot].Top + 1; - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.UIElementOffset + (uint)UICustomGraphic.PortraitBackground); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.UICustomGraphicOffset + (uint)UICustomGraphic.PortraitBackground); sprite.PaletteIndex = 50; sprite.Visible = true; - var text = portraitNames[slot] ??= RenderView.RenderTextFactory.Create(RenderView.GetLayer(Layer.Text), + var text = portraitNames[slot] ??= RenderView.RenderTextFactory.Create(textLayer, RenderView.TextProcessor.CreateText(partyMember.Name.Substring(0, Math.Min(5, partyMember.Name.Length))), TextColor.Red, true, new Rect(Global.PartyMemberPortraitAreas[slot].Left + 2, Global.PartyMemberPortraitAreas[slot].Top + 31, 30, 6), TextAlign.Center); text.DisplayLayer = 1; @@ -720,24 +756,24 @@ void FillCharacterBars(int slot, PartyMember partyMember) characterBars[slot * 4 + 3].Fill(spPercentage); } - public void AddSprite(Rect rect, uint textureIndex, byte paletteIndex, bool background, byte displayLayer = 0) + public void AddSprite(Rect rect, uint textureIndex, byte paletteIndex, byte displayLayer = 2) { var sprite = RenderView.SpriteFactory.Create(rect.Width, rect.Height, false, true) as ILayerSprite; - sprite.TextureAtlasOffset = (background ? textureAtlasBackground : textureAtlasForeground).GetOffset(textureIndex); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(textureIndex); sprite.DisplayLayer = displayLayer; sprite.X = rect.Left; sprite.Y = rect.Top; sprite.PaletteIndex = paletteIndex; - sprite.Layer = RenderView.GetLayer(background ? Layer.UIBackground : Layer.UIForeground); + sprite.Layer = renderLayer; sprite.Visible = true; additionalSprites.Add(sprite); } - public void AddText(Rect rect, string text, TextColor color = TextColor.White, TextAlign textAlign = TextAlign.Left, byte displayLayer = 1) + public void AddText(Rect rect, string text, TextColor color = TextColor.White, TextAlign textAlign = TextAlign.Left, byte displayLayer = 2) { var renderText = RenderView.RenderTextFactory.Create ( - RenderView.GetLayer(Layer.Text), + textLayer, RenderView.TextProcessor.CreateText(text), color, true, rect, textAlign ); @@ -756,11 +792,11 @@ public void Set80x80Picture(Picture80x80 picture) else { var sprite = sprite80x80Picture ??= RenderView.SpriteFactory.Create(80, 80, false, true); - sprite.TextureAtlasOffset = textureAtlasForeground.GetOffset(Graphics.Pics80x80Offset + (uint)(picture - 1)); + sprite.TextureAtlasOffset = textureAtlas.GetOffset(Graphics.Pics80x80Offset + (uint)(picture - 1)); sprite.X = Global.LayoutX + 16; sprite.Y = Global.LayoutY + 6; sprite.PaletteIndex = 49; - sprite.Layer = RenderView.GetLayer(Layer.UIForeground); + sprite.Layer = renderLayer; sprite.Visible = true; } } @@ -777,11 +813,11 @@ public void AddItemGrid(ItemGrid itemGrid) itemGrids.Add(itemGrid); } - IColoredRect CreateArea(Rect rect, Color color, bool topMost, FilledAreaType type = FilledAreaType.Custom) + IColoredRect CreateArea(Rect rect, Color color, byte displayLayer = 0, FilledAreaType type = FilledAreaType.Custom) { var coloredRect = RenderView.ColoredRectFactory.Create(rect.Width, rect.Height, - color, (byte)(topMost ? 255 : type == FilledAreaType.CharacterBar ? 1 : 0)); - coloredRect.Layer = RenderView.GetLayer(topMost ? Layer.Popup : Layer.UIBackground); + color, displayLayer); + coloredRect.Layer = type == FilledAreaType.FadeEffect ? RenderView.GetLayer(Layer.Effects) : renderLayer; coloredRect.X = rect.Left; coloredRect.Y = rect.Top; coloredRect.Visible = true; @@ -802,13 +838,13 @@ IColoredRect CreateArea(Rect rect, Color color, bool topMost, FilledAreaType typ public FilledArea FillArea(Rect rect, Color color, bool topMost) { - return new FilledArea(filledAreas, CreateArea(rect, color, topMost)); + return new FilledArea(filledAreas, CreateArea(rect, color, (byte)(topMost ? 245 : 0))); } public void AddColorFader(Rect rect, Color startColor, Color endColor, int durationInMilliseconds, bool removeWhenFinished, DateTime? startTime = null) { - fadeEffects.Add(new FadeEffect(fadeEffectAreas, CreateArea(rect, startColor, true, FilledAreaType.FadeEffect), startColor, + fadeEffects.Add(new FadeEffect(fadeEffectAreas, CreateArea(rect, startColor, 255, FilledAreaType.FadeEffect), startColor, endColor, durationInMilliseconds, startTime ?? DateTime.Now, removeWhenFinished)); } @@ -835,6 +871,7 @@ public FilledArea FillArea(Rect rect, Color color, bool topMost) public void Update(uint currentTicks) { buttonGrid.Update(currentTicks); + activePopup?.Update(currentTicks); for (int i = fadeEffects.Count - 1; i >= 0; --i) { @@ -883,6 +920,12 @@ public void LeftMouseUp(Position position, out CursorType? newCursorType, uint c { newCursorType = null; + if (PopupActive) + { + activePopup.LeftMouseUp(position); + return; + } + buttonGrid.MouseUp(position, MouseButtons.Left, out CursorType? cursorType, currentTicks); if (cursorType != null) diff --git a/Ambermoon.Core/UI/Popup.cs b/Ambermoon.Core/UI/Popup.cs index 901ae78b..fd0308e0 100644 --- a/Ambermoon.Core/UI/Popup.cs +++ b/Ambermoon.Core/UI/Popup.cs @@ -15,6 +15,7 @@ internal class Popup readonly List texts = new List(); readonly List filledAreas = new List(); readonly List sprites = new List(); + readonly List