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>
115 lines
3.6 KiB
C#
115 lines
3.6 KiB
C#
namespace AcDream.App.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Property identifiers consumed by retail's <c>Dialog</c> and
|
|
/// <c>DialogFactory</c> implementations.
|
|
/// </summary>
|
|
public static class RetailDialogProperty
|
|
{
|
|
public const uint Priority = 0x8Du;
|
|
public const uint Type = 0x8Eu;
|
|
public const uint AcceptLabel = 0x90u;
|
|
public const uint RejectLabel = 0x91u;
|
|
public const uint ConfirmationResult = 0x92u;
|
|
/// <summary>
|
|
/// When true, <c>Dialog::SetData @ 0x00476BE0</c> sets UIElement boolean
|
|
/// attribute <c>0x40</c>. The Keystone-owned attribute name is unavailable.
|
|
/// </summary>
|
|
public const uint ElementAttribute40 = 0xACu;
|
|
public const uint QueueKey = 0xC3u;
|
|
public const uint Message = 0xC5u;
|
|
public const uint UsageObjectId = 0x1000003Du;
|
|
public const uint TrainSkillId = 0x10000040u;
|
|
public const uint TrainSkillCredits = 0x10000041u;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retail dialog types selected by property <c>0x8E</c> in
|
|
/// <c>DialogFactory::CreateDialog_ @ 0x00477AD0</c>.
|
|
/// </summary>
|
|
public enum RetailDialogType : uint
|
|
{
|
|
Confirmation = 1,
|
|
Wait = 2,
|
|
Message = 3,
|
|
TextInput = 4,
|
|
ConfirmationTextInput = 5,
|
|
Menu = 6,
|
|
ConfirmationMenu = 7,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Modern typed carrier for retail's <c>PropertyCollection</c> dialog contract.
|
|
/// The raw numeric keys remain visible because type-specific presenters and semantic
|
|
/// callbacks both extend the same collection in retail.
|
|
/// </summary>
|
|
public sealed class RetailDialogData
|
|
{
|
|
private readonly Dictionary<uint, object> _values = new();
|
|
|
|
public IReadOnlyDictionary<uint, object> Values => _values;
|
|
|
|
public RetailDialogData Set<T>(uint propertyId, T value) where T : notnull
|
|
{
|
|
_values[propertyId] = value;
|
|
return this;
|
|
}
|
|
|
|
public bool Contains(uint propertyId) => _values.ContainsKey(propertyId);
|
|
|
|
public bool TryGet<T>(uint propertyId, out T value)
|
|
{
|
|
if (_values.TryGetValue(propertyId, out object? raw) && raw is T typed)
|
|
{
|
|
value = typed;
|
|
return true;
|
|
}
|
|
|
|
value = default!;
|
|
return false;
|
|
}
|
|
|
|
public bool GetBoolean(uint propertyId, bool defaultValue = false)
|
|
=> _values.TryGetValue(propertyId, out object? raw)
|
|
? raw switch
|
|
{
|
|
bool value => value,
|
|
byte value => value != 0,
|
|
int value => value != 0,
|
|
uint value => value != 0,
|
|
_ => defaultValue,
|
|
}
|
|
: defaultValue;
|
|
|
|
public uint GetUInt32(uint propertyId, uint defaultValue = 0u)
|
|
=> _values.TryGetValue(propertyId, out object? raw)
|
|
? raw switch
|
|
{
|
|
byte value => value,
|
|
ushort value => value,
|
|
int value when value >= 0 => (uint)value,
|
|
uint value => value,
|
|
Enum value => Convert.ToUInt32(value),
|
|
_ => defaultValue,
|
|
}
|
|
: defaultValue;
|
|
|
|
public string? GetString(uint propertyId)
|
|
=> _values.TryGetValue(propertyId, out object? raw) ? raw as string : null;
|
|
|
|
public RetailDialogData Clone()
|
|
{
|
|
var clone = new RetailDialogData();
|
|
foreach ((uint propertyId, object value) in _values)
|
|
clone._values.Add(propertyId, value);
|
|
return clone;
|
|
}
|
|
|
|
public static RetailDialogData Confirmation(string message)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(message);
|
|
return new RetailDialogData()
|
|
.Set(RetailDialogProperty.Type, RetailDialogType.Confirmation)
|
|
.Set(RetailDialogProperty.Message, message);
|
|
}
|
|
}
|