Skip to content

Commit

Permalink
Added portraits for empty slots and dead characters
Browse files Browse the repository at this point in the history
  • Loading branch information
Pyrdacor committed Sep 4, 2020
1 parent 3262379 commit 138f5a1
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 54 deletions.
2 changes: 1 addition & 1 deletion Ambermoon.Common/Ambermoon.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<RepositoryType>Github</RepositoryType>
<Description>Library that contains basic stuff for other Ambermoon libraries.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.0.3</Version>
<Version>1.0.4</Version>
</PropertyGroup>

</Project>
12 changes: 4 additions & 8 deletions Ambermoon.Common/EnumExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@
{
public static class Enum
{
public static TEnum[] GetValues<TEnum>()
{
return (TEnum[])System.Enum.GetValues(typeof(TEnum));
}
public static TEnum[] GetValues<TEnum>() => (TEnum[])System.Enum.GetValues(typeof(TEnum));

public static string GetName<TEnum>(TEnum value)
{
return System.Enum.GetName(typeof(TEnum), value);
}
public static string GetName<TEnum>(TEnum value) => System.Enum.GetName(typeof(TEnum), value);

public static int NameCount<TEnum>() => System.Enum.GetNames(typeof(TEnum)).Length;
}
}
10 changes: 7 additions & 3 deletions Ambermoon.Core/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -983,7 +987,7 @@ internal void SetActivePartyMember(int index)
{
currentSavegame.ActivePartyMemberSlot = index;
CurrentPartyMember = partyMember;
layout.SetActivePortrait(index);
layout.SetActivePortrait(index, currentSavegame.PartyMembers);
}
}

Expand Down
10 changes: 7 additions & 3 deletions Ambermoon.Core/UI/Global.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ public partial class Global
public const int Map3DViewY = 49;
public const int Map3DViewWidth = 144;
public const int Map3DViewHeight = 144;
/// <summary>
/// This includes a 1-pixel border around the portrait.
/// </summary>
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();
/// <summary>
/// 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.
/// </summary>
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;
}
Expand Down
16 changes: 15 additions & 1 deletion Ambermoon.Core/UI/Graphics.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Ambermoon.UI
using Ambermoon.Data.Enumerations;

namespace Ambermoon.UI
{
public static class Graphics
{
Expand All @@ -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<UICustomGraphic>();
static readonly uint ButtonOffset = UIGraphicOffset + (uint)Enum.NameCount<UIGraphic>();

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;
}
}
4 changes: 2 additions & 2 deletions Ambermoon.Core/UI/ItemGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand Down
43 changes: 24 additions & 19 deletions Ambermoon.Core/UI/Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,56 +321,61 @@ public void Reset()
// Note: Don't remove fadeEffects here.
}

public void SetActivePortrait(int slot)
public void SetActivePortrait(int slot, List<PartyMember> 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;
}
}
}

/// <summary>
/// Set portait to 0 to remove the portrait.
/// </summary>
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;
}
}
Expand Down
12 changes: 6 additions & 6 deletions Ambermoon.Core/UI/Scrollbar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ 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.")
});
}
else
{
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.")
});
}
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/// executable data) like scrollbars, free portrait slots
/// and so on.
/// </summary>
public enum UIElementGraphic
public enum UICustomGraphic
{
ScrollbarSmallVertical, // chests, merchants, etc
ScrollbarSmallVerticalHighlighted,
Expand Down
2 changes: 2 additions & 0 deletions Ambermoon.Data.Legacy/ExecutableData/Buttons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Graphic ReadGraphic(IDataReader dataReader)

foreach (var buttonType in Enum.GetValues<ButtonType>())
entries.Add(buttonType, ReadGraphic(dataReader));

dataReader.AlignToWord();
}
}
}
2 changes: 2 additions & 0 deletions Ambermoon.Data.Legacy/GraphicProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
3 changes: 2 additions & 1 deletion Ambermoon.Renderer.OpenGL/RenderLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand Down
5 changes: 1 addition & 4 deletions Ambermoon.Renderer.OpenGL/RenderNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,7 @@ public bool Visible

visible = value;

if (Visible)
AddToLayer();
else if (!visible)
RemoveFromLayer();
OnVisibilityChanged();
}
}

Expand Down
3 changes: 2 additions & 1 deletion Ambermoon.Renderer.OpenGL/Sprite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
5 changes: 4 additions & 1 deletion Ambermoon.Renderer.OpenGL/TextureShader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;",
Expand Down
5 changes: 2 additions & 3 deletions Ambermoon.net/GameWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 138f5a1

Please sign in to comment.