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();