merge: #267 vitae character-panel display (attributes vitae-immune per retail; skill dual parentheticals)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
2493f24c63
12 changed files with 984 additions and 108 deletions
|
|
@ -968,7 +968,11 @@ public class CharacterStatControllerTests
|
|||
var rows = SkillRows(list);
|
||||
rows[1].OnClick!();
|
||||
|
||||
Assert.Equal("War Magic: 285", title.LinesProvider()[0].Text);
|
||||
// SampleData's War Magic row is intentionally Base=280/Current=285 (an
|
||||
// illustrative buffed-skill sample — see SampleData.cs comment); issue
|
||||
// #267 wires the retail "(+5)" buff-delta parenthetical onto that gap,
|
||||
// which was previously computed but never surfaced in the title text.
|
||||
Assert.Equal("War Magic: 285 (+5)", title.LinesProvider()[0].Text);
|
||||
Assert.Equal("Experience To Raise:", l1Label.LinesProvider()[0].Text);
|
||||
Assert.Equal((11_100_000L).ToString("N0"), l1Value.LinesProvider()[0].Text);
|
||||
Assert.Equal("Unassigned Experience:", l2Label.LinesProvider()[0].Text);
|
||||
|
|
@ -1269,6 +1273,178 @@ public class CharacterStatControllerTests
|
|||
public void GetRowName_NegativeIndex_ReturnsEmpty()
|
||||
=> Assert.Equal(string.Empty, CharacterStatController.GetRowName(-1));
|
||||
|
||||
// ── Issue #267 — vitae/buff delta parenthetical ──────────────────────────
|
||||
// Retail format cited from gmAttributeUI::DisplaySelectionFooter_Attribute
|
||||
// (0x0049d280, " (%s%d)") and gmSkillUI::DisplaySelectionFooter_Trained
|
||||
// (0x0049b860, vitae segment " (%d)" + buff segment " (%s%d)").
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeDelta_ComputesEffectiveMinusBase()
|
||||
{
|
||||
var sheet = new CharacterSheet { Strength = 220, AttributeBaseValues = [200, 0, 0, 0, 0, 0] };
|
||||
Assert.Equal(20, CharacterStatController.GetAttributeDelta(sheet, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeDelta_VitalRowIndex_ReturnsZero()
|
||||
{
|
||||
// Vitals (rows 6-8) use a "cur/max" footer format in retail
|
||||
// (DisplaySelectionFooter_Vital), not this delta pattern.
|
||||
var sheet = new CharacterSheet { AttributeBaseValues = [200, 0, 0, 0, 0, 0] };
|
||||
Assert.Equal(0, CharacterStatController.GetAttributeDelta(sheet, 6));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributeDelta_EmptyBaseValues_ReturnsZero()
|
||||
{
|
||||
// SampleData / older sheets that don't populate AttributeBaseValues
|
||||
// must not throw or fabricate a delta.
|
||||
var sheet = new CharacterSheet { Strength = 220 };
|
||||
Assert.Equal(0, CharacterStatController.GetAttributeDelta(sheet, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSkillBuffOnlyDelta_IsolatesBuffFromVitae()
|
||||
{
|
||||
// CurrentLevel=251 already includes vitae (-99); the buff-only
|
||||
// residual is (251 - (-99)) - 300 = 50.
|
||||
var skill = new CharacterSkill(1u, "S", 0u, CharacterSkillAdvancementClass.Trained,
|
||||
BaseLevel: 300, CurrentLevel: 251, UsableUntrained: true,
|
||||
TrainedCost: 0, SpecializedCost: 0, RaiseCost: 0, VitaeModifier: -99);
|
||||
Assert.Equal(50, CharacterStatController.GetSkillBuffOnlyDelta(skill));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RowClick_AttributeWithBuff_FooterTitleShowsPositiveDelta()
|
||||
{
|
||||
var title = new UiText();
|
||||
var list = new UiPanel();
|
||||
var layout = Fake(
|
||||
(CharacterStatController.FooterTitleId, title),
|
||||
(CharacterStatController.ListBoxId, list));
|
||||
|
||||
CharacterSheet Sheet() => new() { Strength = 220, AttributeBaseValues = [200, 0, 0, 0, 0, 0] };
|
||||
CharacterStatController.Bind(layout, Sheet);
|
||||
|
||||
list.Children.OfType<UiClickablePanel>().ToList()[0].OnClick!(); // Strength = index 0
|
||||
|
||||
Assert.Equal("Strength: 220 (+20)", title.LinesProvider()[0].Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RowClick_AttributeWithDebuff_FooterTitleShowsNegativeDelta()
|
||||
{
|
||||
var title = new UiText();
|
||||
var list = new UiPanel();
|
||||
var layout = Fake(
|
||||
(CharacterStatController.FooterTitleId, title),
|
||||
(CharacterStatController.ListBoxId, list));
|
||||
|
||||
CharacterSheet Sheet() => new() { Endurance = 180, AttributeBaseValues = [0, 200, 0, 0, 0, 0] };
|
||||
CharacterStatController.Bind(layout, Sheet);
|
||||
|
||||
list.Children.OfType<UiClickablePanel>().ToList()[1].OnClick!(); // Endurance = index 1
|
||||
|
||||
Assert.Equal("Endurance: 180 (-20)", title.LinesProvider()[0].Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RowClick_AttributeZeroDelta_NoParenthetical()
|
||||
{
|
||||
var title = new UiText();
|
||||
var list = new UiPanel();
|
||||
var layout = Fake(
|
||||
(CharacterStatController.FooterTitleId, title),
|
||||
(CharacterStatController.ListBoxId, list));
|
||||
|
||||
CharacterSheet Sheet() => new() { Strength = 200, AttributeBaseValues = [200, 0, 0, 0, 0, 0] };
|
||||
CharacterStatController.Bind(layout, Sheet);
|
||||
|
||||
list.Children.OfType<UiClickablePanel>().ToList()[0].OnClick!();
|
||||
|
||||
Assert.Equal("Strength: 200", title.LinesProvider()[0].Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkillClick_VitaeOnly_FooterTitleShowsVitaeParenthetical()
|
||||
{
|
||||
// User-reported oracle (ISSUES.md #267): a base-303 skill under 33%
|
||||
// vitae shows the current (reduced) level with "(-100)".
|
||||
var list = new UiPanel { Width = 300 };
|
||||
var title = new UiText();
|
||||
var layout = Fake(
|
||||
(CharacterStatController.ListBoxId, list),
|
||||
(CharacterStatController.FooterTitleId, title));
|
||||
|
||||
CharacterSheet Sheet() => VitaeSkillSheet(currentLevel: 203, baseLevel: 303, vitaeModifier: -100);
|
||||
CharacterStatController.Bind(layout, Sheet, spriteResolve: id => (id, 16, 16));
|
||||
|
||||
ClickTab(layout, left: 92f);
|
||||
SkillRows(list)[0].OnClick!();
|
||||
|
||||
Assert.Equal("Test Skill: 203 (-100)", title.LinesProvider()[0].Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkillClick_VitaePlusBuff_FooterTitleShowsBothParentheticals()
|
||||
{
|
||||
var list = new UiPanel { Width = 300 };
|
||||
var title = new UiText();
|
||||
var layout = Fake(
|
||||
(CharacterStatController.ListBoxId, list),
|
||||
(CharacterStatController.FooterTitleId, title));
|
||||
|
||||
// CurrentLevel=251, base=300, vitae modifier=-99 (300*0.67-300).
|
||||
// Buff-only delta = (251 - (-99)) - 300 = 50.
|
||||
CharacterSheet Sheet() => VitaeSkillSheet(currentLevel: 251, baseLevel: 300, vitaeModifier: -99);
|
||||
CharacterStatController.Bind(layout, Sheet, spriteResolve: id => (id, 16, 16));
|
||||
|
||||
ClickTab(layout, left: 92f);
|
||||
SkillRows(list)[0].OnClick!();
|
||||
|
||||
Assert.Equal("Test Skill: 251 (-99) (+50)", title.LinesProvider()[0].Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SkillClick_ZeroDelta_NoParentheticals()
|
||||
{
|
||||
var list = new UiPanel { Width = 300 };
|
||||
var title = new UiText();
|
||||
var layout = Fake(
|
||||
(CharacterStatController.ListBoxId, list),
|
||||
(CharacterStatController.FooterTitleId, title));
|
||||
|
||||
CharacterSheet Sheet() => VitaeSkillSheet(currentLevel: 300, baseLevel: 300, vitaeModifier: 0);
|
||||
CharacterStatController.Bind(layout, Sheet, spriteResolve: id => (id, 16, 16));
|
||||
|
||||
ClickTab(layout, left: 92f);
|
||||
SkillRows(list)[0].OnClick!();
|
||||
|
||||
Assert.Equal("Test Skill: 300", title.LinesProvider()[0].Text);
|
||||
}
|
||||
|
||||
private static CharacterSheet VitaeSkillSheet(int currentLevel, int baseLevel, int vitaeModifier) => new()
|
||||
{
|
||||
SkillCredits = 0,
|
||||
UnassignedXp = 1_000_000,
|
||||
Skills =
|
||||
[
|
||||
new CharacterSkill(
|
||||
200u,
|
||||
"Test Skill",
|
||||
0x06000001u,
|
||||
CharacterSkillAdvancementClass.Trained,
|
||||
BaseLevel: baseLevel,
|
||||
CurrentLevel: currentLevel,
|
||||
UsableUntrained: true,
|
||||
TrainedCost: 0,
|
||||
SpecializedCost: 0,
|
||||
RaiseCost: 100,
|
||||
Raise10Cost: 1000,
|
||||
VitaeModifier: vitaeModifier),
|
||||
],
|
||||
};
|
||||
|
||||
// ── SampleData sanity ─────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue