fix(client): restore retail interaction parity
All checks were successful
CI / linux-portable (push) Successful in 3m27s
CI / windows-gate (push) Successful in 6m42s
CI / release (push) Successful in 2m12s

Harden keyboard and camera routing, inventory and vendor interactions, chat/emotes, relog portal flow, and paperdoll rendering. Add retail research, connected gate coverage, and release-gate validation.
This commit is contained in:
Erik 2026-08-26 20:45:11 +02:00
parent 0c699240e0
commit f6fe0f2a4f
151 changed files with 10162 additions and 1211 deletions

View file

@ -147,9 +147,53 @@ public sealed class ItemInteractionPolicyTests
Assert.Equal(ItemPolicyActionKind.Reject,
Assert.Single(ItemInteractionPolicy.DecideUse(
Use(direct with { TradeState = 1 })).Actions).Kind);
Assert.Contains("wield", Assert.Single(ItemInteractionPolicy.DecideUse(
Use(direct with { Useability = ItemUseability.Wielded })).Actions).Message,
StringComparison.OrdinalIgnoreCase);
Assert.Equal("You cannot use the item because you are trading it",
Assert.Single(ItemInteractionPolicy.DecideUse(
Use(direct with { TradeState = 1 })).Actions).Message);
Assert.Equal("You must wield the item to use it",
Assert.Single(ItemInteractionPolicy.DecideUse(
Use(direct with { Useability = ItemUseability.Wielded })).Actions).Message);
}
[Fact]
public void UseObject_targetCompatibilityFailures_useRetailVerbatimMessages()
{
var source = OwnedDirect() with
{
Name = "mana stone",
Useability = 0x00080008u,
TargetType = (uint)ItemType.Misc,
};
var target = Obj(0x6002) with
{
Name = "armor",
Type = ItemType.Armor,
ContainerId = Player,
OwnedByPlayer = true,
};
var missing = ItemInteractionPolicy.DecideUse(Use(source) with
{
UseCurrentSelection = true,
});
Assert.Equal("Select your target before using the mana stone",
Assert.Single(missing.Actions).Message);
var incompatible = ItemInteractionPolicy.DecideUse(Use(source) with
{
UseCurrentSelection = true,
SelectedTarget = target,
});
Assert.Equal("Cannot use the mana stone with the armor",
Assert.Single(incompatible.Actions).Message);
var traded = ItemInteractionPolicy.DecideUse(Use(source) with
{
UseCurrentSelection = true,
SelectedTarget = target with { TradeState = 1 },
});
Assert.Equal("You can't use the mana stone on an item you are trading",
Assert.Single(traded.Actions).Message);
}
[Fact]
@ -219,7 +263,7 @@ public sealed class ItemInteractionPolicyTests
PlayerOnGround = false,
});
Assert.False(airborne.ReturnValue);
Assert.Equal(ItemPolicyActionKind.Reject, Assert.Single(airborne.Actions).Kind);
Assert.Equal("You cannot do that in mid air", Assert.Single(airborne.Actions).Message);
var split = ItemInteractionPolicy.DecidePlacement(PlaceOnGround(item) with { SplitSize = 4 });
Assert.True(split.ReturnValue);
@ -232,8 +276,7 @@ public sealed class ItemInteractionPolicyTests
var alreadyWorld = ItemInteractionPolicy.DecidePlacement(
PlaceOnGround(item with { IsIn3DView = true }));
Assert.False(alreadyWorld.ReturnValue);
Assert.Contains("cancelled", Assert.Single(alreadyWorld.Actions).Message,
StringComparison.OrdinalIgnoreCase);
Assert.Equal("Move cancelled", Assert.Single(alreadyWorld.Actions).Message);
}
[Fact]