acdream/src/AcDream.Core/Chat/WeenieErrorMessages.cs
Erik 05f6222865 fix(ui): complete live targeted healing flow
Route ACE server commands through the existing chat path, bind the retail paperdoll hit mask instead of its obscured viewport, and prefer authoritative private health vitals. Record the user-confirmed live gate and pin the production DAT widget type in tests.
2026-07-11 07:58:59 +02:00

164 lines
7.3 KiB
C#

using System.Collections.Generic;
namespace AcDream.Core.Chat;
/// <summary>
/// Translates ACE <c>WeenieError</c> + <c>WeenieErrorWithString</c> codes
/// into the human-readable templates the retail client showed. The
/// retail client baked these strings into <c>string_table.bin</c>; for
/// our purposes we mirror ACE's enum-doc comments
/// (<c>references/ACE/Source/ACE.Entity/Enum/WeenieError.cs</c> +
/// <c>WeenieErrorWithString.cs</c>) since they preserve the original
/// templates verbatim, including the literal <c>_</c> placeholder where
/// the parameter goes.
///
/// <para>
/// <b>Why we need this:</b> Many of the codes ACE sends — especially
/// the high-frequency ones the user actually sees in normal play —
/// are <i>informational</i>, not error-level (e.g. <c>0x051B</c> =
/// "You have entered the X channel.", <c>0x051D</c> = "Turbine Chat
/// is enabled."). Displaying them as <c>WeenieError 0xNNNN</c> is
/// noisy and misleading. With the proper template they read as the
/// retail player would have seen them.
/// </para>
///
/// <para>
/// We don't translate every code — only the ~30 most likely to appear
/// in a normal session. Unknown codes fall back to the
/// <c>WeenieError 0xNNNN[: param]</c> form so nothing is silently
/// lost. New codes can be added in 30 seconds when the user reports
/// one.
/// </para>
/// </summary>
public static class WeenieErrorMessages
{
/// <summary>
/// Format a WeenieError / WeenieErrorWithString into a human-readable
/// system-message string.
/// </summary>
/// <param name="errorCode">The wire error code.</param>
/// <param name="param">The interpolated substring (null for plain
/// <c>WeenieError</c>, set for <c>WeenieErrorWithString</c>).</param>
public static string Format(uint errorCode, string? param)
{
// WeenieErrorWithString templates use the literal underscore
// character `_` as the placeholder for the param. We
// substitute it for `param` if present, otherwise drop it.
if (param is not null && WithStringTemplates.TryGetValue(errorCode, out var withTemplate))
{
return withTemplate.Replace("_", param);
}
if (NoParamTemplates.TryGetValue(errorCode, out var template))
{
// Some "no-param" codes do still arrive with a meaningless
// param string; ignore it.
return template;
}
// Unknown code — fall back to the raw form.
return string.IsNullOrEmpty(param)
? $"WeenieError 0x{errorCode:X4}"
: $"WeenieError 0x{errorCode:X4}: {param}";
}
/// <summary>
/// Codes from <c>WeenieError</c> (no param). Templates copied
/// verbatim from ACE enum-doc comments.
/// </summary>
private static readonly Dictionary<uint, string> NoParamTemplates = new()
{
// Command parser
[0x0026] = "That is not a valid command.", // ThatIsNotAValidCommand
// Tell-related
[0x052B] = "That person is not available now.", // CharacterNotAvailable
// Chat-channel related
[0x051D] = "Turbine Chat is enabled.", // TurbineChatIsEnabled
// Trade
[0x0529] = "Trade Complete!", // TradeComplete
// Allegiance / Fellowship membership errors (high-frequency
// when player isn't in either group and tries the channel)
[0x0414] = "You are not in an allegiance!", // YouAreNotInAllegiance
[0x050F] = "You do not belong to a Fellowship.", // YouDoNotBelongToAFellowship
// Allegiance
[0x0496] = "Your Allegiance has been dissolved!", // YourAllegianceHasBeenDissolved
[0x0497] = "Your patron's Allegiance to you has been broken!",
// YourPatronsAllegianceHasBeenBroken
[0x0535] = "You do not have the authority within your allegiance to do that.",
// Movement / teleport / housing
[0x0498] = "You have moved too far!", // YouHaveMovedTooFar
[0x0499] = "That is not a valid destination!", // TeleToInvalidPosition
[0x0532] = "You must wait 30 days after purchasing a house before you may purchase another with any character on the same account.",
// Fellowship
[0x0528] = "The fellowship is locked; you cannot open locked fellowships.",
// Player flavour
[0x0526] = "You chicken out.", // YouChickenOut
};
/// <summary>
/// Codes from <c>WeenieErrorWithString</c>. Templates copied
/// verbatim from ACE enum-doc comments. The <c>_</c> placeholder
/// is substituted with the param at format time.
/// </summary>
private static readonly Dictionary<uint, string> WithStringTemplates = new()
{
// Channel join / leave (high frequency at login)
[0x051B] = "You have entered the _ channel.", // YouHaveEnteredThe_Channel
[0x051C] = "You have left the _ channel.", // YouHaveLeftThe_Channel
// Chat-server failures
[0x051E] = "_ will not receive your message, please use urgent assistance to speak with an in-game representative.",
[0x051F] = "Message Blocked: _", // MessageBlocked_
// Hear / loud-list
[0x0521] = "_ has been added to the list of people you can hear.",
[0x0522] = "_ has been removed from the list of people you can hear.",
[0x0525] = "You fail to remove _ from your loud list.",
// Snooping (admin)
[0x052C] = "You are now snooping on _.",
[0x052D] = "You are no longer snooping on _.",
[0x052E] = "You fail to snoop on _.",
[0x052F] = "_ attempted to snoop on you.",
[0x0551] = "You are not listening to the _ channel.",
// Allegiance
[0x046A] = "_ doesn't know what to do with that.",
[0x0413] = "_ is already one of your followers.",
[0x0416] = "_ cannot have any more Vassals.",
[0x03EF] = "_ is not accepting gifts right now.",
// Combat / spell failures
[0x004E] = "You fail to affect _ because you cannot affect anyone!",
[0x004F] = "You fail to affect _ because they cannot be harmed!",
[0x0050] = "You fail to affect _ because beneficial spells do not affect them!",
[0x0051] = "You fail to affect _ because you are not a player killer!",
[0x0052] = "You fail to affect _ because they are not a player killer!",
[0x0053] = "You fail to affect _ because you are not the same sort of player killer as them!",
[0x0054] = "You fail to affect _ because you are acting across a house boundary!",
// Healing
[0x04FF] = "_ is already at full health!", // _IsAtFullHealth
// Inventory / etiquette
[0x001E] = "_ is too busy to accept gifts right now.",
[0x002B] = "_ cannot carry anymore.",
// Fellowship
[0x0517] = "_ is not close enough to your level.",
[0x0518] = "This fellowship is locked; _ cannot be recruited into the fellowship.",
// Hooks
[0x0510] = "Maximum number of _ hooked.",
[0x0514] = "Maximum number of _ hooked until one is removed.",
[0x0515] = "You no longer have the maximum number of _ hooked. You may hook additional.",
};
}