feat(B.4b): GameWindow wires Select/Use handlers via WorldPicker
Closes #57. Adds three OnInputAction switch cases (SelectLeft, SelectDblLeft, UseSelected) and three private helpers (PickAndStoreSelection, UseCurrentSelection, SendUse). Single-click selects but does not Use; double-click selects + Uses; R hotkey sends Use on the existing _selectedGuid. ImGui mouse-capture filtering already happens in InputDispatcher — no new guard needed. Diagnostic lines emitted for log grep: [B.4b] pick guid=0x{guid:X8} name={label} [B.4b] use guid=0x{guid:X8} seq={seq} Also adds a one-line doc comment on _selectedGuid clarifying its dual-purpose role (combat Q-cycle + interaction click), per the Task 3 review. Build green; tests 1046/1054 (8 pre-existing-baseline fails unchanged). Switch-case behavior verified at runtime via the Holtburg inn doorway visual test (per spec §Testing → Runtime verification). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7b4aff21b6
commit
89d82e1b76
1 changed files with 74 additions and 0 deletions
|
|
@ -783,6 +783,7 @@ public sealed class GameWindow : IDisposable
|
|||
/// fields when a 0xF625 ObjDescEvent arrives carrying only updated visuals.
|
||||
/// </summary>
|
||||
private readonly Dictionary<uint, AcDream.Core.Net.WorldSession.EntitySpawn> _lastSpawnByGuid = new();
|
||||
// Current selection: written by Q-cycle (combat) and LMB click (interact); cleared on entity despawn.
|
||||
private uint? _selectedGuid;
|
||||
private readonly record struct LiveEntityInfo(
|
||||
string? Name,
|
||||
|
|
@ -8629,6 +8630,18 @@ public sealed class GameWindow : IDisposable
|
|||
SendLiveCombatAttack(AcDream.Core.Combat.CombatAttackAction.High);
|
||||
break;
|
||||
|
||||
case AcDream.UI.Abstractions.Input.InputAction.SelectLeft:
|
||||
PickAndStoreSelection(useImmediately: false);
|
||||
break;
|
||||
|
||||
case AcDream.UI.Abstractions.Input.InputAction.SelectDblLeft:
|
||||
PickAndStoreSelection(useImmediately: true);
|
||||
break;
|
||||
|
||||
case AcDream.UI.Abstractions.Input.InputAction.UseSelected:
|
||||
UseCurrentSelection();
|
||||
break;
|
||||
|
||||
case AcDream.UI.Abstractions.Input.InputAction.EscapeKey:
|
||||
if (_cameraController?.IsFlyMode == true)
|
||||
_cameraController.ToggleFly(); // exit fly, release cursor
|
||||
|
|
@ -8703,6 +8716,67 @@ public sealed class GameWindow : IDisposable
|
|||
return SelectClosestCombatTarget(showToast: false);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Phase B.4b — outbound Use handler. Wires three input actions
|
||||
// (LMB click select, LMB-double-click select+use, R hotkey
|
||||
// use-selected) through WorldPicker into InteractRequests.BuildUse.
|
||||
// The inbound reply (SetState 0xF74B) lands via L.2g slice 1.
|
||||
// ============================================================
|
||||
|
||||
private void PickAndStoreSelection(bool useImmediately)
|
||||
{
|
||||
if (_cameraController is null || _window is null) return;
|
||||
|
||||
var camera = _cameraController.Active;
|
||||
var (origin, direction) = AcDream.Core.Selection.WorldPicker.BuildRay(
|
||||
mouseX: _lastMouseX, mouseY: _lastMouseY,
|
||||
viewportW: _window.Size.X, viewportH: _window.Size.Y,
|
||||
view: camera.View, projection: camera.Projection);
|
||||
|
||||
if (direction.LengthSquared() < 1e-6f) return; // degenerate ray
|
||||
|
||||
var picked = AcDream.Core.Selection.WorldPicker.Pick(
|
||||
origin, direction,
|
||||
_entitiesByServerGuid.Values,
|
||||
skipServerGuid: _playerServerGuid,
|
||||
maxDistance: 50f);
|
||||
|
||||
if (picked is uint guid)
|
||||
{
|
||||
_selectedGuid = guid;
|
||||
string label = DescribeLiveEntity(guid);
|
||||
Console.WriteLine($"[B.4b] pick guid=0x{guid:X8} name={label}");
|
||||
_debugVm?.AddToast($"Selected: {label}");
|
||||
if (useImmediately) SendUse(guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_debugVm?.AddToast("Nothing to select");
|
||||
}
|
||||
}
|
||||
|
||||
private void UseCurrentSelection()
|
||||
{
|
||||
if (_selectedGuid is uint sel)
|
||||
SendUse(sel);
|
||||
else
|
||||
_debugVm?.AddToast("Nothing selected");
|
||||
}
|
||||
|
||||
private void SendUse(uint guid)
|
||||
{
|
||||
if (_liveSession is null
|
||||
|| _liveSession.CurrentState != AcDream.Core.Net.WorldSession.State.InWorld)
|
||||
{
|
||||
_debugVm?.AddToast("Not in world");
|
||||
return;
|
||||
}
|
||||
var seq = _liveSession.NextGameActionSequence();
|
||||
var body = AcDream.Core.Net.Messages.InteractRequests.BuildUse(seq, guid);
|
||||
_liveSession.SendGameAction(body);
|
||||
Console.WriteLine($"[B.4b] use guid=0x{guid:X8} seq={seq}");
|
||||
}
|
||||
|
||||
private uint? SelectClosestCombatTarget(bool showToast)
|
||||
{
|
||||
if (!_entitiesByServerGuid.TryGetValue(_playerServerGuid, out var playerEntity))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue