"use strict"; // Layer: simulation/helpers // Shared, UI-free helpers used by the concrete simulation systems. (function (global) { const policy = global.TarinaiItemUpdatePolicy; function forScheduledItems(worldRef, visitor) { return policy.forScheduledItems(worldRef, visitor); } function updateItemsScheduled(worldRef, dt) { return global.TarinaiItemRuntime.updateScheduled(worldRef, dt); } function trackSpatialEntityMoved(entity, thresholdSq) { if (!entity || entity.dead) return false; const x = Number.isFinite(entity.x) ? entity.x : 0; const y = Number.isFinite(entity.y) ? entity.y : 0; const oldX = entity._spatialTrackX; const oldY = entity._spatialTrackY; if (!Number.isFinite(oldX) || !Number.isFinite(oldY)) { entity._spatialTrackX = x; entity._spatialTrackY = y; return true; } const dx = x - oldX; const dy = y - oldY; if (dx * dx + dy * dy <= thresholdSq) return false; entity._spatialTrackX = x; entity._spatialTrackY = y; return true; } function markSpatialDirtyIfMoved(worldRef, list, reason = "moved", threshold = 0.35) { if (!worldRef || !Array.isArray(list) || !list.length) return false; const thresholdSq = threshold * threshold; let moved = false; for (const entity of list) moved = trackSpatialEntityMoved(entity, thresholdSq) || moved; if (moved) worldRef.markSpatialDirty?.(reason); return moved; } function markScheduledItemsMoved(worldRef, reason = "items-moved", threshold = 0.35) { if (!worldRef) return false; const thresholdSq = threshold * threshold; let moved = false; const tracked = global.TarinaiItemRuntime.trackedDynamicItems(worldRef); const source = tracked && tracked.length ? tracked : null; const visit = (entity) => { moved = trackSpatialEntityMoved(entity, thresholdSq) || moved; }; if (source) for (const entity of source) visit(entity); else forScheduledItems(worldRef, visit); if (moved) worldRef.markSpatialDirty?.(reason); return moved; } function updateVisibleWorldRect(worldRef, margin = 120) { const scale = worldRef.viewScale ? worldRef.viewScale() : 1; const vw = (worldRef.viewportW || worldRef.w || 1000) / Math.max(0.01, scale); const vh = (worldRef.viewportH || worldRef.h || 720) / Math.max(0.01, scale); const cameraX = worldRef.cameraX || 0; const cameraY = worldRef.cameraY || 0; return { left: cameraX - margin, top: cameraY - margin, right: cameraX + vw + margin, bottom: cameraY + vh + margin }; } function pointInRect(x, y, rect) { return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; } const helpers = Object.freeze({ updateItemsScheduled, markSpatialDirtyIfMoved, markScheduledItemsMoved, trackSpatialEntityMoved, updateVisibleWorldRect, pointInRect, }); global.TarinaiSimulationRuntime = Object.freeze({ helpers }); })(typeof window !== "undefined" ? window : globalThis);