acdream/tests/AcDream.Core.Tests/Chat/InventoryFailureMessagesTests.cs
Erik 67fe754dd6 fix: social gate round 2, part 2 - confirmation-dialog sentences + the
refused-drop yellow notice

Item 4 (confirmation dialogs missing text + names): the missing retail
mechanism was StringTable template substitution - an entry is N+1 literal
fragments interleaved with N named variables, composed by
StringTable::GetString @0x004300D0 (no-metalanguage branch @0x004303B7).
ACE sends the bare player name for types 1/4; retail's OWN CLIENT wraps
it. Ported as DatStringResolver.ResolveTemplate (PLAYER hash 0x05506DA2,
the exact compute_str_hash space; Chorizite stores the variable hashes
directly):

- Server-driven type 4 -> ID_Fellowship_FellowshipRequest, type 1 ->
  ID_Allegiance_AcceptSwearConfirmation, injected into
  GameplayConfirmationController; null resolve falls back to the bare
  wire message, never invented English. The 2/3/5/6 " Continue?" family
  never consults the composer.
- Local Swear/Break/Kick: the bind-time fragment-0 latch (which showed
  the dangling "Do you wish to swear to ") is replaced by click-time
  ResolveTemplate with the target's name.

All five templates verified token-free in the installed DAT - this is
NOT a StringTableMetaLanguage port (AD-81's engine caveat stands).

Item 5 (refused drop shows nothing; retail shows yellow top-center
text): the prevRequest latch was ALREADY ported (InventoryTransactionState);
what was missing was the consumer. InventoryTransactionState now raises
RequestFailed(request, weenieError) when a 0x00A0 clears the latch;
ItemInteractionController composes ServerSaysAttemptFailed @0x0058EAE0's
"The <item> can't be <verb>" (verb table + suffix map ported verbatim in
Core's InventoryFailureMessages, NAME_PLURAL for merge/split) and routes
it as LogTextType 0x1A ClientLocal -> the SpewBox, retail's yellow
top-center line. The dispatcher's second leg (@0x0055B342) also runs:
outside the 7-code exclusion set, WeenieErrorMessages resolves per-code
text/destination; 0x426 AttunedItem has no row in either place beyond
the verb line - faithful single-line output.

Register: AD-85 narrowed to its numeric-field item, AD-81 amended (the
token-free interleave is now ported; meta-token engine + FormatName
remain), AD-93 filed (wire-guid-match vs retail's latched-guid
preference; no Move/Wield latch kinds).

Tests: +2 InventoryTransactionState failure-latch, +5 ResolveTemplate
(constructed StringTable fixtures), +1 composer injection, +1 end-to-end
refused-drop line. Core 4,697/1 skip, App 4,983/3 skips.

Research: docs/research/2026-08-13-confirm-and-weenie-error-display.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 21:10:42 +02:00

59 lines
2.1 KiB
C#

using AcDream.Core.Chat;
using AcDream.Core.Items;
namespace AcDream.Core.Tests.Chat;
/// <summary>
/// Conformance rows for the ServerSaysAttemptFailed @ 0x0058EAE0 port —
/// verb table, suffix map, and the 0x00A0 dispatcher exclusion set
/// (@ 0x0055B342).
/// </summary>
public sealed class InventoryFailureMessagesTests
{
[Theory]
[InlineData(
InventoryRequestKind.DropToWorld, "Bloodstone Chunk", 0x426u,
"The Bloodstone Chunk can't be dropped")]
[InlineData(
InventoryRequestKind.Give, "Sword", 0u,
"The Sword can't be given")]
[InlineData(
InventoryRequestKind.Pickup, "Sword", 0x2Au,
"The Sword can't be picked up - you are too encumbered")]
[InlineData(
InventoryRequestKind.PutInContainer, "Sword", 0x3EEu,
"The Sword can't be put in the container - the container is closed")]
[InlineData(
InventoryRequestKind.Merge, "Arrows", 0x1Du,
"The Arrows can't be merged - you're too busy")]
[InlineData(
InventoryRequestKind.SplitToWorld, "Arrows", 0x38u,
"The Arrows can't be split - unable to move to object")]
[InlineData(
InventoryRequestKind.SplitToContainer, "Arrows", 0x36u,
"The Arrows can't be split - action cancelled")]
public void ComposeMatchesServerSaysAttemptFailed(
InventoryRequestKind kind,
string name,
uint error,
string expected)
=> Assert.Equal(expected, InventoryFailureMessages.Compose(kind, name, error));
[Theory]
[InlineData(0x1Eu)]
[InlineData(0x2Bu)]
[InlineData(0x3EFu)]
[InlineData(0x43Eu)]
[InlineData(0x4CEu)]
[InlineData(0x4CFu)]
[InlineData(0x46Au)]
public void ExclusionSetSuppressesGenericFailureText(uint error)
=> Assert.True(InventoryFailureMessages.SuppressesGenericFailureText(error));
[Theory]
[InlineData(0x426u)]
[InlineData(0x1Du)]
[InlineData(0u)]
public void OtherErrorsDoNotSuppressGenericFailureText(uint error)
=> Assert.False(InventoryFailureMessages.SuppressesGenericFailureText(error));
}