feat(chat): Phase J - welcome message + own-echo dedup + long-form slash aliases + WeenieError templates
Six fixes from the 2026-04-25 live verify session. 1. ServerMessage (0xF7E0) wired to ChatLog. ACE's GameMessageSystemChat - used for the login banner "Welcome to Asheron's Call ... powered by ACEmulator ... type @acehelp" plus any future server broadcast - rides opcode 0xF7E0. The parser shipped in I.5 but the WorldSession.ServerMessageReceived event was never subscribed by GameWindow, so the welcome line was silently dropped. Subscribed now; same wave wires the missing EmoteHeard / SoulEmoteHeard / PlayerKilledReceived events that I.5 also left orphan. 2. Drop optimistic /say echo + plumb local-player-guid into ChatLog. ACE's HandleActionTalk broadcasts a HearSpeech back to the sender too, so we were double-printing every /say (own optimistic + server echo). New ChatLog.SetLocalPlayerGuid() pushes the chosen character guid in (mirrors VitalsVM pattern); OnLocalSpeech detects own-guid match and substitutes Sender="" so the formatter 's IsOwnSpeaker path renders "You say, ..." instead of "+Acdream says, ...". Single line per /say. 3. IsOwnSpeaker check now applies to ChatKind.Channel too. Empty/ "You" sender -> "[Allegiance] You say, \"text\"" instead of the "[Allegiance] says, \"text\"" double-space hole that Phase I.6's OnSelfSent left when echoing legacy ChatChannel sends. 4. Long-form slash aliases: /general /allegiance /patron /vassals /monarch /covassals /fellowship /fellow /lookingforgroup /roleplay /rp /tr /gen, plus /s as alias for /say. Retail muscle memory expected these; the prior parser only recognized /g /a /p /v /m /cv /lfg /role and friends, so "/patron hello" fell through as /say with the literal "/patron" prefix. 5. WeenieError templates filled in for the codes the user hit: - 0x0414 YouAreNotInAllegiance -> "You are not in an allegiance!" - 0x050F YouDoNotBelongToAFellowship -> "You do not belong to a Fellowship." Replaces the cryptic "WeenieError 0x0414" / "0x050F" lines. 6. @ command pass-through: ACE handles @help / @acehelp / @tele etc. server-side by intercepting Talk text with @ prefix; the user's message isn't broadcast and ACE replies via SystemChat. Drop the optimistic /say echo so the chat shows only the server's response (the SystemChat wiring from #1 surfaces it as [System] {help}). Tests: - 11 long-form-alias Theory cases on ChatInputParser. - 3 own-guid-substitution cases on ChatLog (own match, different guid, pre-login fallback). - Existing PrefixSubstring test refactored to "/genio" since the previous "/general" stub is now a real verb. Solution total: 1021 green (243 Core.Net + 125 UI + 653 Core), 0 warnings, 0 errors. +14 tests. Acceptance: at login, [System] Welcome to Asheron's Call appears. Single "You say, \"hi\"" per /say. /allegiance with no allegiance shows [Allegiance] You say, ... + [System] You are not in an allegiance!. /patron / /vassals / /monarch route correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3f7821c18d
commit
7726f62528
7 changed files with 177 additions and 20 deletions
|
|
@ -225,12 +225,41 @@ public sealed class ChatInputParserTests
|
|||
[Fact]
|
||||
public void PrefixSubstring_IsNotAVerbMatch()
|
||||
{
|
||||
// "/general" (no leading "/g " token) is NOT /g; it's just text.
|
||||
// Must not be misclassified as /g + "eneral".
|
||||
var parsed = ChatInputParser.Parse("/general public", ChatChannelKind.Say, lastTellSender: null);
|
||||
// Phase J added long-form aliases (/general, /allegiance,
|
||||
// /patron, etc.). The exact-token rule still applies — a
|
||||
// verb prefix that ISN'T one of the listed aliases falls
|
||||
// through to the default channel. "/genio" is not /g, /general,
|
||||
// or /gen — must stay as Say carrying the literal text.
|
||||
var parsed = ChatInputParser.Parse("/genio public", ChatChannelKind.Say, lastTellSender: null);
|
||||
|
||||
Assert.NotNull(parsed);
|
||||
Assert.Equal(ChatChannelKind.Say, parsed!.Value.Channel);
|
||||
Assert.Equal("/general public", parsed.Value.Text);
|
||||
Assert.Equal("/genio public", parsed.Value.Text);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/general what's the deal", ChatChannelKind.General, "what's the deal")]
|
||||
[InlineData("/allegiance recall", ChatChannelKind.Allegiance, "recall")]
|
||||
[InlineData("/patron need help", ChatChannelKind.Patron, "need help")]
|
||||
[InlineData("/vassals listen up", ChatChannelKind.Vassals, "listen up")]
|
||||
[InlineData("/monarch heads up", ChatChannelKind.Monarch, "heads up")]
|
||||
[InlineData("/covassals tax season", ChatChannelKind.CoVassals, "tax season")]
|
||||
[InlineData("/fellowship buff time", ChatChannelKind.Fellowship, "buff time")]
|
||||
[InlineData("/fellow buff time", ChatChannelKind.Fellowship, "buff time")]
|
||||
[InlineData("/lookingforgroup hunt invite", ChatChannelKind.Lfg, "hunt invite")]
|
||||
[InlineData("/roleplay walk-up", ChatChannelKind.Roleplay, "walk-up")]
|
||||
[InlineData("/rp walk-up", ChatChannelKind.Roleplay, "walk-up")]
|
||||
public void LongFormAliases_RouteToTheirChannel(string raw, ChatChannelKind expected, string text)
|
||||
{
|
||||
// Phase J: retail muscle memory uses long forms ("/patron"
|
||||
// not just "/p"). Filed after a 2026-04-25 live test where
|
||||
// "/patron hello" fell through as /say with the literal
|
||||
// slash-prefixed text.
|
||||
var parsed = ChatInputParser.Parse(raw, ChatChannelKind.Say, lastTellSender: null);
|
||||
|
||||
Assert.NotNull(parsed);
|
||||
Assert.Equal(expected, parsed!.Value.Channel);
|
||||
Assert.Null(parsed.Value.TargetName);
|
||||
Assert.Equal(text, parsed.Value.Text);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue