diff --git a/Ambermoon.Common/Ambermoon.Common.csproj b/Ambermoon.Common/Ambermoon.Common.csproj index 398956fe..85d86899 100644 --- a/Ambermoon.Common/Ambermoon.Common.csproj +++ b/Ambermoon.Common/Ambermoon.Common.csproj @@ -8,7 +8,7 @@ Github Library that contains basic stuff for other Ambermoon libraries. true - 1.0.3 + 1.0.4 diff --git a/Ambermoon.Common/EnumExtensions.cs b/Ambermoon.Common/EnumExtensions.cs index ecce8931..0bb0a081 100644 --- a/Ambermoon.Common/EnumExtensions.cs +++ b/Ambermoon.Common/EnumExtensions.cs @@ -2,14 +2,10 @@ { public static class Enum { - public static TEnum[] GetValues() - { - return (TEnum[])System.Enum.GetValues(typeof(TEnum)); - } + public static TEnum[] GetValues() => (TEnum[])System.Enum.GetValues(typeof(TEnum)); - public static string GetName(TEnum value) - { - return System.Enum.GetName(typeof(TEnum), value); - } + public static string GetName(TEnum value) => System.Enum.GetName(typeof(TEnum), value); + + public static int NameCount() => System.Enum.GetNames(typeof(TEnum)).Length; } } diff --git a/Ambermoon.Core/Game.cs b/Ambermoon.Core/Game.cs index f6d708dc..562e70fb 100644 --- a/Ambermoon.Core/Game.cs +++ b/Ambermoon.Core/Game.cs @@ -313,7 +313,11 @@ public void Start(Savegame savegame) if (savegame.CurrentPartyMemberIndices[i] != 0) { var partyMember = savegame.GetPartyMember(i); - layout.SetPortrait(i, partyMember.PortraitIndex, partyMember.Name); + layout.SetPortrait(i, partyMember.PortraitIndex, partyMember.Name, !partyMember.Alive); + } + else + { + layout.SetPortrait(i, 0, null, false); } } CurrentPartyMember = GetPartyMember(currentSavegame.ActivePartyMemberSlot); @@ -843,7 +847,7 @@ 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)UIElementGraphic.PortraitBackground, 50, true, 1); + 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.AddText(new Rect(242, 49, 62, 7), dataNameProvider.GetRaceName(partyMember.Race)); layout.AddText(new Rect(242, 56, 62, 7), dataNameProvider.GetGenderName(partyMember.Gender)); @@ -983,7 +987,7 @@ internal void SetActivePartyMember(int index) { currentSavegame.ActivePartyMemberSlot = index; CurrentPartyMember = partyMember; - layout.SetActivePortrait(index); + layout.SetActivePortrait(index, currentSavegame.PartyMembers); } } diff --git a/Ambermoon.Core/UI/Global.cs b/Ambermoon.Core/UI/Global.cs index 3bb564a5..22ae79a1 100644 --- a/Ambermoon.Core/UI/Global.cs +++ b/Ambermoon.Core/UI/Global.cs @@ -15,13 +15,17 @@ public partial class Global public const int Map3DViewY = 49; public const int Map3DViewWidth = 144; public const int Map3DViewHeight = 144; + /// + /// This includes a 1-pixel border around the portrait. + /// public static readonly Rect[] PartyMemberPortraitAreas = Enumerable.Range(0, 6).Select(index => - new Rect(16 + index * 40, 1, 32, 34)).ToArray(); // TODO + new Rect(15 + index * 48, 0, 32, 36)).ToArray(); /// - /// This includes the ailment icon and the bars for HP and SP. + /// This includes a 1-pixel border around the portrait. + /// This also includes the ailment icon and the bars for HP and SP. /// public static readonly Rect[] ExtendedPartyMemberPortraitAreas = Enumerable.Range(0, 6).Select(index => - new Rect(16 + index * 40, 1, 40, 34)).ToArray(); // TODO + new Rect(15 + index * 48, 0, 48, 36)).ToArray(); public const int GlyphWidth = 6; public const int GlyphLineHeight = 6; } diff --git a/Ambermoon.Core/UI/Graphics.cs b/Ambermoon.Core/UI/Graphics.cs index 7344cbf4..b47ee06b 100644 --- a/Ambermoon.Core/UI/Graphics.cs +++ b/Ambermoon.Core/UI/Graphics.cs @@ -1,4 +1,6 @@ -namespace Ambermoon.UI +using Ambermoon.Data.Enumerations; + +namespace Ambermoon.UI { public static class Graphics { @@ -8,5 +10,17 @@ public static class Graphics // Foreground layer public const uint PortraitOffset = 0u; public const uint Pics80x80Offset = 120u; + + // 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 ButtonOffset = UIGraphicOffset + (uint)Enum.NameCount(); + + public static uint GetScrollbarGraphicIndex(ScrollbarType scrollbarType) => UIElementOffset + (uint)scrollbarType; + public static uint GetCustomUIGraphicIndex(UICustomGraphic customGraphic) => UIElementOffset + (uint)customGraphic; + public static uint GetUIGraphicIndex(UIGraphic graphic) => UIGraphicOffset + (uint)graphic; + public static uint GetButtonGraphicIndex(ButtonType buttonType) => ButtonOffset + (uint)buttonType; } } diff --git a/Ambermoon.Core/UI/ItemGrid.cs b/Ambermoon.Core/UI/ItemGrid.cs index 6861a521..e75b5624 100644 --- a/Ambermoon.Core/UI/ItemGrid.cs +++ b/Ambermoon.Core/UI/ItemGrid.cs @@ -51,7 +51,7 @@ public bool Disabled } var slotTexCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + - (disabled ? (uint)UIElementGraphic.ItemSlotDisabled : (uint)UIElementGraphic.ItemSlotBackground)); + (disabled ? (uint)UICustomGraphic.ItemSlotDisabled : (uint)UICustomGraphic.ItemSlotBackground)); foreach (var background in slotBackgrounds) background.TextureAtlasOffset = slotTexCoords; } @@ -83,7 +83,7 @@ private ItemGrid(Layout layout, IRenderView renderView, IItemManager itemManager void CreateSlotBackgrounds() { var layer = renderView.GetLayer(Layer.UIBackground); - var texCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + (uint)UIElementGraphic.ItemSlotBackground); + var texCoords = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + (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 90029723..e81b8424 100644 --- a/Ambermoon.Core/UI/Layout.cs +++ b/Ambermoon.Core/UI/Layout.cs @@ -321,13 +321,13 @@ public void Reset() // Note: Don't remove fadeEffects here. } - public void SetActivePortrait(int slot) + public void SetActivePortrait(int slot, List partyMembers) { for (int i = 0; i < portraitNames.Length; ++i) { if (portraitNames[i] != null) { - portraitNames[i].TextColor = i == slot ? TextColor.Yellow : TextColor.Red; + portraitNames[i].TextColor = i == slot ? TextColor.Yellow : partyMembers[i].Alive ? TextColor.Red : TextColor.PaleGray; } } } @@ -335,42 +335,47 @@ public void SetActivePortrait(int slot) /// /// Set portait to 0 to remove the portrait. /// - public void SetPortrait(int slot, uint portrait, string name) + public void SetPortrait(int slot, uint portrait, string name, bool dead) { + var sprite = portraits[slot] ??= RenderView.SpriteFactory.Create(32, 34, 0, 0, false, true, 1); + sprite.Layer = RenderView.GetLayer(portrait == 0 || dead ? Layer.UIBackground : Layer.UIForeground); + sprite.X = Global.PartyMemberPortraitAreas[slot].Left + 1; + sprite.Y = Global.PartyMemberPortraitAreas[slot].Top + 1; + if (portrait == 0) + sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.EmptyCharacterSlot)); + else if (dead) + sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.GetUIGraphicIndex(UIGraphic.Skull)); + else + sprite.TextureAtlasOffset = textureAtlasForeground.GetOffset(Graphics.PortraitOffset + portrait - 1); + sprite.PaletteIndex = 49; + sprite.Visible = true; + if (portrait == 0) { // TODO: in original portrait removing is animated by moving down the - // gray masked picture infront of the portrait + // gray masked picture infront of the portrait. But this method is + // also used on game loading where this effect should not be used. portraitBackgrounds[slot]?.Delete(); portraitBackgrounds[slot] = null; - portraits[slot]?.Delete(); - portraits[slot] = null; portraitNames[slot]?.Delete(); portraitNames[slot] = null; } else { - var sprite = portraitBackgrounds[slot] ??= RenderView.SpriteFactory.Create(32, 34, 0, 0, false, true, 0); + sprite = portraitBackgrounds[slot] ??= RenderView.SpriteFactory.Create(32, 34, 0, 0, false, true, 0); sprite.Layer = RenderView.GetLayer(Layer.UIBackground); - sprite.X = Global.PartyMemberPortraitAreas[slot].Left; - sprite.Y = Global.PartyMemberPortraitAreas[slot].Top; - sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.UIElementOffset + (uint)UIElementGraphic.PortraitBackground); + sprite.X = Global.PartyMemberPortraitAreas[slot].Left + 1; + sprite.Y = Global.PartyMemberPortraitAreas[slot].Top + 1; + sprite.TextureAtlasOffset = textureAtlasBackground.GetOffset(Graphics.UIElementOffset + (uint)UICustomGraphic.PortraitBackground); sprite.PaletteIndex = 50; sprite.Visible = true; - sprite = portraits[slot] ??= RenderView.SpriteFactory.Create(32, 34, 0, 0, false, true, 1); - sprite.Layer = RenderView.GetLayer(Layer.UIForeground); - sprite.X = Global.PartyMemberPortraitAreas[slot].Left; - sprite.Y = Global.PartyMemberPortraitAreas[slot].Top; - sprite.TextureAtlasOffset = textureAtlasForeground.GetOffset(Graphics.PortraitOffset + portrait - 1); - sprite.PaletteIndex = 49; - sprite.Visible = true; - var text = portraitNames[slot] ??= RenderView.RenderTextFactory.Create(RenderView.GetLayer(Layer.Text), RenderView.TextProcessor.CreateText(name.Substring(0, Math.Min(5, name.Length))), TextColor.Red, true, - new Rect(Global.PartyMemberPortraitAreas[slot].Left + 1, Global.PartyMemberPortraitAreas[slot].Top + 30, 32, 6), TextAlign.Center); + new Rect(Global.PartyMemberPortraitAreas[slot].Left + 2, Global.PartyMemberPortraitAreas[slot].Top + 31, 30, 6), TextAlign.Center); text.DisplayLayer = 1; + text.TextColor = dead ? TextColor.PaleGray : TextColor.Red; text.Visible = true; } } diff --git a/Ambermoon.Core/UI/Scrollbar.cs b/Ambermoon.Core/UI/Scrollbar.cs index 96005141..fa690e90 100644 --- a/Ambermoon.Core/UI/Scrollbar.cs +++ b/Ambermoon.Core/UI/Scrollbar.cs @@ -34,8 +34,8 @@ public bool Disabled { backgroundSprite.TextureAtlasOffset = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + baseType switch { - ScrollbarType.SmallVertical => (uint)UIElementGraphic.ScrollbarSmallVerticalDisabled, - ScrollbarType.LargeVertical => (uint)UIElementGraphic.ScrollbarLargeVerticalDisabled, + ScrollbarType.SmallVertical => (uint)UICustomGraphic.ScrollbarSmallVerticalDisabled, + ScrollbarType.LargeVertical => (uint)UICustomGraphic.ScrollbarLargeVerticalDisabled, _ => throw new AmbermoonException(ExceptionScope.Application, "Invalid scrollbar type.") }); } @@ -43,8 +43,8 @@ public bool Disabled { backgroundSprite.TextureAtlasOffset = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + baseType switch { - ScrollbarType.SmallVertical => (uint)UIElementGraphic.ScrollbarBackgroundSmallVertical, - ScrollbarType.LargeVertical => (uint)UIElementGraphic.ScrollbarBackgroundLargeVertical, + ScrollbarType.SmallVertical => (uint)UICustomGraphic.ScrollbarBackgroundSmallVertical, + ScrollbarType.LargeVertical => (uint)UICustomGraphic.ScrollbarBackgroundLargeVertical, _ => throw new AmbermoonException(ExceptionScope.Application, "Invalid scrollbar type.") }); } @@ -66,8 +66,8 @@ public Scrollbar(Layout layout, ScrollbarType type, Rect scrollArea, int width, backgroundSprite.Layer = layout.RenderView.GetLayer(Layer.UIBackground); backgroundSprite.TextureAtlasOffset = TextureAtlasManager.Instance.GetOrCreate(Layer.UIBackground).GetOffset(Graphics.UIElementOffset + type switch { - ScrollbarType.SmallVertical => (uint)UIElementGraphic.ScrollbarBackgroundSmallVertical, - ScrollbarType.LargeVertical => (uint)UIElementGraphic.ScrollbarBackgroundLargeVertical, + ScrollbarType.SmallVertical => (uint)UICustomGraphic.ScrollbarBackgroundSmallVertical, + ScrollbarType.LargeVertical => (uint)UICustomGraphic.ScrollbarBackgroundLargeVertical, _ => throw new AmbermoonException(ExceptionScope.Application, "Invalid scrollbar type.") }); backgroundSprite.DisplayLayer = 1; diff --git a/Ambermoon.Data.Common/Enumerations/UIElementGraphic.cs b/Ambermoon.Data.Common/Enumerations/UICustomGraphic.cs similarity index 95% rename from Ambermoon.Data.Common/Enumerations/UIElementGraphic.cs rename to Ambermoon.Data.Common/Enumerations/UICustomGraphic.cs index 09aca065..3579eb08 100644 --- a/Ambermoon.Data.Common/Enumerations/UIElementGraphic.cs +++ b/Ambermoon.Data.Common/Enumerations/UICustomGraphic.cs @@ -6,7 +6,7 @@ /// executable data) like scrollbars, free portrait slots /// and so on. /// - public enum UIElementGraphic + public enum UICustomGraphic { ScrollbarSmallVertical, // chests, merchants, etc ScrollbarSmallVerticalHighlighted, diff --git a/Ambermoon.Data.Legacy/ExecutableData/Buttons.cs b/Ambermoon.Data.Legacy/ExecutableData/Buttons.cs index 9e71155c..ab62263b 100644 --- a/Ambermoon.Data.Legacy/ExecutableData/Buttons.cs +++ b/Ambermoon.Data.Legacy/ExecutableData/Buttons.cs @@ -38,6 +38,8 @@ Graphic ReadGraphic(IDataReader dataReader) foreach (var buttonType in Enum.GetValues()) entries.Add(buttonType, ReadGraphic(dataReader)); + + dataReader.AlignToWord(); } } } diff --git a/Ambermoon.Data.Legacy/GraphicProvider.cs b/Ambermoon.Data.Legacy/GraphicProvider.cs index 73eab510..58faaec9 100644 --- a/Ambermoon.Data.Legacy/GraphicProvider.cs +++ b/Ambermoon.Data.Legacy/GraphicProvider.cs @@ -127,6 +127,8 @@ public GraphicProvider(GameData gameData, ExecutableData.ExecutableData executab else if (type == GraphicType.UIElements) { graphics[GraphicType.UIElements] = UIElementProvider.Create(); + graphics[GraphicType.UIElements].AddRange(executableData.UIGraphics.Entries.Values); + graphics[GraphicType.UIElements].AddRange(executableData.Buttons.Entries.Values); } else { diff --git a/Ambermoon.Renderer.OpenGL/RenderLayer.cs b/Ambermoon.Renderer.OpenGL/RenderLayer.cs index d1a9c4b7..6282ba6f 100644 --- a/Ambermoon.Renderer.OpenGL/RenderLayer.cs +++ b/Ambermoon.Renderer.OpenGL/RenderLayer.cs @@ -231,7 +231,8 @@ public void Render() shader.SetAtlasSize((uint)Texture.Width, (uint)Texture.Height); shader.SetZ(LayerBaseZ[(int)Layer]); - shader.SetColorKey((byte)(Layer == Layer.UIForeground ? 25 : 0)); + // Portraits and some UI graphics use color keys. + shader.SetColorKey((byte)(Layer == Layer.UIForeground || Layer == Layer.UIBackground ? 25 : 0)); } } diff --git a/Ambermoon.Renderer.OpenGL/RenderNode.cs b/Ambermoon.Renderer.OpenGL/RenderNode.cs index adb69432..c40f0952 100644 --- a/Ambermoon.Renderer.OpenGL/RenderNode.cs +++ b/Ambermoon.Renderer.OpenGL/RenderNode.cs @@ -64,10 +64,7 @@ public bool Visible visible = value; - if (Visible) - AddToLayer(); - else if (!visible) - RemoveFromLayer(); + OnVisibilityChanged(); } } diff --git a/Ambermoon.Renderer.OpenGL/Sprite.cs b/Ambermoon.Renderer.OpenGL/Sprite.cs index b8d322c8..0adbe838 100644 --- a/Ambermoon.Renderer.OpenGL/Sprite.cs +++ b/Ambermoon.Renderer.OpenGL/Sprite.cs @@ -375,7 +375,8 @@ protected override void AddToLayer() public void UpdateTextColorIndex() { - (Layer as RenderLayer).UpdateTextColorIndex(drawIndex, TextColorIndex); + if (drawIndex != -1) // -1 means not attached to a layer + (Layer as RenderLayer).UpdateTextColorIndex(drawIndex, TextColorIndex); } } diff --git a/Ambermoon.Renderer.OpenGL/TextureShader.cs b/Ambermoon.Renderer.OpenGL/TextureShader.cs index 1f3d96b3..1e0afa57 100644 --- a/Ambermoon.Renderer.OpenGL/TextureShader.cs +++ b/Ambermoon.Renderer.OpenGL/TextureShader.cs @@ -55,7 +55,10 @@ internal class TextureShader : ColorShader $" float colorIndex = texture({DefaultSamplerName}, varTexCoord).r * 255.0f;", $" vec4 pixelColor = texture({DefaultPaletteName}, vec2((colorIndex + 0.5f) / 32.0f, (palIndex + 0.5f) / 51.0f));", $" ", - $" if (abs(colorIndex - {DefaultColorKeyName}) < 0.5f || pixelColor.a < 0.5f)", + $" // Color key is only available for item/portrait palette 49.", + $" float colorKey = abs(palIndex - 49.0f) < 0.5f ? {DefaultColorKeyName} : 0.0f;", + $" ", + $" if (abs(colorIndex - colorKey) < 0.5f || pixelColor.a < 0.5f)", $" discard;", $" else", $" {DefaultFragmentOutColorName} = pixelColor;", diff --git a/Ambermoon.net/GameWindow.cs b/Ambermoon.net/GameWindow.cs index 631bcb9f..68aed744 100644 --- a/Ambermoon.net/GameWindow.cs +++ b/Ambermoon.net/GameWindow.cs @@ -208,9 +208,8 @@ void Window_Load() // Create game Game = new Game(renderView, - new MapManager(gameData, new MapReader(), new TilesetReader(), new LabdataReader()), - executableData.ItemManager, new SavegameManager(), new SavegameSerializer(), - new DataNameProvider(executableData), + new MapManager(gameData, new MapReader(), new TilesetReader(), new LabdataReader()), executableData.ItemManager, + new SavegameManager(), new SavegameSerializer(), new DataNameProvider(executableData), new Render.Cursor(renderView, executableData.Cursors.Entries.Select(c => new Position(c.HotspotX, c.HotspotY)).ToList().AsReadOnly()), configuration.LegacyMode); Game.Run();