fix(ui): restore retail skill training confirmation

Route untrained-skill promotion through DialogFactory with retail's exact prompt and stored skill/credit properties. Defer the character-sheet refresh until accept or reject completes while leaving trained-skill XP raises immediate.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 17:57:29 +02:00
parent 66bdae7a83
commit 85980fe13f
7 changed files with 341 additions and 14 deletions

View file

@ -674,7 +674,8 @@ public class CharacterStatControllerTests
(CharacterStatController.ListBoxId, list));
var requests = new List<CharacterStatController.RaiseRequest>();
CharacterStatController.Bind(layout, SampleData.SampleCharacter, onRaiseRequest: requests.Add);
CharacterStatController.Bind(layout, SampleData.SampleCharacter,
onRaiseRequest: (request, completed) => { requests.Add(request); completed(); });
list.Children.OfType<UiClickablePanel>().ToList()[4].OnClick!();
btn1.OnClick!();
@ -696,7 +697,8 @@ public class CharacterStatControllerTests
(CharacterStatController.ListBoxId, list));
var requests = new List<CharacterStatController.RaiseRequest>();
CharacterStatController.Bind(layout, SampleData.SampleCharacter, onRaiseRequest: requests.Add);
CharacterStatController.Bind(layout, SampleData.SampleCharacter,
onRaiseRequest: (request, completed) => { requests.Add(request); completed(); });
list.Children.OfType<UiClickablePanel>().ToList()[4].OnClick!();
Assert.Equal("Normal", btn1.ActiveState);
@ -717,7 +719,8 @@ public class CharacterStatControllerTests
(CharacterStatController.ListBoxId, list));
var requests = new List<CharacterStatController.RaiseRequest>();
CharacterStatController.Bind(layout, SampleData.SampleCharacter, onRaiseRequest: requests.Add);
CharacterStatController.Bind(layout, SampleData.SampleCharacter,
onRaiseRequest: (request, completed) => { requests.Add(request); completed(); });
list.Children.OfType<UiClickablePanel>().ToList()[6].OnClick!();
btn1.OnClick!();
@ -745,7 +748,8 @@ public class CharacterStatControllerTests
AttributeRaise10Costs = new long[] { 0L, 0L, 0L, 0L, 1_100L, 0L, 0L, 0L, 0L },
};
CharacterStatController.Bind(layout, () => sheet, onRaiseRequest: requests.Add);
CharacterStatController.Bind(layout, () => sheet,
onRaiseRequest: (request, completed) => { requests.Add(request); completed(); });
list.Children.OfType<UiClickablePanel>().ToList()[4].OnClick!();
btn10.OnClick!();
@ -1004,7 +1008,7 @@ public class CharacterStatControllerTests
CharacterStatController.Bind(layout, SampleData.SampleCharacter,
spriteResolve: id => (id, 16, 16),
onRaiseRequest: requests.Add);
onRaiseRequest: (request, completed) => { requests.Add(request); completed(); });
ClickTab(layout, left: 92f);
SkillRows(list)[1].OnClick!();
@ -1063,7 +1067,7 @@ public class CharacterStatControllerTests
CharacterStatController.Bind(layout, SampleData.SampleCharacter,
spriteResolve: id => (id, 16, 16),
onRaiseRequest: requests.Add);
onRaiseRequest: (request, completed) => { requests.Add(request); completed(); });
ClickTab(layout, left: 92f);
SkillRows(list)[5].OnClick!();
@ -1107,7 +1111,7 @@ public class CharacterStatControllerTests
CharacterStatController.Bind(layout, () => sheet,
spriteResolve: id => (id, 16, 16),
onRaiseRequest: request =>
onRaiseRequest: (request, completed) =>
{
requests.Add(request);
sheet = new CharacterSheet
@ -1124,9 +1128,10 @@ public class CharacterStatControllerTests
TrainedCost: 4,
SpecializedCost: 0,
RaiseCost: 10,
Raise10Cost: 100),
Raise10Cost: 100),
},
};
completed();
});
ClickTab(layout, left: 92f);
@ -1144,6 +1149,58 @@ public class CharacterStatControllerTests
Assert.Equal("Normal", btn10.ActiveState);
}
[Fact]
public void SkillsTab_DeferredTrainRefreshesOnlyWhenRequestCompletes()
{
var list = new UiPanel { Width = 300 };
var btn1 = MakeButton();
var btn10 = MakeButton();
CharacterSheet sheet = TrainingSheet(CharacterSkillAdvancementClass.Untrained);
var layout = Fake(
(CharacterStatController.ListBoxId, list),
(CharacterStatController.RaiseOneId, btn1),
(CharacterStatController.RaiseTenId, btn10));
Action? completeRaise = null;
CharacterStatController.Bind(layout, () => sheet,
spriteResolve: id => (id, 16, 16),
onRaiseRequest: (_, completed) => completeRaise = completed);
ClickTab(layout, left: 92f);
SkillRows(list).Single().OnClick!();
btn1.OnClick!();
sheet = TrainingSheet(CharacterSkillAdvancementClass.Trained);
Assert.NotNull(completeRaise);
Assert.False(btn10.Visible);
completeRaise!();
Assert.True(btn10.Visible);
static CharacterSheet TrainingSheet(CharacterSkillAdvancementClass advancement)
=> new()
{
SkillCredits = advancement == CharacterSkillAdvancementClass.Untrained ? 10 : 6,
UnassignedXp = 1_000,
Skills =
[
new CharacterSkill(
100u,
"Train Me",
0x06000001u,
advancement,
BaseLevel: 5,
CurrentLevel: 5,
UsableUntrained: true,
TrainedCost: 4,
SpecializedCost: 0,
RaiseCost: advancement == CharacterSkillAdvancementClass.Untrained ? 0 : 10,
Raise10Cost: advancement == CharacterSkillAdvancementClass.Untrained ? 0 : 100),
],
};
}
[Fact]
public void SkillsTab_BindsCharacterScrollbarToScrollableViewport()
{