using System.Collections.Generic; namespace AcDream.Core.Chat; /// /// Translates ACE WeenieError + WeenieErrorWithString codes /// into the human-readable templates the retail client showed. The /// retail client baked these strings into string_table.bin; for /// our purposes we mirror ACE's enum-doc comments /// (references/ACE/Source/ACE.Entity/Enum/WeenieError.cs + /// WeenieErrorWithString.cs) since they preserve the original /// templates verbatim, including the literal _ placeholder where /// the parameter goes. /// /// /// Why we need this: Many of the codes ACE sends — especially /// the high-frequency ones the user actually sees in normal play — /// are informational, not error-level (e.g. 0x051B = /// "You have entered the X channel.", 0x051D = "Turbine Chat /// is enabled."). Displaying them as WeenieError 0xNNNN is /// noisy and misleading. With the proper template they read as the /// retail player would have seen them. /// /// /// /// We don't translate every code — only the ~30 most likely to appear /// in a normal session. Unknown codes fall back to the /// WeenieError 0xNNNN[: param] form so nothing is silently /// lost. New codes can be added in 30 seconds when the user reports /// one. /// /// public static class WeenieErrorMessages { /// /// Format a WeenieError / WeenieErrorWithString into a human-readable /// system-message string. /// /// The wire error code. /// The interpolated substring (null for plain /// WeenieError, set for WeenieErrorWithString). 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}"; } /// /// Codes from WeenieError (no param). Templates copied /// verbatim from ACE enum-doc comments. /// private static readonly Dictionary 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 }; /// /// Codes from WeenieErrorWithString. Templates copied /// verbatim from ACE enum-doc comments. The _ placeholder /// is substituted with the param at format time. /// private static readonly Dictionary 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!", // 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.", }; }