Release Healer Man 0.1.29

This commit is contained in:
phenom committed 2026-09-11 14:26:15 -04:00
1 parent 8c9809d3e0
commit 544c1bee91
120 files changed
+4363 -1036

No files matched your search

+110
View File
@@ -4,6 +4,8 @@ import {
emitControllerToken,
orderedControllerTokenTransitions,
resetControllerState,
setExternalControllerMovement,
interceptControllerInput,
} from "../input/controller";
import {
MAX_MOUSE_DELTA_PER_EVENT,
@@ -23,8 +25,11 @@ import {
keyboardCommandForCode,
queueJumpRequest,
sanitizeMouseDelta,
readInputSnapshot,
type InputActions,
} from "./inputManager";
import { normalizeInputPreferences, replaceInputBinding } from "../input/keybinds";
import { setCapturingInput } from "../input/keybindRuntime";
function stubInputEventTargets(): void {
const windowTarget = new EventTarget();
@@ -59,12 +64,117 @@ function inputActionSpies(blocked = false): InputActions {
}
afterEach(() => {
setCapturingInput(false);
endMouseLookDrag();
clearJumpRequest();
resetControllerState();
vi.unstubAllGlobals();
});
function keyboardEvent(type: "keydown" | "keyup", code: string, modifiers: Partial<KeyboardEvent> = {}) {
const event = new Event(type, { cancelable: true });
Object.defineProperties(event, Object.fromEntries(Object.entries({ code, target: null, ...modifiers }).map(([key, value]) => [key, { value }])));
window.dispatchEvent(event);
return event;
}
describe("remappable gameplay controls", () => {
it("keeps moving forward when a legacy WoW skill also owns W", () => {
stubInputEventTargets();
const next = inputActionSpies(); const preferences = normalizeInputPreferences(null);
preferences.pc["bar:0:0"] = "KeyW";
next.getBindings = (device) => preferences[device];
next.desktopActionBarsActive = () => true;
next.castDesktopSlot = vi.fn();
const dispose = installInput(next);
keyboardEvent("keydown", "KeyW");
expect(readInputSnapshot().moveForward).toBe(1);
expect(next.castDesktopSlot).not.toHaveBeenCalled();
keyboardEvent("keyup", "KeyW");
expect(readInputSnapshot().moveForward).toBe(0);
dispose();
});
it("honors remapped movement and clears held input when a menu opens", () => {
stubInputEventTargets();
const next = inputActionSpies(); const preferences = normalizeInputPreferences(null);
preferences.pc = replaceInputBinding(preferences.pc, "pc", "move-forward", "ArrowUp");
next.getBindings = (device) => preferences[device];
const dispose = installInput(next);
keyboardEvent("keydown", "KeyW"); expect(readInputSnapshot().moveForward).toBe(0);
keyboardEvent("keydown", "ArrowUp");
expect(readInputSnapshot()).toMatchObject({ moveForward: 1, lookY: 0 });
(next.gameplayActionsBlocked as ReturnType<typeof vi.fn>).mockReturnValue(true);
expect(readInputSnapshot().moveForward).toBe(0);
(next.gameplayActionsBlocked as ReturnType<typeof vi.fn>).mockReturnValue(false);
expect(readInputSnapshot().moveForward).toBe(0);
dispose();
});
it("casts remapped bar chords once and gives ground confirmation its context", () => {
stubInputEventTargets();
const next = inputActionSpies(); const preferences = normalizeInputPreferences(null);
preferences.pc["bar:4:11"] = "Ctrl+Shift+KeyQ";
preferences.pc["ground-confirm"] = "Ctrl+Shift+KeyQ";
next.getBindings = (device) => preferences[device]; next.desktopActionBarsActive = () => true; next.castDesktopSlot = vi.fn();
const dispose = installInput(next);
keyboardEvent("keydown", "KeyQ", { ctrlKey: true, shiftKey: true });
keyboardEvent("keydown", "KeyQ", { ctrlKey: true, shiftKey: true, repeat: true });
expect(next.castDesktopSlot).toHaveBeenCalledExactlyOnceWith(4, 11);
(next.groundTargetActive as ReturnType<typeof vi.fn>).mockReturnValue(true);
keyboardEvent("keydown", "KeyQ", { ctrlKey: true, shiftKey: true });
expect(next.confirmGroundTarget).toHaveBeenCalledTimes(1);
expect(next.castDesktopSlot).toHaveBeenCalledTimes(1);
dispose();
});
it("remaps controller axes and buttons with analog movement preserved", () => {
stubInputEventTargets();
const next = inputActionSpies(); const preferences = normalizeInputPreferences(null);
preferences.controller = replaceInputBinding(preferences.controller, "controller", "move-forward", "Axis3-");
preferences.controller = replaceInputBinding(preferences.controller, "controller", "move-right", "Button3");
next.getBindings = (device) => preferences[device];
const dispose = installInput(next);
setExternalControllerMovement({ moveX: 0, moveY: -1, lookX: 0, lookY: 0 });
expect(readInputSnapshot().moveForward).toBe(0);
setExternalControllerMovement({ moveX: 0, moveY: 0, lookX: 0, lookY: -.7 });
expect(readInputSnapshot().moveForward).toBeGreaterThan(.3);
expect(readInputSnapshot().moveForward).toBeLessThan(1);
expect(readInputSnapshot().lookY).toBe(0);
setExternalControllerMovement({ moveX: 0, moveY: 0, lookX: 0, lookY: 0 });
emitControllerToken({ token: "Button3", repeat: false, pressed: true });
expect(readInputSnapshot().moveX).toBe(1);
expect(next.castActionBinding).not.toHaveBeenCalled();
emitControllerToken({ token: "Button3", repeat: false, pressed: false });
expect(readInputSnapshot().moveX).toBe(0);
dispose();
});
it("uses customized controller combinations before their base button", () => {
stubInputEventTargets();
const next = inputActionSpies(); const preferences = normalizeInputPreferences(null);
preferences.controller = replaceInputBinding(preferences.controller, "controller", "jump", "Button8&Button0");
next.getBindings = (device) => preferences[device];
const dispose = installInput(next);
emitControllerToken({ token: "Button8", repeat: false, pressed: true });
emitControllerToken({ token: "Button0", repeat: false, pressed: true });
expect(consumeJumpRequest()).not.toBeNull();
expect(next.castActionBinding).not.toHaveBeenCalled();
expect(next.lootActiveBoss).not.toHaveBeenCalled();
dispose();
});
it("blocks keyboard actions, controller actions and movement throughout capture", () => {
stubInputEventTargets();
const next = inputActionSpies(); const dispose = installInput(next);
setCapturingInput(true);
const captured = vi.fn(() => true); const unsubscribe = interceptControllerInput(captured);
keyboardEvent("keydown", "KeyW"); keyboardEvent("keydown", "KeyI"); keyboardEvent("keydown", "Digit1");
emitControllerToken({ token: "Button0", repeat: false, pressed: true });
emitControllerToken({ token: "Button9", repeat: false, pressed: true });
setExternalControllerMovement({ moveX: 1, moveY: -1, lookX: 0, lookY: 0 });
expect(readInputSnapshot()).toMatchObject({ moveX: 0, moveForward: 0 });
expect(next.castActionBinding).not.toHaveBeenCalled(); expect(next.toggleInventory).not.toHaveBeenCalled(); expect(next.togglePause).not.toHaveBeenCalled();
expect(captured).toHaveBeenCalledTimes(2);
unsubscribe(); dispose();
});
});
describe("gameplay input bindings", () => {
it("consumes custom action keys before movement, menus and legacy action bindings", () => {
stubInputEventTargets();