fix(studio): polish character skill rows
This commit is contained in:
parent
5f92493742
commit
ffaf593c76
3 changed files with 47 additions and 19 deletions
|
|
@ -138,6 +138,8 @@ public static class CharacterStatController
|
|||
/// <summary>Row highlight color — semi-translucent gold, matches retail
|
||||
/// UIStateId.Highlight (0x06) sprite 0x06001397 visual intent.</summary>
|
||||
private static readonly Vector4 HighlightBg = new(1f, 0.75f, 0.2f, 0.25f);
|
||||
private static readonly Vector4 SkillRowBackground = new(0f, 0f, 0f, 1f);
|
||||
private static readonly Vector4 BuffedSkillGreen = new(0.55f, 1f, 0.55f, 1f);
|
||||
|
||||
// ── Row layout constants ─────────────────────────────────────────────────
|
||||
// RowHeight 22px + IconSize 16px: retail spec (2026-06-26) says icons ~icon-height
|
||||
|
|
@ -155,8 +157,8 @@ public static class CharacterStatController
|
|||
|
||||
private const uint SkillHeaderSpecializedSprite = 0x06000F90u;
|
||||
private const uint SkillHeaderTrainedSprite = 0x06000F86u;
|
||||
private const uint SkillHeaderUntrainedSprite = 0x06000F89u;
|
||||
private const uint SkillRowBgSprite = 0x06000F98u;
|
||||
private const uint SkillHeaderUntrainedSprite = 0x06000F98u;
|
||||
private const uint SkillHeaderUnusableSprite = 0x06000F89u;
|
||||
private const uint RowHighlightSprite = 0x06001397u;
|
||||
|
||||
private enum CharacterStatTab
|
||||
|
|
@ -583,9 +585,9 @@ public static class CharacterStatController
|
|||
OrderedSkills(data(), CharacterSkillAdvancementClass.Specialized, usableUntrained: null));
|
||||
AddBucket("Trained Skills", SkillHeaderTrainedSprite,
|
||||
OrderedSkills(data(), CharacterSkillAdvancementClass.Trained, usableUntrained: null));
|
||||
AddBucket("Useable Untrained Skills", SkillHeaderUntrainedSprite,
|
||||
AddBucket("Untrained Skills", SkillHeaderUntrainedSprite,
|
||||
OrderedSkills(data(), CharacterSkillAdvancementClass.Untrained, usableUntrained: true));
|
||||
AddBucket("Unuseable Untrained Skills", SkillHeaderUntrainedSprite,
|
||||
AddBucket("Unusable Skills", SkillHeaderUnusableSprite,
|
||||
OrderedSkills(data(), CharacterSkillAdvancementClass.Untrained, usableUntrained: false));
|
||||
|
||||
skillRows = bindings;
|
||||
|
|
@ -606,7 +608,8 @@ public static class CharacterStatController
|
|||
nameText: skill.Name,
|
||||
valueProvider: () => skill.CurrentLevel.ToString(),
|
||||
valueColorProvider: () => SkillValueColor(skill),
|
||||
backgroundSprite: SkillRowBgSprite,
|
||||
nameColor: Vector4.One,
|
||||
backgroundColor: SkillRowBackground,
|
||||
useSelectionBars: false);
|
||||
row.OnClick = () => HandleSkillRowClick(rowIndex, sel, bindings, spriteResolve, data, allRaise1, allRaise10);
|
||||
bindings.Add(new SkillRowBinding(row, skill));
|
||||
|
|
@ -656,7 +659,7 @@ public static class CharacterStatController
|
|||
Anchors = AnchorEdges.Left | AnchorEdges.Top,
|
||||
};
|
||||
string captured = title;
|
||||
label.LinesProvider = () => new[] { new UiText.Line(captured, Gold) };
|
||||
label.LinesProvider = () => new[] { new UiText.Line(captured, Vector4.One) };
|
||||
header.AddChild(label);
|
||||
list.AddChild(header);
|
||||
return header;
|
||||
|
|
@ -700,9 +703,9 @@ public static class CharacterStatController
|
|||
}
|
||||
|
||||
private static Vector4 SkillValueColor(CharacterSkill skill)
|
||||
=> skill.CurrentLevel > skill.BaseLevel ? new Vector4(0.55f, 1f, 0.55f, 1f)
|
||||
=> skill.CurrentLevel > skill.BaseLevel ? BuffedSkillGreen
|
||||
: skill.CurrentLevel < skill.BaseLevel ? new Vector4(1f, 0.45f, 0.45f, 1f)
|
||||
: Body;
|
||||
: Vector4.One;
|
||||
|
||||
/// <summary>
|
||||
/// Handles a row click: toggle (same row → deselect), else select new row.
|
||||
|
|
@ -781,7 +784,7 @@ public static class CharacterStatController
|
|||
{
|
||||
if (spriteResolve is not null)
|
||||
{
|
||||
row.BackgroundColor = Vector4.Zero;
|
||||
row.BackgroundColor = SkillRowBackground;
|
||||
row.BackgroundSprite = RowHighlightSprite;
|
||||
row.SpriteResolve = id => { var (h, w, ht) = spriteResolve(id); return (h, w, ht); };
|
||||
row.UseSelectionBars = true;
|
||||
|
|
@ -796,11 +799,9 @@ public static class CharacterStatController
|
|||
}
|
||||
else
|
||||
{
|
||||
row.BackgroundColor = Vector4.Zero;
|
||||
row.BackgroundSprite = spriteResolve is not null ? SkillRowBgSprite : 0u;
|
||||
row.SpriteResolve = spriteResolve is not null
|
||||
? id => { var (h, w, ht) = spriteResolve(id); return (h, w, ht); }
|
||||
: null;
|
||||
row.BackgroundColor = SkillRowBackground;
|
||||
row.BackgroundSprite = 0u;
|
||||
row.SpriteResolve = null;
|
||||
row.UseSelectionBars = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -931,6 +932,8 @@ public static class CharacterStatController
|
|||
string nameText,
|
||||
Func<string> valueProvider,
|
||||
Func<Vector4>? valueColorProvider = null,
|
||||
Vector4? nameColor = null,
|
||||
Vector4? backgroundColor = null,
|
||||
uint backgroundSprite = 0u,
|
||||
bool useSelectionBars = true)
|
||||
{
|
||||
|
|
@ -940,7 +943,7 @@ public static class CharacterStatController
|
|||
Top = top,
|
||||
Width = width,
|
||||
Height = height,
|
||||
BackgroundColor = Vector4.Zero, // transparent until selected
|
||||
BackgroundColor = backgroundColor ?? Vector4.Zero,
|
||||
BackgroundSprite = spriteResolve is not null ? backgroundSprite : 0u,
|
||||
SpriteResolve = spriteResolve is not null && backgroundSprite != 0u
|
||||
? id => { var (h, w, ht) = spriteResolve(id); return (h, w, ht); }
|
||||
|
|
@ -973,6 +976,7 @@ public static class CharacterStatController
|
|||
float nameY = 0f;
|
||||
|
||||
string capturedName = nameText;
|
||||
Vector4 capturedNameColor = nameColor ?? Body;
|
||||
var nameEl = new UiText
|
||||
{
|
||||
Left = nameX,
|
||||
|
|
@ -986,7 +990,7 @@ public static class CharacterStatController
|
|||
Padding = 1f,
|
||||
Anchors = AnchorEdges.Left | AnchorEdges.Top,
|
||||
};
|
||||
nameEl.LinesProvider = () => new[] { new UiText.Line(capturedName, Body) };
|
||||
nameEl.LinesProvider = () => new[] { new UiText.Line(capturedName, capturedNameColor) };
|
||||
|
||||
float valueW = width - nameX - nameW - RowPadX;
|
||||
float valueX = nameX + nameW;
|
||||
|
|
|
|||
|
|
@ -175,6 +175,9 @@ public class UiClickablePanel : UiPanel
|
|||
{
|
||||
if (UseSelectionBars && BackgroundSprite != 0 && SpriteResolve is { } sr)
|
||||
{
|
||||
if (BackgroundColor.W > 0f)
|
||||
ctx.DrawRect(0, 0, Width, Height, BackgroundColor);
|
||||
|
||||
// Draw the selection highlight as a thin bar at the TOP and BOTTOM of the row.
|
||||
// The sprite (0x06001397) is 300×32 px — we draw it as horizontal strips at
|
||||
// native height (SelectionBarHeight), stretched to full panel width (UV tile
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using AcDream.App.Studio;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using System.Numerics;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
|
|
@ -634,20 +635,32 @@ public class CharacterStatControllerTests
|
|||
|
||||
ClickTab(layout, left: 92f);
|
||||
|
||||
var headers = list.Children
|
||||
var headerPanels = list.Children
|
||||
.Where(c => c is UiPanel and not UiClickablePanel)
|
||||
.Cast<UiPanel>()
|
||||
.ToList();
|
||||
var headers = headerPanels
|
||||
.Select(c => c.Children.OfType<UiText>().First().LinesProvider()[0].Text)
|
||||
.ToList();
|
||||
Assert.Equal(new[]
|
||||
{
|
||||
"Specialized Skills",
|
||||
"Trained Skills",
|
||||
"Useable Untrained Skills",
|
||||
"Unuseable Untrained Skills",
|
||||
"Untrained Skills",
|
||||
"Unusable Skills",
|
||||
}, headers);
|
||||
Assert.Equal(new[] { 0x06000F90u, 0x06000F86u, 0x06000F98u, 0x06000F89u },
|
||||
headerPanels.Select(h => h.BackgroundSprite).ToArray());
|
||||
Assert.All(headerPanels, h =>
|
||||
Assert.Equal(Vector4.One, h.Children.OfType<UiText>().First().LinesProvider()[0].Color));
|
||||
|
||||
var rows = list.Children.OfType<UiClickablePanel>().ToList();
|
||||
Assert.Equal(12, rows.Count);
|
||||
Assert.All(rows, row =>
|
||||
{
|
||||
Assert.Equal(new Vector4(0f, 0f, 0f, 1f), row.BackgroundColor);
|
||||
Assert.Equal(0u, row.BackgroundSprite);
|
||||
});
|
||||
var rowNames = rows
|
||||
.Select(r => r.Children.OfType<UiText>().ToList()[1].LinesProvider()[0].Text)
|
||||
.ToList();
|
||||
|
|
@ -658,6 +671,14 @@ public class CharacterStatControllerTests
|
|||
"Healing", "Jump", "Loyalty", "Run",
|
||||
"Alchemy", "Cooking", "Fletching",
|
||||
}, rowNames);
|
||||
|
||||
var meleeTexts = rows[0].Children.OfType<UiText>().ToList();
|
||||
Assert.Equal(Vector4.One, meleeTexts[1].LinesProvider()[0].Color);
|
||||
Assert.Equal(new Vector4(0.55f, 1f, 0.55f, 1f), meleeTexts[2].LinesProvider()[0].Color);
|
||||
|
||||
var healingTexts = rows[5].Children.OfType<UiText>().ToList();
|
||||
Assert.Equal(Vector4.One, healingTexts[1].LinesProvider()[0].Color);
|
||||
Assert.Equal(Vector4.One, healingTexts[2].LinesProvider()[0].Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue