auto-git:

[change] src/runtime-three/runtime-host.ts
 [change] tests/unit/runtime-host.test.ts
This commit is contained in:
2026-04-25 10:45:04 +02:00
parent 12df95f98a
commit 86e89b25ec
2 changed files with 80 additions and 7 deletions

View File

@@ -329,7 +329,9 @@ const DIALOGUE_PARTICIPANT_RESTORE_EPSILON_DEGREES = 0.5;
const TARGETING_LUX_FOLLOW_RATE = 8;
const TARGETING_LUX_BOB_RATE = 4.2;
const TARGETING_LUX_PULSE_RATE = 6.5;
const PROPOSED_TARGET_CAMERA_ASSIST_STRENGTH = 0.28;
// Proposed-target camera nudging is intentionally disabled for now. Lux alone
// should communicate proposal without moving the gameplay camera.
// const PROPOSED_TARGET_CAMERA_ASSIST_STRENGTH = 0.28;
const ACTIVE_TARGET_CAMERA_ASSIST_STRENGTH = 0.55;
export function resolveRuntimeTargetVisualPlacement(target: {
@@ -5576,12 +5578,14 @@ export class RuntimeHost {
};
}
if (this.proposedRuntimeTarget !== null) {
return {
targetPosition: this.proposedRuntimeTarget.center,
strength: PROPOSED_TARGET_CAMERA_ASSIST_STRENGTH
};
}
// Keep this branch commented instead of deleted; proposed-target nudging may
// come back later after the Lux readability pass has settled.
// if (this.proposedRuntimeTarget !== null) {
// return {
// targetPosition: this.proposedRuntimeTarget.center,
// strength: PROPOSED_TARGET_CAMERA_ASSIST_STRENGTH
// };
// }
return null;
}
@@ -5739,6 +5743,7 @@ export class RuntimeHost {
if (event.code === "Escape" && this.activeRuntimeTargetReference !== null) {
event.preventDefault();
event.stopImmediatePropagation();
this.clearActiveRuntimeTarget();
return;
}

View File

@@ -3129,6 +3129,74 @@ describe("RuntimeHost", () => {
expect(placement.activeMarkerScale).toBeGreaterThan(1);
});
it("uses Lux-only proposal feedback and consumes Escape when clearing an active target", () => {
const host = new RuntimeHost({
enableRendering: false
});
const hostInternals = host as unknown as {
runtimeScene: unknown;
sceneReady: boolean;
activeController: unknown;
thirdPersonController: unknown;
proposedRuntimeTarget: {
kind: "npc" | "interactable";
entityId: string;
center: { x: number; y: number; z: number };
} | null;
activeRuntimeTargetReference: {
kind: "npc" | "interactable";
entityId: string;
} | null;
resolveThirdPersonTargetAssist(): unknown;
handleRuntimeKeyDown(event: KeyboardEvent): void;
};
const escapeEvent = {
code: "Escape",
defaultPrevented: false,
repeat: false,
altKey: false,
ctrlKey: false,
metaKey: false,
target: null,
preventDefault: vi.fn(),
stopImmediatePropagation: vi.fn()
} as unknown as KeyboardEvent;
hostInternals.runtimeScene = {
playerInputBindings: {
keyboard: {
pauseTime: "KeyP"
}
},
entities: {
cameraRigs: [],
interactables: [],
npcs: []
},
interactionLinks: []
} as never;
hostInternals.sceneReady = true;
hostInternals.activeController = hostInternals.thirdPersonController;
hostInternals.proposedRuntimeTarget = {
kind: "npc",
entityId: "npc-proposed",
center: { x: 0, y: 1, z: 3 }
};
expect(hostInternals.resolveThirdPersonTargetAssist()).toBeNull();
hostInternals.activeRuntimeTargetReference = {
kind: "npc",
entityId: "npc-active"
};
hostInternals.handleRuntimeKeyDown(escapeEvent);
expect(hostInternals.activeRuntimeTargetReference).toBeNull();
expect(escapeEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(escapeEvent.stopImmediatePropagation).toHaveBeenCalledTimes(1);
host.dispose();
});
it("clears runtime targeting when switching into first-person mode", () => {
const host = new RuntimeHost({
enableRendering: false