The FA1 contract asked to "add the 8 missing fellowship entries lane B lists" (0x0417-0x041C, 0x04DB, 0x04DC). Re-ran this table's own binary- sweep methodology specifically for these 8 ids rather than inventing text for them: a full-text grep of the 1.4M-line acclient_2013_pseudo_c.txt found ZERO comparisons/case-labels against any of the 8 anywhere in the retail client, and a manual walk of HandleFailureEvent's own case-label sequence confirmed the switch goes straight from case 0x416/0x41d (skipping 0x417-0x41c) and from case 0x4da/0x4dd (skipping 0x4db/0x4dc). Conclusion: retail's Sept-2013 client has no display text for any of these 8 ids -- they are intentionally absent from this table, not overlooked. This contradicts the FA1 contract's premise but not lane B's own text, which only claimed the ids were "missing" from the table (true) and that two of them (0x0417, 0x04DB) are on ACE's live send paths (also true) -- it never claimed retail has text for them. Two of the ids are therefore live-but-silent gaps against a real ACE server, and acdream's current no-display behavior for them is ALREADY retail-faithful. Adding invented English would be exactly the class of mistake SHOULD-FIX 4 (the no-default-case rule this table's Resolve() already implements) exists to prevent. Documents the finding at both table gaps and adds a conformance test (Resolve_FellowshipIdsAbsentFromHandleFailureEvent_ReturnsNoText) proving all 8 resolve to null text, matching the existing Format_0x051D_ReturnsNull_NoRetailCaseExists precedent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
639 lines
54 KiB
C#
639 lines
54 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 retail actually showed, AND the retail
|
|
/// destination (<see cref="RetailLogTextType"/>) each one routed to.
|
|
///
|
|
/// <para>
|
|
/// <b>Campaign CH slice CH2 (2026-08-09):</b> this is a full port of
|
|
/// <c>ClientCommunicationSystem::HandleFailureEvent @0x00571990</c>
|
|
/// (Sept 2013 EoR build), the 339-case switch that decides BOTH the display
|
|
/// string and the <c>AddTextToScroll</c> type argument for every
|
|
/// <c>WeenieError</c>/<c>WeenieErrorWithString</c> id retail's client knows
|
|
/// about. Originally transcribed from
|
|
/// <c>docs/research/2026-08-09-chat-retail-interface-text.md</c> Appendix A
|
|
/// (itself read off the named retail decomp's ~33-char inline string
|
|
/// previews).
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>REJECT-review rework (2026-08-09, <c>docs/research/2026-08-09-ch2-review-findings.md</c>
|
|
/// BLOCKER 2):</b> the Appendix A transcription — "enumerate <c>case</c>
|
|
/// labels, read the truncated preview" — turned out to be structurally
|
|
/// unsound: it missed 5 ids dispatched via <c>else if (arg2 == N)</c> rather
|
|
/// than a <c>case</c> label, and it silently trusted several truncated
|
|
/// previews whose full text differs materially from the 33-char prefix.
|
|
/// This rework re-derives the table MECHANICALLY: a Python sweep
|
|
/// (<c>tools/pdb-extract/sweep_weenie_strings.py</c>) walks the PE section
|
|
/// table of the PDB-paired binary (<c>C:\Users\erikn\Downloads\acclient.exe</c>,
|
|
/// verified via <c>check_exe_pdb.py</c>) and every <c>push imm32</c>
|
|
/// (opcode <c>0x68</c>) operand in VA <c>0x571990</c>-<c>0x575480</c> that
|
|
/// dereferences into <c>.rdata</c>/<c>.data</c> is read as a UTF-16LE
|
|
/// literal to its NUL terminator — the FULL string, never the pseudo-C's
|
|
/// truncated preview. Every changed row was additionally cross-checked
|
|
/// against ACE's own <c>WeenieError.cs</c>/<c>WeenieErrorWithString.cs</c>
|
|
/// enum doc comments (<c>references/ACE/Source/ACE.Entity/Enum/</c>); the
|
|
/// two independent oracles agreed on every row.
|
|
/// </para>
|
|
///
|
|
/// <list type="bullet">
|
|
/// <item><b>5 ids added</b> that Appendix A missed entirely because they
|
|
/// dispatch via <c>else if (arg2 == N)</c> chains ABOVE the main switch,
|
|
/// not a <c>case</c> label: <c>0x04F</c> (Magic), <c>0x3EE</c>
|
|
/// (ClientLocal), <c>0x408</c> (ClientLocal), <c>0x48A</c> (Default),
|
|
/// <c>0x4E8</c> (Default). <c>0x43</c> remains correctly absent — its
|
|
/// only retail effect is <c>ClientCombatSystem::AbortAutomaticAttack</c>,
|
|
/// no display text.</item>
|
|
/// <item><b>18 existing rows corrected</b> — the swept binary literal
|
|
/// disagreed with the previously-landed text for
|
|
/// <c>0x051</c>/<c>0x053</c>/<c>0x054</c>/<c>0x466</c>/<c>0x4A3</c>/
|
|
/// <c>0x4B5</c>/<c>0x4E0</c>/<c>0x4E9</c>/<c>0x4F7</c>/<c>0x518</c>/
|
|
/// <c>0x544</c>/<c>0x54E</c>/<c>0x552</c>/<c>0x553</c>/<c>0x554</c>/
|
|
/// <c>0x555</c>/<c>0x57F</c>/<c>0x582</c>. Two of these (<c>0x4E9</c>,
|
|
/// <c>0x518</c>) were NOT in the review's own flagged list — they surfaced
|
|
/// from an automated diff between every swept literal and the landed
|
|
/// table, confirming the review's own "sweep may find more" prediction.
|
|
/// <c>0x4E9</c> is the sharpest case: the review's own findings doc
|
|
/// proposed the wrong text for the NEW <c>0x4E8</c> row (attributing
|
|
/// <c>0x4E9</c>'s genuine text to it) — the corrected mechanical anchor
|
|
/// (the <c>else if (arg2 == 0x4e8)</c> block's OWN instruction address,
|
|
/// not proximity to a neighboring <c>case</c> label in the printed
|
|
/// listing) plus the ACE cross-check together overrule that proposal:
|
|
/// <c>0x4E8</c> = "...and only the owner may open the hook.", <c>0x4E9</c>
|
|
/// = "...use the '@house hooks on' command to make the hook openable." —
|
|
/// the previously-landed table had them reversed (0x4E9 held 0x4E8's
|
|
/// text; 0x4E8 did not exist as a row at all).</item>
|
|
/// <item><c>0x4F8</c> now resolves for real (see below) — no id is
|
|
/// deliberately excluded any more. New pinned count: 344 rows
|
|
/// (338 landed + 5 added + <c>0x4F8</c>).</item>
|
|
/// <item>7 ids Appendix A marked "no literal — shared string global"
|
|
/// (<c>0x024</c>, <c>0x048</c>, <c>0x049</c>, <c>0x4DE</c>, <c>0x4DF</c>,
|
|
/// <c>0x55A</c>, <c>0x55E</c>) remain resolved exactly as the prior pass
|
|
/// found: <c>0x024</c>/<c>0x048</c>/<c>0x049</c> reuse the same
|
|
/// process-lifetime globals as the local jump-refusal family (see
|
|
/// <see cref="ClientTextRefusals"/>); <c>0x4DE</c>/<c>0x4DF</c> are
|
|
/// <c>arg3 + "\n"</c>; <c>0x55A</c> is <c>sprintf("%s\n", arg3)</c>;
|
|
/// <c>0x55E</c> passes <c>arg3</c> straight through with no format string
|
|
/// at all.</item>
|
|
/// <item><c>0x4F4</c>'s retail literal is
|
|
/// <c>"%s fails to affect you because $s cannot affect anyone!"</c> —
|
|
/// note <c>$s</c>, not <c>%s</c>, for the second placeholder. That is a
|
|
/// genuine retail typo/bug (only the first <c>%s</c> substitutes; the
|
|
/// literal <c>$s</c> prints as-is) and is preserved verbatim rather than
|
|
/// corrected, per the "the client is probably right" rule. <c>0x04F</c>
|
|
/// preserves the same <c>$s</c> typo pattern.</item>
|
|
/// <item><c>0x4F7</c>'s retail string is a normal, COMPLETE
|
|
/// <c>arg3 + literal</c> concatenation — "%s fails to affect you because
|
|
/// you are not a player killer!" — with no dangling text. The prior
|
|
/// pass's class-doc claim that <c>0x4F7</c>'s literal "dangles" was a
|
|
/// misattribution of <c>0x4F8</c>'s FIRST operand (which does end
|
|
/// mid-clause, on "...as ", by design — see <c>0x4F8</c> below), not a
|
|
/// genuine truncation in <c>0x4F7</c> itself.</item>
|
|
/// <item><c>0x4F8</c> resolves cleanly once traced correctly: its case
|
|
/// body concatenates <c>arg3 + " fails to affect you because you are not
|
|
/// the same sort of player killer as " + arg3 + "!\n"</c> across three
|
|
/// <c>operator+</c> calls whose decompiled operand names are BN
|
|
/// self-referential artifacts (see
|
|
/// <c>claude-memory/feedback_bn_decomp_field_names.md</c>) — the sweep's
|
|
/// direct dereference of both literal data pointers
|
|
/// (<c>data_7d2ee8</c> = <c>" fails to affect you because you are not the
|
|
/// same sort of player killer as "</c>, <c>data_7d2f80</c> =
|
|
/// <c>"!\n"</c>) resolves the ambiguity without needing to trust the
|
|
/// confusing decompiler naming. Both <c>%s</c> placeholders take the SAME
|
|
/// parameter (retail only has one <c>arg3</c> to substitute twice).</item>
|
|
/// </list>
|
|
///
|
|
/// <para>
|
|
/// This retires register row AP-176 (the CH1-era approximation that fixed
|
|
/// every WeenieError display at <c>LogTextType.Default</c> pending this
|
|
/// full port).
|
|
/// </para>
|
|
/// </summary>
|
|
public static class WeenieErrorMessages
|
|
{
|
|
/// <summary>
|
|
/// Returns true for internal client-control statuses that retail consumes
|
|
/// without adding a line to the chat scroll.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <c>ClientCommunicationSystem::HandleFailureEvent</c>
|
|
/// (<c>0x00571990</c>) has no display case for ILeftTheWorld
|
|
/// (<c>0x003B</c>) or ITeleported (<c>0x003C</c>). ACE sends the latter
|
|
/// after a successful portal use; presenting it as a failure is therefore
|
|
/// an acdream-only artifact.
|
|
/// </remarks>
|
|
public static bool IsSilentClientControlStatus(uint errorCode) =>
|
|
errorCode is 0x003Bu or 0x003Cu;
|
|
|
|
/// <summary>
|
|
/// One retail routing-table row: the display template (retail's own
|
|
/// <c>%s</c> placeholder, substituted verbatim — never retail's literal
|
|
/// <c>$s</c>, see <c>0x4F4</c> above) plus the exact
|
|
/// <see cref="RetailLogTextType"/> <c>HandleFailureEvent</c> passed to
|
|
/// <c>AddTextToScroll</c> for this id.
|
|
/// </summary>
|
|
public readonly record struct Entry(string Template, RetailLogTextType Type);
|
|
|
|
/// <summary>
|
|
/// Format a WeenieError / WeenieErrorWithString into a human-readable
|
|
/// system-message string. Back-compat wrapper over <see cref="Resolve"/>
|
|
/// for callers that only need text, not the routing type.
|
|
/// </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>
|
|
/// <returns>
|
|
/// The retail display text, or <see langword="null"/> for an id
|
|
/// <c>HandleFailureEvent</c>'s switch has no case for — see
|
|
/// <see cref="Resolve"/>.
|
|
/// </returns>
|
|
public static string? Format(uint errorCode, string? param) => Resolve(errorCode, param).Text;
|
|
|
|
/// <summary>
|
|
/// Resolve a WeenieError / WeenieErrorWithString code into its retail
|
|
/// display text AND the retail <see cref="RetailLogTextType"/> it routes
|
|
/// to.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// CH2 REJECT-review rework, SHOULD-FIX 4
|
|
/// (<c>docs/research/2026-08-09-ch2-review-findings.md</c>): retail's
|
|
/// <c>HandleFailureEvent</c> switch has NO <c>default:</c> case — an id
|
|
/// it does not recognize produces NO text at all, silently, toward the
|
|
/// player. The prior pass's <c>WeenieError 0xNNNN[: param]</c> hex
|
|
/// fallback was acdream's own invention with no retail counterpart (an
|
|
/// unregistered divergence). Callers must treat a <see langword="null"/>
|
|
/// <c>Text</c> as "retail shows nothing here" and skip display entirely;
|
|
/// they should still log the raw id to a diagnostics-only sink so an
|
|
/// unmapped code is not silently invisible to US, only to the player.
|
|
/// </remarks>
|
|
public static (string? Text, RetailLogTextType Type) Resolve(uint errorCode, string? param)
|
|
{
|
|
if (Table.TryGetValue(errorCode, out Entry entry))
|
|
{
|
|
string text = param is not null && entry.Template.Contains("%s")
|
|
? entry.Template.Replace("%s", param)
|
|
: entry.Template;
|
|
return (text, entry.Type);
|
|
}
|
|
|
|
return (null, RetailLogTextType.Default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The full retail routing table, transcribed from
|
|
/// <c>ClientCommunicationSystem::HandleFailureEvent @0x00571990</c> — 344
|
|
/// rows (338 landed at the prior pass + 5 ids the prior pass's
|
|
/// case-label enumeration missed + <c>0x4F8</c>, which now resolves for
|
|
/// real; every id the switch dispatches has a row here). See the class
|
|
/// doc comment for the binary-sweep + ACE cross-check methodology and
|
|
/// every correction made against it.
|
|
/// </summary>
|
|
private static readonly Dictionary<uint, Entry> Table = new()
|
|
{
|
|
[0x017u] = new("You failed to go to non-combat mode.", RetailLogTextType.ClientLocal),
|
|
[0x01Du] = new("You're too busy!", RetailLogTextType.ClientLocal),
|
|
[0x01Eu] = new("You must control both objects!", RetailLogTextType.ClientLocal),
|
|
[0x020u] = new("You must control both objects!", RetailLogTextType.ClientLocal),
|
|
[0x023u] = new("Unable to move to object!", RetailLogTextType.ClientLocal),
|
|
[0x024u] = new(ClientTextRefusals.CantJumpInAir, RetailLogTextType.ClientLocal),
|
|
[0x026u] = new("That is not a valid command.", RetailLogTextType.ClientLocal),
|
|
[0x028u] = new("The item is under someone else's control!", RetailLogTextType.ClientLocal),
|
|
[0x029u] = new("You cannot pick that up!", RetailLogTextType.ClientLocal),
|
|
[0x02Au] = new("You are too encumbered to carry that!", RetailLogTextType.ClientLocal),
|
|
[0x02Bu] = new("%s cannot carry anymore.", RetailLogTextType.Default),
|
|
[0x036u] = new("Action cancelled!", RetailLogTextType.ClientLocal),
|
|
[0x037u] = new("Unable to move to object!", RetailLogTextType.ClientLocal),
|
|
[0x038u] = new("Unable to move to object!", RetailLogTextType.ClientLocal),
|
|
[0x039u] = new("Unable to move to object!", RetailLogTextType.ClientLocal),
|
|
[0x03Au] = new("You can't do that... you're dead!", RetailLogTextType.ClientLocal),
|
|
[0x03Du] = new("You charged too far!", RetailLogTextType.ClientLocal),
|
|
[0x03Eu] = new("You are too tired to do that!", RetailLogTextType.ClientLocal),
|
|
[0x048u] = new(ClientTextRefusals.CantJumpPosition, RetailLogTextType.ClientLocal),
|
|
[0x049u] = new(ClientTextRefusals.CantJumpLoad, RetailLogTextType.ClientLocal),
|
|
[0x04Au] = new("Ack! You killed yourself!", RetailLogTextType.Default),
|
|
[0x04Du] = new("Invalid PK status!", RetailLogTextType.ClientLocal),
|
|
[0x04Eu] = new("You fail to affect %s because you cannot affect anyone!", RetailLogTextType.Magic),
|
|
// 0x04F added — BLOCKER2: else-if dispatch (arg2 == 0x4f), missed by
|
|
// the prior case-label enumeration. Preserves retail's own $s typo,
|
|
// same pattern as 0x4F4.
|
|
[0x04Fu] = new("You fail to affect %s because $s cannot be harmed!", RetailLogTextType.Magic),
|
|
[0x050u] = new("You fail to affect %s because beneficial spells do not affect %s!", RetailLogTextType.Magic),
|
|
// 0x051/0x053/0x054 corrected — BLOCKER2: the prior pass copied
|
|
// 0x04E's/0x053's template across all four "You fail to affect"
|
|
// Magic-family ids; each is in fact a DISTINCT retail literal.
|
|
[0x051u] = new("You fail to affect %s because you are not a player killer!", RetailLogTextType.Magic),
|
|
[0x052u] = new("You fail to affect %s because %s is not a player killer!", RetailLogTextType.Magic),
|
|
[0x053u] = new("You fail to affect %s because you are not the same sort of player killer as %s!", RetailLogTextType.Magic),
|
|
[0x054u] = new("You fail to affect %s because you are acting across a house boundary!", RetailLogTextType.Magic),
|
|
// 0x3EE added — BLOCKER2: else-if dispatch (arg2 == 0x3ee).
|
|
[0x3EEu] = new("The container is closed!", RetailLogTextType.ClientLocal),
|
|
[0x3EFu] = new("%s is not accepting gifts right now.", RetailLogTextType.Default),
|
|
[0x3F1u] = new("You failed to go to non-combat mode.", RetailLogTextType.ClientLocal),
|
|
[0x3F7u] = new("You are too fatigued to attack!", RetailLogTextType.ClientLocal),
|
|
[0x3F8u] = new("You are out of ammunition!", RetailLogTextType.ClientLocal),
|
|
[0x3F9u] = new("Your missile attack misfired!", RetailLogTextType.ClientLocal),
|
|
[0x3FAu] = new("You've attempted an impossible spell path!", RetailLogTextType.ClientLocal),
|
|
[0x3FEu] = new("You don't know that spell!", RetailLogTextType.ClientLocal),
|
|
[0x3FFu] = new("Incorrect target type", RetailLogTextType.ClientLocal),
|
|
[0x400u] = new("You don't have all the components for this spell.", RetailLogTextType.ClientLocal),
|
|
[0x401u] = new("You don't have enough Mana to cast this spell.", RetailLogTextType.ClientLocal),
|
|
[0x402u] = new("Your spell fizzled.", RetailLogTextType.Magic),
|
|
[0x403u] = new("Your spell's target is missing!", RetailLogTextType.ClientLocal),
|
|
[0x404u] = new("Your projectile spell mislaunched!", RetailLogTextType.ClientLocal),
|
|
[0x407u] = new("Your spell cannot be cast outside", RetailLogTextType.ClientLocal),
|
|
// 0x408 added — BLOCKER2: else-if dispatch (arg2 == 0x408), sibling
|
|
// of 0x407 above (both lack trailing punctuation in retail's own
|
|
// literal — not a display artifact).
|
|
[0x408u] = new("Your spell cannot be cast inside", RetailLogTextType.ClientLocal),
|
|
[0x40Au] = new("You are unprepared to cast a spell", RetailLogTextType.ClientLocal),
|
|
[0x40Bu] = new("You've already sworn your Allegiance", RetailLogTextType.ClientLocal),
|
|
[0x40Cu] = new("You don't have enough experience available to swear Allegiance", RetailLogTextType.ClientLocal),
|
|
[0x413u] = new("%s is already one of your followers", RetailLogTextType.ClientLocal),
|
|
[0x414u] = new("You are not in an allegiance!", RetailLogTextType.ClientLocal),
|
|
[0x416u] = new("%s cannot have any more Vassals", RetailLogTextType.ClientLocal),
|
|
// Campaign FA slice FA1 (2026-08-11): 0x0417-0x041C
|
|
// (FellowshipIgnoringRequests/FellowshipSquelched/
|
|
// FellowshipMaxDistanceExceeded/FellowshipMember/
|
|
// FellowshipIllegalLevel/FellowshipRecruitBusy) are INTENTIONALLY
|
|
// absent — verified, not overlooked. Re-ran this table's own
|
|
// methodology (the class-doc's binary sweep) specifically for
|
|
// these six ids: `grep`-walked the ENTIRE 1.4M-line
|
|
// acclient_2013_pseudo_c.txt for every literal/case-label
|
|
// comparison against each of 0x417..0x41c and found ZERO hits
|
|
// anywhere in the file, then independently walked
|
|
// HandleFailureEvent's own ~3350-line case-label list
|
|
// (0x40a..0x489 switch block) and confirmed the sequence goes
|
|
// ...case 0x414: case 0x416: case 0x41d:... with NO case for
|
|
// 0x417-0x41c in between. Retail's Sept-2013 client has no
|
|
// display text for any of these six ids — they fall through to
|
|
// the "no case matched" outcome this table already models via
|
|
// Resolve()'s null-Text/no-display return. ACE never sends any of
|
|
// them (only 0x0417 and 0x04DB below are on a live ACE send path,
|
|
// and 0x0417 shows nothing in retail regardless). Do not add rows
|
|
// for these — that would be invented English with no retail
|
|
// anchor, exactly the class of mistake SHOULD-FIX 4 fixed for the
|
|
// table's no-default-case rule.
|
|
[0x41Du] = new("You must be the leader of a Fellowship", RetailLogTextType.ClientLocal),
|
|
[0x41Eu] = new("Your Fellowship is full", RetailLogTextType.ClientLocal),
|
|
[0x41Fu] = new("That Fellowship name is not permitted", RetailLogTextType.ClientLocal),
|
|
[0x422u] = new("That channel doesn't exist.", RetailLogTextType.ClientLocal),
|
|
[0x423u] = new("You can't use that channel.", RetailLogTextType.ClientLocal),
|
|
[0x424u] = new("You're already on that channel.", RetailLogTextType.ClientLocal),
|
|
[0x425u] = new("You're not currently on that channel.", RetailLogTextType.ClientLocal),
|
|
[0x427u] = new("You cannot merge different stacks!", RetailLogTextType.ClientLocal),
|
|
[0x428u] = new("You cannot merge enchanted items!", RetailLogTextType.ClientLocal),
|
|
[0x429u] = new("You must control at least one stack!", RetailLogTextType.ClientLocal),
|
|
[0x432u] = new("Your craft attempt fails.", RetailLogTextType.ClientLocal),
|
|
[0x433u] = new("Your craft attempt fails.", RetailLogTextType.ClientLocal),
|
|
[0x434u] = new("Given that number of items, you cannot craft anything.", RetailLogTextType.ClientLocal),
|
|
[0x435u] = new("Your craft attempt fails.", RetailLogTextType.ClientLocal),
|
|
[0x437u] = new("Either you or one of the items involved does not pass the requirements for this craft interaction.", RetailLogTextType.ClientLocal),
|
|
[0x438u] = new("You do not have all the neccessary items.", RetailLogTextType.ClientLocal),
|
|
[0x439u] = new("Not all the items are avaliable.", RetailLogTextType.ClientLocal),
|
|
[0x43Au] = new("You must be at rest in peace mode to do trade skills.", RetailLogTextType.ClientLocal),
|
|
[0x43Bu] = new("You are not trained in that trade skill.", RetailLogTextType.ClientLocal),
|
|
[0x43Cu] = new("Your hands must be free.", RetailLogTextType.ClientLocal),
|
|
[0x43Du] = new("You cannot link to that portal!", RetailLogTextType.Magic),
|
|
[0x43Eu] = new("You have solved this quest too recently!", RetailLogTextType.Default),
|
|
[0x43Fu] = new("You have solved this quest too many times!", RetailLogTextType.Default),
|
|
[0x445u] = new("This item requires you to complete a specific quest before you can pick it up!", RetailLogTextType.Default),
|
|
[0x45Cu] = new("Player killers may not interact with that portal!", RetailLogTextType.Magic),
|
|
[0x45Du] = new("Non-player killers may not interact with that portal!", RetailLogTextType.Magic),
|
|
[0x45Eu] = new("You do not own a house!", RetailLogTextType.ClientLocal),
|
|
[0x45Fu] = new("You do not own a house!", RetailLogTextType.ClientLocal),
|
|
// 0x466 corrected — BLOCKER2: retail's colon-separated "Asheron's
|
|
// Call: Dark Majesty..." wording, distinct from the "--"-separated
|
|
// Throne of Destiny family at 0x552-0x555 below.
|
|
[0x466u] = new("You must purchase Asheron's Call: Dark Majesty to interact with that portal.", RetailLogTextType.Magic),
|
|
[0x469u] = new("You have used all the hooks you are allowed to use for this house.", RetailLogTextType.Default),
|
|
[0x46Au] = new("%s doesn't know what to do with that.", RetailLogTextType.Default),
|
|
[0x474u] = new("You must complete a quest to interact with that portal.", RetailLogTextType.Magic),
|
|
[0x47Fu] = new("You must own a house to use this command.", RetailLogTextType.ClientLocal),
|
|
[0x480u] = new("Your monarch does not own a mansion or a villa!", RetailLogTextType.ClientLocal),
|
|
[0x481u] = new("Your monarch does not own a mansion or a villa!", RetailLogTextType.ClientLocal),
|
|
[0x482u] = new("Your monarch has closed the mansion to the Allegiance.", RetailLogTextType.ClientLocal),
|
|
[0x488u] = new("You must be above level %s to purchase this dwelling.", RetailLogTextType.Default),
|
|
[0x489u] = new("You must be at or below level %s to purchase this dwelling.", RetailLogTextType.Default),
|
|
// 0x48A added — BLOCKER2: else-if dispatch (arg2 == 0x48a).
|
|
[0x48Au] = new("You must be a monarch to purchase this dwelling.", RetailLogTextType.Default),
|
|
[0x48Bu] = new("You must be above allegiance rank %s to purchase this dwelling.", RetailLogTextType.Default),
|
|
[0x48Cu] = new("You must be at or below allegiance rank %s to purchase this dwelling.", RetailLogTextType.Default),
|
|
[0x48Eu] = new("Your offer of Allegiance has been ignored.", RetailLogTextType.ClientLocal),
|
|
[0x48Fu] = new("You are already involved in something!", RetailLogTextType.ClientLocal),
|
|
[0x490u] = new("You must be a monarch to use this command.", RetailLogTextType.ClientLocal),
|
|
[0x491u] = new("You must specify a character to boot.", RetailLogTextType.ClientLocal),
|
|
[0x492u] = new("You can't boot yourself!", RetailLogTextType.ClientLocal),
|
|
[0x493u] = new("That character does not exist.", RetailLogTextType.ClientLocal),
|
|
[0x494u] = new("That person is not a member of your Allegiance!", RetailLogTextType.ClientLocal),
|
|
[0x495u] = new("No patron from which to break!", RetailLogTextType.ClientLocal),
|
|
[0x496u] = new("Your Allegiance has been dissolved!", RetailLogTextType.Default),
|
|
[0x497u] = new("Your patron's Allegiance to you has been broken!", RetailLogTextType.Default),
|
|
[0x498u] = new("You have moved too far!", RetailLogTextType.ClientLocal),
|
|
[0x499u] = new("That is not a valid destination!", RetailLogTextType.ClientLocal),
|
|
[0x49Au] = new("You must purchase Asheron's Call -- Dark Majesty to use this function.", RetailLogTextType.ClientLocal),
|
|
[0x49Bu] = new("You fail to link with the lifestone!", RetailLogTextType.Magic),
|
|
[0x49Cu] = new("You wandered too far to link with the lifestone!", RetailLogTextType.Magic),
|
|
[0x49Du] = new("You successfully link with the lifestone!", RetailLogTextType.Magic),
|
|
[0x49Eu] = new("You must have linked with a lifestone in order to recall to it!", RetailLogTextType.Magic),
|
|
[0x49Fu] = new("You fail to recall to the lifestone!", RetailLogTextType.Magic),
|
|
[0x4A0u] = new("You fail to link with the portal!", RetailLogTextType.Magic),
|
|
[0x4A1u] = new("You successfully link with the portal!", RetailLogTextType.Magic),
|
|
[0x4A2u] = new("You fail to recall to the portal!", RetailLogTextType.Magic),
|
|
// 0x4A3 corrected — BLOCKER2: retail's real text is about RECALL,
|
|
// not summon (0x4A5 below is the genuine "summon it" sibling).
|
|
[0x4A3u] = new("You must have linked with a portal in order to recall to it!", RetailLogTextType.Magic),
|
|
[0x4A4u] = new("You fail to summon the portal!", RetailLogTextType.Magic),
|
|
[0x4A5u] = new("You must have linked with a portal in order to summon it!", RetailLogTextType.Magic),
|
|
[0x4A6u] = new("You fail to teleport!", RetailLogTextType.Magic),
|
|
[0x4A7u] = new("You have been teleported too recently!", RetailLogTextType.Magic),
|
|
[0x4A8u] = new("You must be an Advocate to interact with that portal.", RetailLogTextType.Magic),
|
|
[0x4AAu] = new("Players may not interact with that portal.", RetailLogTextType.Magic),
|
|
[0x4ABu] = new("You are not powerful enough to interact with that portal!", RetailLogTextType.Magic),
|
|
[0x4ACu] = new("You are too powerful to interact with that portal!", RetailLogTextType.Magic),
|
|
[0x4ADu] = new("You cannot recall to that portal!", RetailLogTextType.Magic),
|
|
[0x4AEu] = new("You cannot summon that portal!", RetailLogTextType.Magic),
|
|
[0x4AFu] = new("The lock is already unlocked.", RetailLogTextType.ClientLocal),
|
|
[0x4B0u] = new("You can't lock or unlock that!", RetailLogTextType.ClientLocal),
|
|
[0x4B1u] = new("You can't lock or unlock what is open!", RetailLogTextType.ClientLocal),
|
|
[0x4B2u] = new("The key doesn't fit this lock.", RetailLogTextType.Default),
|
|
[0x4B3u] = new("The lock has been used too recently.", RetailLogTextType.ClientLocal),
|
|
[0x4B4u] = new("You aren't trained in lockpicking!", RetailLogTextType.ClientLocal),
|
|
// 0x4B5 corrected — BLOCKER2: distinct from 0x491's genuine "boot"
|
|
// wording (0x4B5 is the allegiance-query variant).
|
|
[0x4B5u] = new("You must specify a character to query.", RetailLogTextType.ClientLocal),
|
|
[0x4B6u] = new("Please use the allegiance panel to view your own information.", RetailLogTextType.ClientLocal),
|
|
[0x4B7u] = new("You have used that command too recently.", RetailLogTextType.ClientLocal),
|
|
[0x4B8u] = new("You do not own that salvage tool!", RetailLogTextType.Default),
|
|
[0x4B9u] = new("You do not own that salvage tool!", RetailLogTextType.Default),
|
|
[0x4BAu] = new("You do not own that salvage tool!", RetailLogTextType.Default),
|
|
[0x4BDu] = new("You do not own that salvage tool!", RetailLogTextType.Default),
|
|
[0x4BEu] = new("You do not own that item!", RetailLogTextType.Default),
|
|
[0x4BFu] = new("The %s was not suitable for salvaging.", RetailLogTextType.ClientLocal),
|
|
[0x4C0u] = new("The %s contains the wrong material.", RetailLogTextType.ClientLocal),
|
|
[0x4C1u] = new("The material cannot be created.", RetailLogTextType.Default),
|
|
[0x4C2u] = new("The list of items you are attempting to salvage is invalid.", RetailLogTextType.Default),
|
|
[0x4C3u] = new("You cannot salvage items that you are trading!", RetailLogTextType.Default),
|
|
[0x4C4u] = new("You must be a guest in this house to interact with that portal.", RetailLogTextType.Magic),
|
|
[0x4C5u] = new("Your Allegiance Rank is too low to use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x4C6u] = new("You must be %s to use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x4C7u] = new("Your Arcane Lore skill is too low to use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x4C8u] = new("That item doesn't have enough Mana.", RetailLogTextType.ClientLocal),
|
|
[0x4C9u] = new("Your %s is too low to use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x4CAu] = new("Only %s may use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x4CBu] = new("You must have %s specialized to use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x4CCu] = new("You have been involved in a player killer battle too recently to do that!", RetailLogTextType.Magic),
|
|
[0x4CEu] = new("%s is too busy to accept gifts right now.", RetailLogTextType.Default),
|
|
[0x4CFu] = new("%s cannot accept stacked objects. Try giving one at a time.", RetailLogTextType.Default),
|
|
[0x4D0u] = new("You have failed to alter your skill.", RetailLogTextType.Default),
|
|
[0x4D1u] = new("Your %s skill must be trained, not untrained or specialized, in order to be altered in this way!", RetailLogTextType.Default),
|
|
[0x4D2u] = new("You do not have enough skill credits to specialize your %s skill.", RetailLogTextType.Default),
|
|
[0x4D3u] = new("You have too many available experience points to be able to absorb the experience points from your %s skill. Please spend some of your experience points and try again.", RetailLogTextType.Default),
|
|
[0x4D4u] = new("Your %s skill is already untrained!", RetailLogTextType.Default),
|
|
[0x4D5u] = new("You are currently wielding items which require a certain level of %s. Your %s skill cannot be lowered while you are wielding these items. Please remove these items and try again.", RetailLogTextType.Default),
|
|
[0x4D6u] = new("You have succeeded in specializing your %s skill!", RetailLogTextType.Default),
|
|
[0x4D7u] = new("You have succeeded in lowering your %s skill from specialized to trained!", RetailLogTextType.Default),
|
|
[0x4D8u] = new("You have succeeded in untraining your %s skill!", RetailLogTextType.Default),
|
|
[0x4D9u] = new("Although you cannot untrain your %s skill, you have succeeded in recovering all the experience you had invested in it.", RetailLogTextType.Default),
|
|
[0x4DAu] = new("You have too many credits invested in specialized skills already! Before you can specialize your %s skill, you will need to unspecialize some other skill.", RetailLogTextType.Default),
|
|
// Campaign FA slice FA1 (2026-08-11): 0x04DB (FellowshipDeclined)
|
|
// and 0x04DC (FellowshipTimeout) are INTENTIONALLY absent, same
|
|
// finding as the 0x0417-0x041C block above. The switch's case
|
|
// sequence in this address range goes
|
|
// ...case 0x4da: case 0x4dd:... — 0x4db/0x4dc have no case. This
|
|
// is the sharper of the two gaps: ACE actually sends
|
|
// FellowshipDeclined (Fellowship.cs:147, on a recruit refusal),
|
|
// so a live server event genuinely produces NO text in the
|
|
// Sept-2013 client — acdream's current silent-drop behavior for
|
|
// it is therefore retail-faithful, not a bug to fix by inventing
|
|
// a line retail never showed.
|
|
[0x4DDu] = new("You have failed to alter your attributes.", RetailLogTextType.Default),
|
|
[0x4DEu] = new("%s", RetailLogTextType.Default),
|
|
[0x4DFu] = new("%s", RetailLogTextType.Default),
|
|
// 0x4E0 corrected — BLOCKER2: the prior pass copied 0x4D5's
|
|
// "skill cannot be lowered" template; retail's genuine 0x4E0 text is
|
|
// about ATTRIBUTES not transferring, a distinct sibling to 0x4DD
|
|
// ("failed to alter your attributes") and 0x4E1 ("succeeded in
|
|
// transferring your attributes!").
|
|
[0x4E0u] = new("You are currently wielding items which require a certain level of skill. Your attributes cannot be transferred while you are wielding these items. Please remove these items and try again.", RetailLogTextType.Default),
|
|
[0x4E1u] = new("You have succeeded in transferring your attributes!", RetailLogTextType.Default),
|
|
[0x4E2u] = new("This hook is a duplicated housing object. You may not add items to a duplicated housing object. Please empty the hook and allow it to reset.", RetailLogTextType.Default),
|
|
[0x4E3u] = new("That item is of the wrong type to be placed on this hook.", RetailLogTextType.Default),
|
|
[0x4E4u] = new("This chest is a duplicated housing object. You may not add items to a duplicated housing object. Please empty everything -- including backpacks -- out of the chest and allow the chest to reset.", RetailLogTextType.Default),
|
|
[0x4E5u] = new("This hook was a duplicated housing object. Since it is now empty, it will be deleted momentarily. Once it is gone, it is safe to use the other, non-duplicated hook that is here.", RetailLogTextType.Default),
|
|
[0x4E6u] = new("This chest was a duplicated housing object. Since it is now empty, it will be deleted momentarily. Once it is gone, it is safe to use the other, non-duplicated chest that is here.", RetailLogTextType.Default),
|
|
[0x4E7u] = new("You cannot swear allegiance to anyone because you own a monarch-only house. Please abandon your house and try again.", RetailLogTextType.Default),
|
|
// 0x4E8 added — BLOCKER2: else-if dispatch (arg2 == 0x4e8), anchored
|
|
// on that branch's OWN instruction address (not proximity to a
|
|
// neighboring case label — see the class doc comment's 0x4E9 note).
|
|
[0x4E8u] = new("The %s cannot be used while on a hook and only the owner may open the hook.", RetailLogTextType.Default),
|
|
// 0x4E9 corrected — BLOCKER2: the prior pass gave this row 0x4E8's
|
|
// text (the two are easy to conflate — near-duplicate wording for
|
|
// adjacent ids). Confirmed against ACE's ItemUnusableOnHook_CanOpen
|
|
// (0x04E9) doc comment.
|
|
[0x4E9u] = new("The %s cannot be used while on a hook, use the '@house hooks on' command to make the hook openable.", RetailLogTextType.Default),
|
|
[0x4EAu] = new("The %s can only be used while on a hook.", RetailLogTextType.Default),
|
|
[0x4EBu] = new("You can't do that while in the air!", RetailLogTextType.ClientLocal),
|
|
[0x4ECu] = new("You cannot modify your player killer status while you are recovering from a PK death.", RetailLogTextType.Default),
|
|
[0x4EDu] = new("Advocates may not change their player killer status!", RetailLogTextType.Default),
|
|
[0x4EEu] = new("Your level is too low to change your player killer status with this object.", RetailLogTextType.Default),
|
|
[0x4EFu] = new("Your level is too high to change your player killer status with this object.", RetailLogTextType.Default),
|
|
[0x4F0u] = new("You feel a harsh dissonance, and you sense that an act of killing you have committed recently is interfering with the conversion.", RetailLogTextType.Default),
|
|
[0x4F1u] = new("Bael'Zharon's power flows through you again. You are once more a player killer.", RetailLogTextType.Default),
|
|
[0x4F2u] = new("Bael'Zharon has granted you respite after your moment of weakness. You are temporarily no longer a player killer.", RetailLogTextType.Default),
|
|
[0x4F3u] = new("Lite Player Killers may not interact with that portal!", RetailLogTextType.Magic),
|
|
[0x4F4u] = new("%s fails to affect you because $s cannot affect anyone!", RetailLogTextType.Magic),
|
|
[0x4F5u] = new("%s fails to affect you because you cannot be harmed!", RetailLogTextType.Magic),
|
|
[0x4F6u] = new("%s fails to affect you because %s is not a player killer!", RetailLogTextType.Magic),
|
|
// 0x4F7 corrected — BLOCKER2: this is a COMPLETE, ordinary
|
|
// concatenation, not the dangling literal the prior pass's class
|
|
// doc misattributed to it (that "..as " dangle belongs to 0x4F8,
|
|
// its first operand — see the class doc comment).
|
|
[0x4F7u] = new("%s fails to affect you because you are not a player killer!", RetailLogTextType.Magic),
|
|
// 0x4F8 added — BLOCKER2: previously deliberately excluded pending
|
|
// a confident trace of its 3-operator+ concatenation chain; the
|
|
// binary sweep's direct dereference of both literal data pointers
|
|
// (data_7d2ee8 + data_7d2f80) resolves it. Both %s placeholders
|
|
// substitute the SAME parameter (retail only has one arg3).
|
|
[0x4F8u] = new("%s fails to affect you because you are not the same sort of player killer as %s!", RetailLogTextType.Magic),
|
|
[0x4F9u] = new("%s fails to affect you across a house boundary!", RetailLogTextType.Magic),
|
|
[0x4FAu] = new("%s is an invalid target.", RetailLogTextType.Magic),
|
|
[0x4FBu] = new("You are an invalid target for the spell of %s.", RetailLogTextType.Magic),
|
|
[0x4FCu] = new("You aren't trained in healing!", RetailLogTextType.ClientLocal),
|
|
[0x4FDu] = new("You don't own that healing kit!", RetailLogTextType.ClientLocal),
|
|
[0x4FEu] = new("You can't heal that!", RetailLogTextType.ClientLocal),
|
|
[0x4FFu] = new("%s is already at full health!", RetailLogTextType.ClientLocal),
|
|
[0x500u] = new("You aren't ready to heal!", RetailLogTextType.ClientLocal),
|
|
[0x501u] = new("You can only use Healing Kits on player characters.", RetailLogTextType.ClientLocal),
|
|
[0x502u] = new("The Lifestone's magic protects you from the attack!", RetailLogTextType.Magic),
|
|
[0x503u] = new("The portal's residual energy protects you from the attack!", RetailLogTextType.Magic),
|
|
[0x504u] = new("You are enveloped in a feeling of warmth as you are brought back into the protection of the Light. You are once again a Non-Player Killer.", RetailLogTextType.Default),
|
|
[0x505u] = new("You're too close to your sanctuary!", RetailLogTextType.ClientLocal),
|
|
[0x506u] = new("You can't do that -- you're trading!", RetailLogTextType.ClientLocal),
|
|
[0x507u] = new("Only Non-Player Killers may enter PK Lite. Please see @help pklite for more details about this command.", RetailLogTextType.Default),
|
|
[0x508u] = new("A cold wind touches your heart. You are now a Player Killer Lite.", RetailLogTextType.Default),
|
|
[0x509u] = new("%s has no appropriate targets equipped for this spell.", RetailLogTextType.Magic),
|
|
[0x50Au] = new("You have no appropriate targets equipped for %s's spell.", RetailLogTextType.Magic),
|
|
[0x50Bu] = new("%s is now an open fellowship; anyone may recruit new members.", RetailLogTextType.Default),
|
|
[0x50Cu] = new("%s is now a closed fellowship.", RetailLogTextType.Default),
|
|
[0x50Du] = new("%s is now the leader of this fellowship.", RetailLogTextType.Default),
|
|
[0x50Eu] = new("You have passed leadership of the fellowship to %s", RetailLogTextType.Default),
|
|
[0x50Fu] = new("You do not belong to a Fellowship.", RetailLogTextType.ClientLocal),
|
|
[0x510u] = new("You may not hook any more %s on your house. You already have the maximum number of %s hooked or you are not permitted to hook any on your type of house.", RetailLogTextType.Default),
|
|
[0x512u] = new("You are now using the maximum number of hooks. You cannot use another hook until you take an item off one of your hooks.", RetailLogTextType.Default),
|
|
[0x513u] = new("You are no longer using the maximum number of hooks. You may again add items to your hooks.", RetailLogTextType.Default),
|
|
[0x514u] = new("You now have the maximum number of %s hooked. You cannot hook any additional %s until you remove one or more from your house.", RetailLogTextType.Default),
|
|
[0x515u] = new("You no longer have the maximum number of %s hooked. You may hook additional %s.", RetailLogTextType.Default),
|
|
[0x516u] = new("You are not permitted to use that hook.", RetailLogTextType.Default),
|
|
[0x517u] = new("%s is not close enough to your level.", RetailLogTextType.Default),
|
|
// 0x518 corrected — BLOCKER2: not in the review's flagged list;
|
|
// surfaced from an automated diff between the swept binary
|
|
// literals and the landed table. Retail's real text is a 3-part
|
|
// concatenation ("This fellowship is locked; " + arg3 + " cannot be
|
|
// recruited into the fellowship."), not the arg3-only fragment
|
|
// previously landed. Confirmed against ACE's
|
|
// LockedFellowshipCannotRecruit_ (0x0518) doc comment.
|
|
[0x518u] = new("This fellowship is locked; %s cannot be recruited into the fellowship.", RetailLogTextType.Default),
|
|
[0x519u] = new("The fellowship is locked, you were not added to the fellowship.", RetailLogTextType.Default),
|
|
[0x51Au] = new("Only the original owner may use that item's magic.", RetailLogTextType.ClientLocal),
|
|
[0x51Bu] = new("You have entered the %s channel.", RetailLogTextType.Default),
|
|
[0x51Cu] = new("You have left the %s channel.", RetailLogTextType.Default),
|
|
[0x51Eu] = new("%s will not receive your message, please use urgent assistance to speak with an in-game representative", RetailLogTextType.Default),
|
|
[0x51Fu] = new("Message Blocked: %s", RetailLogTextType.ClientLocal),
|
|
[0x520u] = new("You cannot add anymore people to the list of players that you can hear.", RetailLogTextType.Default),
|
|
[0x521u] = new("%s has been added to the list of people you can hear.", RetailLogTextType.Default),
|
|
[0x522u] = new("%s has been removed from the list of people you can hear.", RetailLogTextType.Default),
|
|
[0x523u] = new("You are now deaf to player's screams.", RetailLogTextType.Default),
|
|
[0x524u] = new("You can hear all players once again.", RetailLogTextType.Default),
|
|
[0x525u] = new("You fail to remove %s from your loud list.", RetailLogTextType.Default),
|
|
[0x526u] = new("You chicken out.", RetailLogTextType.ClientLocal),
|
|
[0x527u] = new("You cannot posssibly succeed.", RetailLogTextType.ClientLocal),
|
|
[0x528u] = new("The fellowship is locked; you cannot open locked fellowships.", RetailLogTextType.Default),
|
|
[0x529u] = new("Trade Complete!", RetailLogTextType.ClientLocal),
|
|
[0x52Au] = new("That is not a salvaging tool.", RetailLogTextType.ClientLocal),
|
|
[0x52Bu] = new("That person is not available now.", RetailLogTextType.ClientLocal),
|
|
[0x52Cu] = new("You are now snooping on %s.", RetailLogTextType.Default),
|
|
[0x52Du] = new("You are no longer snooping on %s.", RetailLogTextType.Default),
|
|
[0x52Eu] = new("You fail to snoop on %s.", RetailLogTextType.Default),
|
|
[0x52Fu] = new("%s attempted to snoop on you.", RetailLogTextType.Default),
|
|
[0x530u] = new("%s is already being snooped on, only one person may snoop on another at a time.", RetailLogTextType.Default),
|
|
[0x531u] = new("%s is in limbo and cannot receive your message.", RetailLogTextType.Default),
|
|
[0x532u] = new("You must wait 30 days after purchasing a house before you may purchase another with any character on the same account. This applies to all housing except apartments.", RetailLogTextType.Default),
|
|
[0x533u] = new("You have been booted from your allegiance chat room. Use \"@allegiance chat on\" to rejoin. (%s).", RetailLogTextType.Default),
|
|
[0x534u] = new("%s has been booted from the allegiance chat room.", RetailLogTextType.Default),
|
|
[0x535u] = new("You do not have the authority within your allegiance to do that.", RetailLogTextType.Default),
|
|
[0x536u] = new("The account of %s is already banned from the allegiance.", RetailLogTextType.Default),
|
|
[0x537u] = new("The account of %s is not banned from the allegiance.", RetailLogTextType.Default),
|
|
[0x538u] = new("The account of %s was not unbanned from the allegiance.", RetailLogTextType.Default),
|
|
[0x539u] = new("The account of %s has been banned from the allegiance.", RetailLogTextType.Default),
|
|
[0x53Au] = new("The account of %s is no longer banned from the allegiance.", RetailLogTextType.Default),
|
|
[0x53Bu] = new("Banned Characters:", RetailLogTextType.Default),
|
|
[0x53Eu] = new("%s is banned from the allegiance!", RetailLogTextType.Default),
|
|
[0x53Fu] = new("You are banned from %s's allegiance!", RetailLogTextType.Default),
|
|
[0x540u] = new("You have the maximum number of accounts banned.!", RetailLogTextType.Default),
|
|
[0x541u] = new("%s is now an allegiance officer.", RetailLogTextType.Default),
|
|
[0x542u] = new("An unspecified error occurred while attempting to set %s as an allegiance officer.", RetailLogTextType.Default),
|
|
[0x543u] = new("%s is no longer an allegiance officer.", RetailLogTextType.Default),
|
|
// 0x544 corrected — BLOCKER2: retail's genuine text says REMOVE, not
|
|
// SET (0x542 above is the genuine "set" sibling); the prior pass
|
|
// duplicated 0x542's template here.
|
|
[0x544u] = new("An unspecified error occurred while attempting to remove %s as an allegiance officer.", RetailLogTextType.Default),
|
|
[0x545u] = new("You already have the maximum number of allegiance officers. You must remove some before you add any more.", RetailLogTextType.Default),
|
|
[0x546u] = new("Your allegiance officers have been cleared.", RetailLogTextType.Default),
|
|
[0x547u] = new("You must wait %s before communicating again!", RetailLogTextType.Default),
|
|
[0x548u] = new("You cannot join any chat channels while gagged.", RetailLogTextType.Default),
|
|
[0x549u] = new("Your allegiance officer status has been modified. You now hold the position of: %s.", RetailLogTextType.Default),
|
|
[0x54Au] = new("You are no longer an allegiance officer.", RetailLogTextType.Default),
|
|
[0x54Bu] = new("%s is already an allegiance officer of that level.", RetailLogTextType.Default),
|
|
[0x54Cu] = new("Your allegiance does not have a hometown.", RetailLogTextType.Default),
|
|
[0x54Du] = new("The %s is currently in use.", RetailLogTextType.ClientLocal),
|
|
// 0x54E corrected — BLOCKER2: retail's genuine 0x54E text is the
|
|
// "you do not own the house" variant, distinct from 0x54F's
|
|
// "@house hooks on" variant the prior pass duplicated onto both ids.
|
|
[0x54Eu] = new("The hook does not contain a usable item. You cannot open the hook because you do not own the house to which it belongs.", RetailLogTextType.Default),
|
|
[0x54Fu] = new("The hook does not contain a usable item. Use the '@house hooks on'command to make the hook openable.", RetailLogTextType.Default),
|
|
[0x550u] = new("Out of Range!", RetailLogTextType.ClientLocal),
|
|
[0x551u] = new("You are not listening to the %s channel!", RetailLogTextType.Default),
|
|
// 0x552-0x555 corrected — BLOCKER2: the prior pass copied 0x49A's
|
|
// "Dark Majesty...to use this function." template across all four
|
|
// ids. Retail's genuine family is the "--"-separated Throne of
|
|
// Destiny expansion, each with its OWN distinct ending
|
|
// (function/item/portal/quest) — not four identical strings.
|
|
// Confirmed against ACE's MustPurchaseThroneOfDestinyToUseFunction/
|
|
// ToUseItem/ToUsePortal/ToAccessQuest (0x0552-0x0555) doc comments.
|
|
[0x552u] = new("You must purchase Asheron's Call -- Throne of Destiny to use this function.", RetailLogTextType.ClientLocal),
|
|
[0x553u] = new("You must purchase Asheron's Call -- Throne of Destiny to use this item.", RetailLogTextType.ClientLocal),
|
|
[0x554u] = new("You must purchase Asheron's Call -- Throne of Destiny to use this portal.", RetailLogTextType.ClientLocal),
|
|
[0x555u] = new("You must purchase Asheron's Call -- Throne of Destiny to access this quest.", RetailLogTextType.ClientLocal),
|
|
[0x556u] = new("You have failed to complete the augmentation.", RetailLogTextType.Default),
|
|
[0x557u] = new("You have used this augmentation too many times already.", RetailLogTextType.Default),
|
|
[0x558u] = new("You have used augmentations of this type too many times already.", RetailLogTextType.Default),
|
|
[0x559u] = new("You do not have enough unspent experience available to purchase this augmentation.", RetailLogTextType.Default),
|
|
[0x55Au] = new("%s", RetailLogTextType.Default),
|
|
[0x55Bu] = new("Congratulations! You have succeeded in acquiring the %s augmentation.", RetailLogTextType.Default),
|
|
[0x55Cu] = new("Although your augmentation will not allow you to untrain your %s skill, you have succeeded in recovering all the experience you had invested in it.", RetailLogTextType.Default),
|
|
[0x55Du] = new("You must exit the Training Academy before that command will be available to you.", RetailLogTextType.Default),
|
|
[0x55Eu] = new("%s", RetailLogTextType.Default),
|
|
[0x55Fu] = new("Only Player Killer characters may use this command!", RetailLogTextType.Default),
|
|
[0x560u] = new("Only Player Killer Lite characters may use this command!", RetailLogTextType.Default),
|
|
[0x561u] = new("You may only have a maximum of 50 friends at once. If you wish to add more friends, you must first remove some.", RetailLogTextType.ClientLocal),
|
|
[0x562u] = new("%s is already on your friends list!", RetailLogTextType.Default),
|
|
[0x563u] = new("That character is not on your friends list!", RetailLogTextType.Default),
|
|
[0x564u] = new("Only the character who owns the house may use this command.", RetailLogTextType.Default),
|
|
[0x565u] = new("That allegiance name is invalid because it is empty. Please use the @allegiance name clear command to clear your allegiance name.", RetailLogTextType.Default),
|
|
[0x566u] = new("That allegiance name is too long. Please choose another name.", RetailLogTextType.Default),
|
|
[0x567u] = new("That allegiance name contains illegal characters. Please choose another name using only letters, spaces, - and '.", RetailLogTextType.Default),
|
|
[0x568u] = new("That allegiance name is not appropriate. Please choose another name.", RetailLogTextType.Default),
|
|
[0x569u] = new("That allegiance name is already in use. Please choose another name.", RetailLogTextType.Default),
|
|
[0x56Au] = new("You may only change your allegiance name once every 24 hours. You may change your allegiance name again in %s.", RetailLogTextType.Default),
|
|
[0x56Bu] = new("Your allegiance name has been cleared.", RetailLogTextType.Default),
|
|
[0x56Cu] = new("That is already the name of your allegiance!", RetailLogTextType.Default),
|
|
[0x56Du] = new("%s is the monarch and cannot be promoted or demoted.", RetailLogTextType.Default),
|
|
[0x56Eu] = new("That level of allegiance officer is now known as: %s.", RetailLogTextType.Default),
|
|
[0x56Fu] = new("That is an invalid officer level.", RetailLogTextType.Default),
|
|
[0x570u] = new("That allegiance officer title is not appropriate.", RetailLogTextType.Default),
|
|
[0x571u] = new("That allegiance name is too long. Please choose another name.", RetailLogTextType.Default),
|
|
[0x572u] = new("All of your allegiance officer titles have been cleared.", RetailLogTextType.Default),
|
|
[0x573u] = new("That allegiance title contains illegal characters. Please choose another name using only letters, spaces, - and '.", RetailLogTextType.Default),
|
|
[0x574u] = new("Your allegiance is currently: %s.", RetailLogTextType.Default),
|
|
[0x575u] = new("Your allegiance is now: %s.", RetailLogTextType.Default),
|
|
[0x576u] = new("You may not accept the offer of allegiance from %s because your allegiance is locked.", RetailLogTextType.Default),
|
|
[0x577u] = new("You may not swear allegiance at this time because the allegiance of %s is locked.", RetailLogTextType.Default),
|
|
[0x578u] = new("You have pre-approved %s to join your allegiance.", RetailLogTextType.Default),
|
|
[0x579u] = new("You have not pre-approved any vassals to join your allegiance.", RetailLogTextType.Default),
|
|
[0x57Au] = new("%s is already a member of your allegiance!", RetailLogTextType.Default),
|
|
[0x57Bu] = new("%s has been pre-approved to join your allegiance.", RetailLogTextType.Default),
|
|
[0x57Cu] = new("You have cleared the pre-approved vassal for your allegiance.", RetailLogTextType.Default),
|
|
[0x57Du] = new("That character is already gagged!", RetailLogTextType.Default),
|
|
[0x57Eu] = new("That character is not currently gagged!", RetailLogTextType.Default),
|
|
// 0x57F corrected — BLOCKER2: 0x581's "restored" text was
|
|
// incorrectly duplicated here; 0x57F is the "removed" notice, the
|
|
// sibling that fires when privileges are taken away, not restored.
|
|
[0x57Fu] = new("Your allegiance chat privileges have been temporarily removed by %s. Until they are restored, you may not view or speak in the allegiance chat channel.", RetailLogTextType.Default),
|
|
[0x580u] = new("%s is now temporarily unable to view or speak in allegiance chat. The gag will run out in 5 minutes, or %s may be explicitly ungagged before then.", RetailLogTextType.Default),
|
|
[0x581u] = new("Your allegiance chat privileges have been restored.", RetailLogTextType.Default),
|
|
// 0x582 corrected — BLOCKER2: 0x581's unparameterized text was
|
|
// incorrectly duplicated here; 0x582 is the %s-parameterized
|
|
// sibling ("...restored BY %s.").
|
|
[0x582u] = new("Your allegiance chat privileges have been restored by %s.", RetailLogTextType.Default),
|
|
[0x583u] = new("You have restored allegiance chat privileges to %s.", RetailLogTextType.Default),
|
|
[0x584u] = new("You cannot pick up more of that item!", RetailLogTextType.ClientLocal),
|
|
[0x585u] = new("You are restricted to clothes and armor created for your race.", RetailLogTextType.ClientLocal),
|
|
[0x586u] = new("That item was specifically created for another race.", RetailLogTextType.ClientLocal),
|
|
[0x587u] = new("Olthoi cannot interact with that!", RetailLogTextType.Magic),
|
|
[0x588u] = new("Olthoi cannot use regular lifestones! Asheron would not allow it!", RetailLogTextType.Magic),
|
|
[0x589u] = new("The vendor looks at you in horror!", RetailLogTextType.Magic),
|
|
[0x58Au] = new("%s cowers from you!", RetailLogTextType.Default),
|
|
[0x58Bu] = new("As a mindless engine of destruction an Olthoi cannot join a fellowship!", RetailLogTextType.Magic),
|
|
[0x58Cu] = new("The Olthoi only have an allegiance to the Olthoi Queen!", RetailLogTextType.Magic),
|
|
[0x58Du] = new("You cannot use that item!", RetailLogTextType.Magic),
|
|
[0x58Eu] = new("This person will not interact with you!", RetailLogTextType.Magic),
|
|
[0x58Fu] = new("Only Olthoi may pass through this portal!", RetailLogTextType.Magic),
|
|
[0x590u] = new("Olthoi may not pass through this portal!", RetailLogTextType.Magic),
|
|
[0x591u] = new("You may not pass through this portal while Vitae weakens you!", RetailLogTextType.Magic),
|
|
[0x592u] = new("This character must be two weeks old or have been created on an account at least two weeks old to use this portal!", RetailLogTextType.Magic),
|
|
[0x593u] = new("Olthoi characters can only use Lifestone and PK Arena recalls!", RetailLogTextType.Magic),
|
|
};
|
|
}
|