feat(input): #21 Phase K.1a - input architecture skeleton (parallel to existing handlers)
Introduces the abstraction without changing user-visible behavior.
Existing keyboard/mouse handlers in GameWindow continue working
unchanged. The new InputDispatcher runs alongside, fires
InputAction events, and a diagnostic Console.WriteLine subscriber
proves the path is observable. K.1b cuts the existing handlers
over; K.1c flips bindings to retail.
New types in src/AcDream.UI.Abstractions/Input/:
- InputAction enum (~110 actions, doc-grouped by retail keymap
category: MovementCommands, ItemSelectionCommands, UICommands,
QuickslotCommands, Chat, Combat, Emotes, Camera, Scroll, Mouse
selection, plus Acdream-specific debug actions for the existing
F-key behaviors)
- KeyChord record struct (Silk.NET.Input.Key + ModifierMask + Device)
- ModifierMask [Flags] enum matching retail keymap bit values
(Shift=0x01, Ctrl=0x02, Alt=0x04, Win=0x08)
- ActivationType enum (Press, Release, Hold, DoubleClick, Analog)
- Binding record (chord -> action -> activation)
- InputScope enum with stack semantics (Always at bottom, Game on
top during normal play; Chat / EditField / Dialog / MeleeCombat /
MissileCombat / MagicCombat / Camera push as transient overlays)
- KeyBindings collection class with Find / ForAction / Add / Remove.
AcdreamCurrentDefaults() factory matches today's hardcoded binds
(W/S/A/D/Z/X movement, Shift run, F-key debug surface) so K.1a
doesn't change behavior. RetailDefaults() is K.1c's job; for now
it returns the same map.
- IKeyboardSource / IMouseSource - test-fakeable interfaces wrapping
Silk.NET. Both surface WantCaptureMouse / WantCaptureKeyboard
flags so the dispatcher can gate per ImGui state.
- InputDispatcher: multicast event Fired<InputAction, ActivationType>;
scope stack with PushScope/PopScope/ActiveScope; per-frame Tick()
fires Hold-type bindings for currently-held chords; mouse buttons
encoded as KeyChord with Device=1.
New adapters in src/AcDream.App/Input/:
- SilkKeyboardSource - Silk.NET IKeyboard wrapper, tracks held state
- SilkMouseSource - Silk.NET IMouse wrapper, proxies ImGui WantCapture
flags for both keyboard and mouse
GameWindow.cs:
- Constructs adapters + dispatcher in OnLoad
- Subscribes to dispatcher.Fired with diagnostic Console.WriteLine
("[input] {action} {activation}") so the path is observable in
launch.log without touching any actual game state
- Calls _inputDispatcher.Tick() per frame in OnUpdate
- Existing IsKeyPressed and event handlers unchanged
Memory crib at memory/project_input_pipeline.md describes the five
layers (Silk events -> Source interfaces -> Dispatcher -> Action
events -> Subscribers) with file paths + scope semantics + the K.1c
retail-defaults plan. Indexed in MEMORY.md.
Two deviations from plan, both documented:
1. InputDispatcher placed in UI.Abstractions/Input/ rather than
App/Input/ - it has no Silk dependencies (uses only the test-
fakeable interfaces) and the test fakes live in
UI.Abstractions.Tests. Mirrors LiveCommandBus precedent. Silk
adapters + GameWindow wiring stay in App.
2. WantCaptureKeyboard moved to IMouseSource alongside WantCaptureMouse
(the dispatcher needs both at the same point).
34 new tests covering KeyChord equality, ModifierMask flags,
KeyBindings lookup, dispatcher chord matching with modifier
mismatch rejection, Hold-type Press/Release transitions, Tick()
firing held bindings, scope stack push/pop with mismatched-pop
throwing, WantCapture* gating.
Solution total: 1118 green (243 Core.Net + 215 UI + 660 Core),
0 warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4717a5b6f7
commit
84512d3c64
20 changed files with 1426 additions and 0 deletions
259
src/AcDream.UI.Abstractions/Input/InputAction.cs
Normal file
259
src/AcDream.UI.Abstractions/Input/InputAction.cs
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
namespace AcDream.UI.Abstractions.Input;
|
||||
|
||||
/// <summary>
|
||||
/// High-level input actions. Each name matches an entry in the retail
|
||||
/// <c>acclient.keymap</c> "Bindings" header (see
|
||||
/// <c>docs/research/named-retail/retail-default.keymap.txt</c>). The
|
||||
/// <c>Acdream*</c> entries at the bottom are extensions for current
|
||||
/// debug bindings that have no retail equivalent.
|
||||
///
|
||||
/// <para>
|
||||
/// K.1a defines the enum but only the existing acdream-current chords
|
||||
/// resolve to actions; K.1c flips the bindings table to the full retail
|
||||
/// preset. Several actions in this enum (<c>UseQuickSlot_*</c>,
|
||||
/// <c>Combat*</c>, <c>UseSpellSlot_*</c>) have NO subscribers in K.1 —
|
||||
/// the chord fires the event but nothing acts on it. Phase L panels
|
||||
/// will subscribe.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public enum InputAction
|
||||
{
|
||||
/// <summary>Sentinel — no action. Reserved for "unbound".</summary>
|
||||
None = 0,
|
||||
|
||||
// ── MovementCommands ──────────────────────────────────
|
||||
/// <summary>Move forward (run by default; walk if <see cref="MovementWalkMode"/> active).</summary>
|
||||
MovementForward,
|
||||
/// <summary>Move backward.</summary>
|
||||
MovementBackup,
|
||||
/// <summary>Turn the character to the left.</summary>
|
||||
MovementTurnLeft,
|
||||
/// <summary>Turn the character to the right.</summary>
|
||||
MovementTurnRight,
|
||||
/// <summary>Strafe (sidestep) to the left.</summary>
|
||||
MovementStrafeLeft,
|
||||
/// <summary>Strafe (sidestep) to the right.</summary>
|
||||
MovementStrafeRight,
|
||||
/// <summary>Hold-modifier — toggles forward motion to walk while held (retail Shift).</summary>
|
||||
MovementWalkMode,
|
||||
/// <summary>Toggle autorun on/off (retail Q).</summary>
|
||||
MovementRunLock,
|
||||
/// <summary>Charged jump — hold to power, release to launch.</summary>
|
||||
MovementJump,
|
||||
/// <summary>Cancel current motion / return to ready stance.</summary>
|
||||
MovementStop,
|
||||
/// <summary>Ready posture / sheathe-unsheathe (retail Y).</summary>
|
||||
Ready,
|
||||
/// <summary>Sit posture (retail G).</summary>
|
||||
Sitting,
|
||||
/// <summary>Crouch posture (retail H).</summary>
|
||||
Crouch,
|
||||
/// <summary>Sleep posture (retail B).</summary>
|
||||
Sleeping,
|
||||
|
||||
// ── ItemSelectionCommands ─────────────────────────────
|
||||
/// <summary>Pick up the selected item.</summary>
|
||||
SelectionPickUp,
|
||||
/// <summary>Split a stack of the selected item.</summary>
|
||||
SelectionSplitStack,
|
||||
/// <summary>Cycle back to the previous selection.</summary>
|
||||
SelectionPreviousSelection,
|
||||
/// <summary>Closest compass-tracked item.</summary>
|
||||
SelectionClosestCompassItem,
|
||||
/// <summary>Previous compass-tracked item.</summary>
|
||||
SelectionPreviousCompassItem,
|
||||
/// <summary>Next compass-tracked item.</summary>
|
||||
SelectionNextCompassItem,
|
||||
/// <summary>Closest item in radius.</summary>
|
||||
SelectionClosestItem,
|
||||
/// <summary>Previous item in radius.</summary>
|
||||
SelectionPreviousItem,
|
||||
/// <summary>Next item in radius.</summary>
|
||||
SelectionNextItem,
|
||||
/// <summary>Closest monster.</summary>
|
||||
SelectionClosestMonster,
|
||||
/// <summary>Previous monster.</summary>
|
||||
SelectionPreviousMonster,
|
||||
/// <summary>Next monster.</summary>
|
||||
SelectionNextMonster,
|
||||
/// <summary>Most-recent attacker (for retaliation).</summary>
|
||||
SelectionLastAttacker,
|
||||
/// <summary>Closest player.</summary>
|
||||
SelectionClosestPlayer,
|
||||
/// <summary>Previous player.</summary>
|
||||
SelectionPreviousPlayer,
|
||||
/// <summary>Next player.</summary>
|
||||
SelectionNextPlayer,
|
||||
/// <summary>Previous fellow.</summary>
|
||||
SelectionPreviousFellow,
|
||||
/// <summary>Next fellow.</summary>
|
||||
SelectionNextFellow,
|
||||
/// <summary>Examine (Appraise) the current selection.</summary>
|
||||
SelectionExamine,
|
||||
|
||||
// ── UICommands ────────────────────────────────────────
|
||||
/// <summary>Use the selected item / interact (retail R).</summary>
|
||||
UseSelected,
|
||||
/// <summary>Cancel the topmost UI / clear selection / open log-out menu.</summary>
|
||||
EscapeKey,
|
||||
/// <summary>Log out of the game (retail Shift+Esc).</summary>
|
||||
LOGOUT,
|
||||
/// <summary>Toggle the help / control reference panel (retail F1).</summary>
|
||||
ToggleHelp,
|
||||
/// <summary>Toggle the plugin manager panel (retail F1+Shift+Ctrl).</summary>
|
||||
TogglePluginManager,
|
||||
/// <summary>Toggle the allegiance panel (retail F3).</summary>
|
||||
ToggleAllegiancePanel,
|
||||
/// <summary>Toggle the fellowship panel (retail F4).</summary>
|
||||
ToggleFellowshipPanel,
|
||||
/// <summary>Toggle the spellbook panel (retail F5).</summary>
|
||||
ToggleSpellbookPanel,
|
||||
/// <summary>Toggle the spell-components panel (retail F6).</summary>
|
||||
ToggleSpellComponentsPanel,
|
||||
/// <summary>Toggle the attributes panel (retail F8).</summary>
|
||||
ToggleAttributesPanel,
|
||||
/// <summary>Toggle the skills panel (retail F9).</summary>
|
||||
ToggleSkillsPanel,
|
||||
/// <summary>Toggle the world / map panel (retail F10).</summary>
|
||||
ToggleWorldPanel,
|
||||
/// <summary>Toggle the options / settings panel (retail F11) — opens
|
||||
/// our SettingsPanel in K.3.</summary>
|
||||
ToggleOptionsPanel,
|
||||
/// <summary>Toggle the inventory panel (retail F12).</summary>
|
||||
ToggleInventoryPanel,
|
||||
/// <summary>Toggle floating chat window 1 (retail Alt+1).</summary>
|
||||
ToggleFloatingChatWindow1,
|
||||
/// <summary>Toggle floating chat window 2.</summary>
|
||||
ToggleFloatingChatWindow2,
|
||||
/// <summary>Toggle floating chat window 3.</summary>
|
||||
ToggleFloatingChatWindow3,
|
||||
/// <summary>Toggle floating chat window 4.</summary>
|
||||
ToggleFloatingChatWindow4,
|
||||
/// <summary>Capture a screenshot (retail PrintScreen).</summary>
|
||||
CaptureScreenshot,
|
||||
|
||||
// ── QuickslotCommands ─────────────────────────────────
|
||||
UseQuickSlot_1,
|
||||
UseQuickSlot_2,
|
||||
UseQuickSlot_3,
|
||||
UseQuickSlot_4,
|
||||
UseQuickSlot_5,
|
||||
UseQuickSlot_6,
|
||||
UseQuickSlot_7,
|
||||
UseQuickSlot_8,
|
||||
UseQuickSlot_9,
|
||||
UseQuickSlot_14,
|
||||
UseQuickSlot_15,
|
||||
UseQuickSlot_16,
|
||||
UseQuickSlot_17,
|
||||
UseQuickSlot_18,
|
||||
/// <summary>Drop-target shortcut creation (retail 0 / drag-drop).</summary>
|
||||
CreateShortcut,
|
||||
|
||||
// ── Chat ──────────────────────────────────────────────
|
||||
/// <summary>Focus the chat input field (retail Tab).</summary>
|
||||
ToggleChatEntry,
|
||||
/// <summary>Send the current chat-input contents (retail Return).</summary>
|
||||
EnterChatMode,
|
||||
|
||||
// ── Combat ────────────────────────────────────────────
|
||||
/// <summary>Toggle combat-stance on/off (retail Grave / `).</summary>
|
||||
CombatToggleCombat,
|
||||
// Mode-dependent (dormant in K — Phase L lights them up)
|
||||
CombatDecreaseAttackPower,
|
||||
CombatIncreaseAttackPower,
|
||||
CombatLowAttack,
|
||||
CombatMediumAttack,
|
||||
CombatHighAttack,
|
||||
CombatDecreaseMissileAccuracy,
|
||||
CombatIncreaseMissileAccuracy,
|
||||
CombatAimLow,
|
||||
CombatAimMedium,
|
||||
CombatAimHigh,
|
||||
CombatPrevSpellTab,
|
||||
CombatNextSpellTab,
|
||||
CombatPrevSpell,
|
||||
CombatCastCurrentSpell,
|
||||
CombatNextSpell,
|
||||
CombatFirstSpellTab,
|
||||
CombatLastSpellTab,
|
||||
CombatFirstSpell,
|
||||
CombatLastSpell,
|
||||
UseSpellSlot_1,
|
||||
UseSpellSlot_2,
|
||||
UseSpellSlot_3,
|
||||
UseSpellSlot_4,
|
||||
UseSpellSlot_5,
|
||||
UseSpellSlot_6,
|
||||
UseSpellSlot_7,
|
||||
UseSpellSlot_8,
|
||||
UseSpellSlot_9,
|
||||
|
||||
// ── Emotes ────────────────────────────────────────────
|
||||
/// <summary>Cry emote (retail U).</summary>
|
||||
Cry,
|
||||
/// <summary>Laugh emote (retail I).</summary>
|
||||
Laugh,
|
||||
/// <summary>Cheer emote — celebratory jump (retail O).</summary>
|
||||
Cheer,
|
||||
/// <summary>Wave emote (retail J).</summary>
|
||||
Wave,
|
||||
/// <summary>Point emote (retail K).</summary>
|
||||
PointState,
|
||||
|
||||
// ── Camera ────────────────────────────────────────────
|
||||
/// <summary>Toggle alternate camera mode (retail F2 / Numpad-/).</summary>
|
||||
CameraActivateAlternateMode,
|
||||
/// <summary>Hold-MMB instant mouse-look — hardcoded behavior in K.2.</summary>
|
||||
CameraInstantMouseLook,
|
||||
CameraRotateLeft,
|
||||
CameraRotateRight,
|
||||
CameraRotateUp,
|
||||
CameraRotateDown,
|
||||
CameraMoveToward,
|
||||
CameraMoveAway,
|
||||
CameraViewDefault,
|
||||
CameraViewFirstPerson,
|
||||
CameraViewLookDown,
|
||||
CameraViewMapMode,
|
||||
|
||||
// ── Scroll ────────────────────────────────────────────
|
||||
/// <summary>Scroll up — wheel up or Ctrl+Up.</summary>
|
||||
ScrollUp,
|
||||
/// <summary>Scroll down — wheel down or Ctrl+Down.</summary>
|
||||
ScrollDown,
|
||||
|
||||
// ── Mouse selection ───────────────────────────────────
|
||||
/// <summary>Single left-click select.</summary>
|
||||
SelectLeft,
|
||||
/// <summary>Single right-click select.</summary>
|
||||
SelectRight,
|
||||
/// <summary>Single middle-click select.</summary>
|
||||
SelectMid,
|
||||
/// <summary>Double left-click select.</summary>
|
||||
SelectDblLeft,
|
||||
/// <summary>Double right-click select.</summary>
|
||||
SelectDblRight,
|
||||
/// <summary>Double middle-click select.</summary>
|
||||
SelectDblMid,
|
||||
|
||||
// ── Acdream debug actions (existing F-key behavior) ──
|
||||
/// <summary>F1 toggles entire DebugPanel visibility (acdream-only).</summary>
|
||||
AcdreamToggleDebugPanel,
|
||||
/// <summary>F2 toggles collision wireframes.</summary>
|
||||
AcdreamToggleCollisionWires,
|
||||
/// <summary>F3 dumps player + nearby entities to console.</summary>
|
||||
AcdreamDumpNearby,
|
||||
/// <summary>F7 cycles time of day.</summary>
|
||||
AcdreamCycleTimeOfDay,
|
||||
/// <summary>F8 decreases mouse sensitivity.</summary>
|
||||
AcdreamSensitivityDown,
|
||||
/// <summary>F9 increases mouse sensitivity.</summary>
|
||||
AcdreamSensitivityUp,
|
||||
/// <summary>F10 cycles weather.</summary>
|
||||
AcdreamCycleWeather,
|
||||
/// <summary>F (existing) toggles between fly camera and orbit/chase mode.</summary>
|
||||
AcdreamToggleFlyMode,
|
||||
/// <summary>Tab — currently toggles fly↔player mode (will be reassigned to ToggleChatEntry in K.1c).</summary>
|
||||
AcdreamTogglePlayerMode,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue