81 lines
3.2 KiB
JavaScript
81 lines
3.2 KiB
JavaScript
"use strict";
|
|
|
|
// Layer: simulation/system-environment
|
|
// Owns frame time, world clock, frame signals, weather/terrain preparation,
|
|
// phase-change notification, and diagnostics publication.
|
|
(function (global) {
|
|
function normalizeDelta(worldRef, dt) {
|
|
if (!worldRef || worldRef.paused) return null;
|
|
return dt * worldRef.speed;
|
|
}
|
|
|
|
function updateClock(worldRef, dt) {
|
|
const previousDay = worldRef.day || 1;
|
|
worldRef.time += dt;
|
|
worldRef.elapsedDays = Math.max(0, Math.floor(worldRef.time / CONFIG.dayLength));
|
|
worldRef.day = worldRef.elapsedDays + 1;
|
|
if (worldRef.day !== previousDay) {
|
|
for (const t of worldRef.tarinai || []) if (t) t.personalityDaily = { day: worldRef.day, total: 0, byKey: {}, byCause: {} };
|
|
global.TarinaiAchievements?.evaluateEvent?.(worldRef, "season", { previousDay, day: worldRef.day });
|
|
}
|
|
}
|
|
|
|
function updateFrameSignals(worldRef, dt) {
|
|
if (worldRef.pointer) worldRef.pointer.motion = Math.max(0, (worldRef.pointer.motion || 0) - dt * 2.4);
|
|
const recoilX = Number(worldRef.shootCursorOffsetX) || 0;
|
|
const recoilY = Number(worldRef.shootCursorOffsetY) || 0;
|
|
if (recoilX || recoilY) {
|
|
// Recoil is a real aim offset, but it should settle back to the actual
|
|
// mouse/touch position in roughly 1-2 seconds instead of drifting forever.
|
|
const k = Math.exp(-Math.max(0, Number(dt) || 0) * 3.15);
|
|
worldRef.shootCursorOffsetX = Math.abs(recoilX * k) < 0.25 ? 0 : recoilX * k;
|
|
worldRef.shootCursorOffsetY = Math.abs(recoilY * k) < 0.25 ? 0 : recoilY * k;
|
|
worldRef.drawListDirty = true;
|
|
}
|
|
worldRef.shakeTimer = Math.max(0, (worldRef.shakeTimer || 0) - dt);
|
|
if ((worldRef.shakeTimer || 0) <= 0) { worldRef.shakeDuration = 0; worldRef.shakeStrength = 0; }
|
|
}
|
|
|
|
function updateEnvironment(worldRef, dt) {
|
|
worldRef.updateWeather(dt);
|
|
worldRef.updateTemperature?.(dt);
|
|
worldRef.updateTerrainDirtyState?.(dt);
|
|
const now = worldRef.time || 0;
|
|
if (now >= (worldRef.nextColonyMoodEvalAt || 0)) worldRef.evaluateColonyMood?.();
|
|
}
|
|
|
|
function prepareSpatialFrame(worldRef) {
|
|
if (global.TarinaiPerf?.diagnosticsEnabled?.() === true) {
|
|
worldRef.spatialRebuildsThisFrame = 0;
|
|
worldRef.spatialDirtyMarksThisFrame = 0;
|
|
}
|
|
worldRef.ensureSpatial?.("update-start");
|
|
worldRef.beginFramePerformanceBudgets?.();
|
|
}
|
|
|
|
function emitPhaseChange(worldRef) {
|
|
const phase = worldRef.phaseName();
|
|
if (phase !== worldRef.lastPhase) {
|
|
worldRef.lastPhase = phase;
|
|
worldRef.emit("world:phase", { world: worldRef, phase });
|
|
}
|
|
}
|
|
|
|
function finalizeDiagnostics(worldRef) {
|
|
if (!worldRef) return;
|
|
const wantsDiagnostics = Boolean(global.__tarinaiDebugOverlayEnabled || global.__tarinaiForceRuntimeDiagnostics);
|
|
worldRef.lastRuntimeDiagnostics = wantsDiagnostics ? (worldRef.runtimeDiagnostics?.() || null) : null;
|
|
}
|
|
|
|
const phases = Object.freeze({
|
|
normalizeDelta,
|
|
updateClock,
|
|
updateFrameSignals,
|
|
updateEnvironment,
|
|
prepareSpatialFrame,
|
|
emitPhaseChange,
|
|
finalizeDiagnostics,
|
|
});
|
|
|
|
global.TarinaiEnvironmentSimulationSystem = Object.freeze({ phases });
|
|
})(typeof window !== "undefined" ? window : globalThis);
|