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

@ -195,6 +195,14 @@ public static class CharacterStatController
long Cost,
int Amount);
/// <summary>
/// Handles a retail stat raise and invokes <paramref name="completed"/> when
/// the request has either been applied or cancelled. The completion seam is
/// required by asynchronous retail confirmation dialogs such as
/// <c>gmSkillUI::TrainSkill @ 0x0049C5F0</c>.
/// </summary>
public delegate void RaiseRequestHandler(RaiseRequest request, Action completed);
private sealed class TabVisual
{
public required CharacterStatTab Tab { get; init; }
@ -248,7 +256,7 @@ public static class CharacterStatController
UiDatFont? datFont = null,
UiDatFont? rowDatFont = null,
Func<uint, (uint handle, int w, int h)>? spriteResolve = null,
Action<RaiseRequest>? onRaiseRequest = null,
RaiseRequestHandler? onRaiseRequest = null,
Action? onClose = null)
{
// rowDatFont: larger font for attribute row name/value text (18px vs 16px default).
@ -1129,7 +1137,7 @@ public static class CharacterStatController
int[] attrSel,
int[] skillSel,
Func<IReadOnlyList<SkillRowBinding>> skillRows,
Action<RaiseRequest>? onRaiseRequest,
RaiseRequestHandler? onRaiseRequest,
Action<uint?>? afterRaiseRequest)
{
foreach (var button in allRaise1)
@ -1154,7 +1162,7 @@ public static class CharacterStatController
int[] attrSel,
int[] skillSel,
Func<IReadOnlyList<SkillRowBinding>> skillRows,
Action<RaiseRequest>? onRaiseRequest,
RaiseRequestHandler? onRaiseRequest,
Action<uint?>? afterRaiseRequest)
{
if (onRaiseRequest is null) return;
@ -1180,8 +1188,7 @@ public static class CharacterStatController
if (request is not { } value) return;
onRaiseRequest(value);
afterRaiseRequest?.Invoke(selectedSkillId);
onRaiseRequest(value, () => afterRaiseRequest?.Invoke(selectedSkillId));
}
private static RaiseRequest? TryBuildAttributeRaiseRequest(