fix(ui): retail target-cursor shape (pending over UI, world-object driven) + visible UseDone refusals
Visual gate round 2 (user, retail oracle): the target cursor over UI must be the 0x27 four-arrows PENDING cursor — retail's UpdateCursorState (0x00564630) keys valid/invalid solely off the SmartBox found object, i.e. the WORLD entity under the cursor. And "can't heal myself" turned out to be TWO stacked causes, both fixed: - Cursor: UI hover no longer forces TargetInvalid (that arm was a non-retail invention) and the doll/status-bar hover providers are gone (UiElement.UseTargetGuidProvider deleted). Valid/invalid now come from (a) a hovered occupied item slot's own item, or (b) the world entity under the cursor via a new worldTargetProvider — the B.4b screen-rect picker extracted into GameWindow.PickWorldGuidAtCursor and shared by click + hover. - Self-heal: the world picker always skipped the local player (skipServerGuid), so clicking your own toon in target mode could never acquire self. Target-use picks now include self (retail lets you kit-heal yourself by clicking your character); plain selection keeps the exclusion. The doll click still self-targets. - Silent refusals: the [use-target] log proved the UseWithTarget action WAS sent (kit 0x00220008 / TargetType 0x10, target self) — ACE refused with WeenieError.YouArentTrainedInHealing (0x04FC) and we never parsed UseDone (0x01C7). Now dispatched + surfaced as a chat line via WeenieErrorText (interim subset map, register AP-74, #166 ports the retail String-table lookup). Full suite green (3,295). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
047410ccc9
commit
769ebef30d
10 changed files with 151 additions and 61 deletions
|
|
@ -75,16 +75,19 @@ public sealed class CursorFeedbackControllerTests
|
|||
c.Resolve(new CursorFeedbackSnapshot(HoverUi: true, HoverTargetGuid: Player)).Kind);
|
||||
Assert.Equal(CursorFeedbackKind.TargetInvalid,
|
||||
c.Resolve(new CursorFeedbackSnapshot(HoverUi: true, HoverTargetGuid: 0x5000BADu)).Kind);
|
||||
Assert.Equal(CursorFeedbackKind.TargetInvalid,
|
||||
// Retail UpdateCursorState: no found object → PENDING, including over
|
||||
// UI chrome (the HoverUi→Invalid arm was a non-retail invention).
|
||||
Assert.Equal(CursorFeedbackKind.TargetPending,
|
||||
c.Resolve(new CursorFeedbackSnapshot(HoverUi: true)).Kind);
|
||||
Assert.Equal(CursorFeedbackKind.TargetPending,
|
||||
c.Resolve(new CursorFeedbackSnapshot()).Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateFromRoot_usesUseTargetGuidProviderBeforeSlotItem()
|
||||
public void UpdateFromRoot_slotItemDrivesTargetCursor_withStateArt()
|
||||
{
|
||||
var objects = SeedTargetObjects();
|
||||
objects.Get(Source)!.TargetType = (uint)ItemType.Misc; // a tool that targets items
|
||||
var interaction = new ItemInteractionController(
|
||||
objects,
|
||||
playerGuid: () => Player,
|
||||
|
|
@ -100,7 +103,6 @@ public sealed class CursorFeedbackControllerTests
|
|||
Top = 10,
|
||||
Width = 32,
|
||||
Height = 32,
|
||||
UseTargetGuidProvider = () => Player,
|
||||
};
|
||||
var acceptCursor = new UiCursorMedia(0x06009999u, 11, 12);
|
||||
slot.SetStateCursors(new Dictionary<string, UiCursorMedia>
|
||||
|
|
@ -119,6 +121,34 @@ public sealed class CursorFeedbackControllerTests
|
|||
Assert.Equal(acceptCursor, feedback.Cursor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UpdateFromRoot_worldProviderDrivesTargetCursor_whenUiNotHovered()
|
||||
{
|
||||
var objects = SeedTargetObjects();
|
||||
var interaction = new ItemInteractionController(
|
||||
objects,
|
||||
playerGuid: () => Player,
|
||||
sendUse: null,
|
||||
sendUseWithTarget: null,
|
||||
sendWield: null,
|
||||
sendDrop: null,
|
||||
nowMs: () => 1_000);
|
||||
var root = new UiRoot { Width = 800, Height = 600 }; // nothing under the mouse
|
||||
root.OnMouseMove(400, 300);
|
||||
var c = new CursorFeedbackController(interaction, worldTargetProvider: () => Player);
|
||||
|
||||
Assert.True(interaction.ActivateItem(Source));
|
||||
|
||||
// World hover (retail SmartBox found object) drives valid/invalid…
|
||||
Assert.Equal(CursorFeedbackKind.TargetValid, c.Update(root).Kind);
|
||||
|
||||
// …but UI occludes the world: hovering a plain panel → PENDING even
|
||||
// though the world provider would return a valid target.
|
||||
var panel = new UiPanel { Left = 390, Top = 290, Width = 40, Height = 40 };
|
||||
root.AddChild(panel);
|
||||
Assert.Equal(CursorFeedbackKind.TargetPending, c.Update(root).Kind);
|
||||
}
|
||||
|
||||
private static ClientObjectTable SeedTargetObjects()
|
||||
{
|
||||
var objects = new ClientObjectTable();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue