auto-git:

[change] src/app/App.tsx
 [change] src/runtime-three/runtime-host.ts
This commit is contained in:
2026-04-27 15:41:57 +02:00
parent b617056593
commit 5548eaa17f
2 changed files with 76 additions and 32 deletions

View File

@@ -239,7 +239,6 @@ import {
areProjectTimeSettingsEqual,
cloneProjectTimeSettings,
formatTimeOfDayHours,
HOURS_PER_DAY,
normalizeTimeOfDayHours,
type ProjectTimeSettings
} from "../document/project-time-settings";
@@ -431,10 +430,8 @@ import {
} from "../runtime-three/runtime-global-state";
import type { RuntimeSceneTransitionRequest } from "../runtime-three/runtime-host";
import {
advanceRuntimeClockState,
areRuntimeClockStatesEqual,
createRuntimeClockState,
reconfigureRuntimeClockState,
resolveRuntimeTimeState,
type RuntimeClockState
} from "../runtime-three/runtime-project-time";
@@ -443,7 +440,10 @@ import {
type RuntimeNavigationMode,
type RuntimeSceneDefinition
} from "../runtime-three/runtime-scene-build";
import { applyResolvedControlStateToRuntimeScene } from "../runtime-three/runtime-scene-editor-simulation";
import {
EditorSimulationController,
type EditorSimulationUiSnapshot
} from "../runtime-three/editor-simulation-controller";
import { validateRuntimeSceneBuild } from "../runtime-three/runtime-scene-validation";
import { EditorAutosaveController } from "../serialization/editor-autosave";
import { Panel } from "../shared-ui/Panel";
@@ -873,28 +873,26 @@ function createVec3Draft(vector: Vec3): Vec3Draft {
};
}
function offsetRuntimeClockState(
state: RuntimeClockState,
deltaHours: number
): RuntimeClockState {
const totalHours = Math.max(
0,
state.dayCount * HOURS_PER_DAY + state.timeOfDayHours + deltaHours
);
return {
timeOfDayHours: normalizeTimeOfDayHours(totalHours),
dayCount: Math.floor(totalHours / HOURS_PER_DAY),
dayLengthMinutes: state.dayLengthMinutes
};
}
function formatRuntimeDayPhaseLabel(
dayPhase: ReturnType<typeof resolveRuntimeTimeState>["dayPhase"]
): string {
return dayPhase.charAt(0).toUpperCase() + dayPhase.slice(1);
}
function createInitialEditorSimulationUiSnapshot(
time: ProjectTimeSettings
): EditorSimulationUiSnapshot {
return {
playing: false,
overrideActive: false,
clock: createRuntimeClockState(time),
message: null,
sceneReady: false,
sceneVersion: 0,
frameVersion: 0
};
}
function createPlayerStartMovementTemplateNumberDraft(
template: PlayerStartMovementTemplate
): PlayerStartMovementTemplateNumberDraft {
@@ -3121,14 +3119,10 @@ export function App({ store, initialStatusMessage }: AppProps) {
createDefaultRuntimeGlobalState(editorState.projectDocument.time)
);
const [runtimeMessage, setRuntimeMessage] = useState<string | null>(null);
const [editorSimulationClockOverride, setEditorSimulationClockOverride] =
useState<RuntimeClockState | null>(null);
const [editorSimulationPlaying, setEditorSimulationPlaying] = useState(false);
const [editorSimulationScene, setEditorSimulationScene] =
useState<RuntimeSceneDefinition | null>(null);
const [editorSimulationMessage, setEditorSimulationMessage] = useState<
string | null
>(null);
const [editorSimulationSnapshot, setEditorSimulationSnapshot] =
useState<EditorSimulationUiSnapshot>(() =>
createInitialEditorSimulationUiSnapshot(editorState.projectDocument.time)
);
const [firstPersonTelemetry, setFirstPersonTelemetry] =
useState<FirstPersonTelemetry | null>(null);
const [runtimeInteractionPrompt, setRuntimeInteractionPrompt] =
@@ -3169,6 +3163,8 @@ export function App({ store, initialStatusMessage }: AppProps) {
Record<string, ProjectAssetStorageRecord>
>({});
const autosaveControllerRef = useRef<EditorAutosaveController | null>(null);
const editorSimulationControllerRef =
useRef<EditorSimulationController | null>(null);
const lastAutosaveErrorRef = useRef<string | null>(null);
const viewportQuadSplitRef = useRef(editorState.viewportQuadSplit);
const lastPointerPositionRef = useRef<HierarchicalMenuPosition>({
@@ -3187,6 +3183,10 @@ export function App({ store, initialStatusMessage }: AppProps) {
} | null>(null);
const [schedulePaneResizeActive, setSchedulePaneResizeActive] =
useState(false);
if (editorSimulationControllerRef.current === null) {
editorSimulationControllerRef.current = new EditorSimulationController();
}
const editorSimulationController = editorSimulationControllerRef.current;
const documentValidation = validateSceneDocument(editorState.document);
const projectValidation = validateProjectDocument(
editorState.projectDocument

View File

@@ -1045,7 +1045,6 @@ export class RuntimeHost {
mount(container: HTMLElement) {
this.container = container;
container.appendChild(this.domElement);
this.domElement.addEventListener("click", this.handleRuntimeClick);
this.domElement.addEventListener(
"pointerdown",
this.handleRuntimePointerDown
@@ -1372,7 +1371,6 @@ export class RuntimeHost {
this.worldBackgroundRenderer.dispose();
this.renderer?.forceContextLoss();
this.renderer?.dispose();
this.domElement.removeEventListener("click", this.handleRuntimeClick);
this.domElement.removeEventListener(
"pointerdown",
this.handleRuntimePointerDown
@@ -5008,6 +5006,7 @@ export class RuntimeHost {
const now = performance.now();
const dt = Math.min((now - this.previousFrameTime) / 1000, 1 / 20);
this.previousFrameTime = now;
this.updateInteractInputState();
this.updatePauseInputState();
this.updateRuntimeTargetingInputState();
const simulationDt = this.isRuntimePaused() ? 0 : dt;
@@ -6591,7 +6590,7 @@ export class RuntimeHost {
}
}
private handleRuntimeClick = () => {
private dispatchRuntimeInteract() {
if (
!this.sceneReady ||
this.runtimeScene === null ||
@@ -6621,7 +6620,7 @@ export class RuntimeHost {
this.runtimeScene,
this.createInteractionDispatcher()
);
};
}
private handleRuntimePointerDown = (event: PointerEvent) => {
if (!this.sceneReady) {
@@ -6630,6 +6629,20 @@ export class RuntimeHost {
this.audioSystem.handleUserGesture();
if (this.runtimeScene !== null) {
const pointerBinding = getPlayerStartMouseBindingCodeForButton(
event.button
);
if (
pointerBinding !== null &&
this.runtimeScene.playerInputBindings.keyboard.interact ===
pointerBinding
) {
this.dispatchRuntimeInteract();
}
}
if (
this.activeRuntimeCameraRig === null ||
!this.activeRuntimeCameraRig.lookAround.enabled ||
@@ -6664,6 +6677,18 @@ export class RuntimeHost {
return;
}
const interactKeyboardBinding =
this.runtimeScene.playerInputBindings.keyboard.interact;
if (
!isPlayerStartMouseBindingCode(interactKeyboardBinding) &&
event.code === interactKeyboardBinding
) {
event.preventDefault();
this.dispatchRuntimeInteract();
this.previousInteractInputActive = true;
return;
}
if (event.code === "Tab") {
event.preventDefault();
this.activateOrCycleRuntimeTarget();
@@ -6748,6 +6773,25 @@ export class RuntimeHost {
this.cameraRigLookDragging = false;
};
private updateInteractInputState() {
if (this.runtimeScene === null || !this.sceneReady) {
this.previousInteractInputActive = false;
return;
}
const interactInputActive =
resolvePlayerStartInteractInput(
this.pressedKeys,
this.runtimeScene.playerInputBindings
) >= 0.5;
if (interactInputActive && !this.previousInteractInputActive) {
this.dispatchRuntimeInteract();
}
this.previousInteractInputActive = interactInputActive;
}
private updatePauseInputState() {
if (this.runtimeScene === null || !this.sceneReady) {
this.previousPauseInputActive = false;