"use strict"; (function (global) { function cursorForTool(tool) { if (tool === "water_hose") return "crosshair"; if (tool === "shoot") return "none"; if (tool === "pinch") return "grab"; if (tool === "observe") return "default"; if (tool === "poke" || tool === "delete" || tool === "area_delete") return "pointer"; return "crosshair"; } function createInputContext({ canvas, world, ui, uiCache }) { if (!canvas || !world || !uiCache) return null; const inputModeManager = global.TarinaiInputMode; function setMobileInputMode(mode = "auto") { mode = inputModeManager?.setMode?.(mode) || (["auto", "camera", "tool"].includes(mode) ? mode : "auto"); uiCache.mobileInputMode = mode; ui.mobileModeControls?.querySelectorAll("[data-mobile-mode]").forEach(btn => btn.classList.toggle("active", btn.dataset.mobileMode === mode)); document.body.classList.toggle("mobile-mode-auto", mode === "auto"); document.body.classList.toggle("mobile-mode-camera", mode === "camera"); document.body.classList.toggle("mobile-mode-tool", mode === "tool"); global.syncMobileRotateControls?.(); return mode; } function updateTouchMobileUiClass() { const enabled = Boolean(inputModeManager?.refresh?.().touchFirst); document.body.classList.toggle("touch-mobile-ui", enabled); if (ui.mobileModeControls) ui.mobileModeControls.hidden = !enabled; global.syncMobileRotateControls?.(); return enabled; } function mobileInputMode() { return inputModeManager?.currentMode || uiCache.mobileInputMode || "auto"; } function bindMobileModeControls() { updateTouchMobileUiClass(); window.addEventListener("resize", updateTouchMobileUiClass, { passive: true }); window.matchMedia?.("(pointer: coarse)")?.addEventListener?.("change", updateTouchMobileUiClass); setMobileInputMode(uiCache.mobileInputMode || "auto"); const root = ui.mobileModeControls; if (!root || root.dataset.bound === "1") return; root.dataset.bound = "1"; const activate = (e) => { const btn = e.target?.closest?.("[data-mobile-mode]"); if (!btn || !root.contains(btn)) return; const now = performance?.now?.() ?? Date.now(); if (now - Number(root.dataset.lastActivateAt || 0) < 180) return; root.dataset.lastActivateAt = String(now); e.preventDefault?.(); e.stopPropagation?.(); window.TarinaiTooltips.hide(); audio.uiClick?.(); setMobileInputMode(btn.dataset.mobileMode || "auto"); showToast(`\u30b9\u30de\u30db\u64cd\u4f5c: ${btn.textContent || btn.dataset.mobileMode}`); }; root.addEventListener("pointerup", activate, { passive: false }); root.addEventListener("touchend", activate, { passive: false }); root.addEventListener("click", activate); } function touchPoint(touches, index = 0) { const t = touches?.[index]; if (!t) return null; return { clientX: t.clientX, clientY: t.clientY }; } function touchCenter(touches) { const a = touchPoint(touches, 0); const b = touchPoint(touches, 1); if (!a) return null; if (!b) return a; return { clientX: (a.clientX + b.clientX) / 2, clientY: (a.clientY + b.clientY) / 2 }; } function touchDistance(touches) { const a = touchPoint(touches, 0); const b = touchPoint(touches, 1); if (!a || !b) return 0; return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY); } function screenToWorldClient(clientX, clientY) { const rect = canvas.getBoundingClientRect(); const sx = clientX - rect.left; const sy = clientY - rect.top; return world.screenToWorld ? world.screenToWorld(sx, sy) : { x: sx, y: sy, inside: sx >= 0 && sy >= 0 }; } function screenToWorldEvent(e) { return screenToWorldClient(e.clientX, e.clientY); } function clientToWorldRaw(clientX, clientY) { const rect = canvas.getBoundingClientRect(); const sx = clientX - rect.left; const sy = clientY - rect.top; const scale = world.viewScale ? world.viewScale() : 1; const off = world.fieldScreenOffset ? world.fieldScreenOffset() : { x: 0, y: 0 }; const rawX = (sx - off.x) / scale + (world.cameraX || 0); const rawY = (sy - off.y) / scale + (world.cameraY || 0); return { x: rawX, y: rawY, inside: rawX >= 0 && rawY >= 0 && rawX <= world.w && rawY <= world.h }; } function setZoomAroundClientPoint(clientX, clientY, nextZoom) { if (!world.setFieldZoom) return false; const rect = canvas.getBoundingClientRect(); const sx = clientX - rect.left; const sy = clientY - rect.top; const focus = world.screenToWorld ? world.screenToWorld(sx, sy) : { x: sx, y: sy }; if (!world.setFieldZoom(nextZoom)) return false; const off = world.fieldScreenOffset ? world.fieldScreenOffset() : { x: 0, y: 0 }; const scale = world.viewScale ? world.viewScale() : 1; world.cameraX = focus.x - (sx - off.x) / scale; world.cameraY = focus.y - (sy - off.y) / scale; world.clampCamera?.(); return true; } function directFeedTargetAt(p, item = null) { const feeding = global.TarinaiDirectFeedingSystem; if (!p?.inside || !feeding) return null; const type = item?.type || (typeof global.toolItemType === "function" ? global.toolItemType(world.tool || "") : ""); if (!feeding.isDirectFeedType?.(type)) return null; if (item && !feeding.hasConsumableAmount?.(item)) return null; return feeding.findTargetAt?.(world, p.x, p.y) || null; } function setGiveHover(target = null) { const next = target && !target.dead ? target : null; if (world.hoverGiveTarget !== next) { world.hoverGiveTarget = next; world.drawListDirty = true; } return next; } function rememberGrabOrigin(target) { if (!target || uiCache.grabKind !== "tarinai") return; uiCache.grabOrigin = { x: target.x, y: target.y, vx: target.vx || 0, vy: target.vy || 0 }; } function finishTarinaiGrab(target, clientX, clientY) { if (!target || uiCache.grabKind !== "tarinai") return false; const p = screenToWorldClient(clientX, clientY); if (!p.inside && uiCache.grabOrigin) { target.x = uiCache.grabOrigin.x; target.y = uiCache.grabOrigin.y; target.vx = uiCache.grabOrigin.vx || 0; target.vy = uiCache.grabOrigin.vy || 0; target.thought = "\u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u623b\u3055\u308c\u305f"; target.pinchThoughtTimer = Math.max(target.pinchThoughtTimer || 0, 0.8); showToast("\u30d5\u30a3\u30fc\u30eb\u30c9\u5916\u306a\u306e\u3067\u5143\u306e\u4f4d\u7f6e\u306b\u623b\u3057\u307e\u3057\u305f\u3002"); uiCache.grabOrigin = null; return true; } uiCache.grabOrigin = null; return false; } function syncGrabbedPhysicsItem(target, reason = "pinch-move") { if (!target || target.dead) return false; const pb = global.TarinaiPhysicsBodySystem; const isBody = typeof pb?.isBodyType === "function" ? pb.isBodyType(target) : Boolean(typeof pb?.isPhysicsType === "function" && pb.isPhysicsType(target) && typeof pb?.ensureBody === "function" && pb.ensureBody(target, null, { syncFromLegacy: false })); if (!isBody) return false; pb.syncPoseFromItem(target); pb.setScalar(target, "awakeUntil", Math.max(Number(pb.scalar(target, "awakeUntil", 0) || 0), (world.time || 0) + 0.65), reason); global.TarinaiMechanicalSystem.invalidateGeometry(target); return true; } function prepareGrabTarget(target) { if (uiCache.grabKind === "tarinai") { const lodged = target.currentLodgedPin?.() || (world.items || []).find(it => it && isPinType(it.type) && it.pinState === "lodged" && it.pinTargetId === target.id); if (lodged) global.TarinaiItemDynamicPinSystem.detach(lodged, world, "pinch"); target.sleeping = false; if (target.state === "sleep" || target.state === "seek_bed") target.goIdle?.("\u3064\u307e\u307e\u308c\u3066\u3044\u308b"); target.surpriseTimer = Math.max(target.surpriseTimer || 0, 0.18); target.thought = "\u3064\u307e\u307e\u308c\u3066\u3044\u308b"; target.pinchThoughtTimer = Math.max(target.pinchThoughtTimer || 0, 1.2); } else { if (isPinType(target.type) && target.pinState === "lodged") global.TarinaiItemDynamicPinSystem.detach(target, world, "pinch"); if (target.achievementBurstOwnerId) target.achievementBurstNeverStopped = false; target.dropTimer = 0; target.dropMax = 0; target.dropImpactDone = true; } } function beginGrab(p, clientX, clientY, options = {}) { const target = world.findGrabTargetAt ? world.findGrabTargetAt(p.x, p.y) : null; if (!target) { if (options.notifyNoTarget) showToast("\u3064\u307e\u3081\u308b\u5bfe\u8c61\u304c\u3042\u308a\u307e\u305b\u3093\u3002"); return false; } global.TarinaiAchievements?.recordIntervention?.({ world, tool: "pinch", target }); global.TarinaiHistory.capture(world, "pinch"); audio.grab?.(); uiCache.grabbing = true; uiCache.grabMoved = false; uiCache.grabTarget = target; uiCache.grabKind = target instanceof Tarinai ? "tarinai" : "item"; rememberGrabOrigin(target); uiCache.grabLastWorldX = p.x; uiCache.grabLastWorldY = p.y; uiCache.grabVelocityX = 0; uiCache.grabVelocityY = 0; uiCache.grabLastMoveAt = performance?.now?.() ?? Date.now(); uiCache.grabStartX = clientX || 0; uiCache.grabStartY = clientY || 0; target.playerHeld = true; target._heldByPlayer = true; if (uiCache.grabKind === "item") syncGrabbedPhysicsItem(target, "pinch-start"); if (target.type === "duplicator") { target.duplicatorLoadSuppressedUntil = Math.max(target.duplicatorLoadSuppressedUntil || 0, (world.time || 0) + 1.0); } prepareGrabTarget(target); if (options.touchMode) uiCache.touchMode = options.touchMode; renderStats(); return true; } function moveGrab(p, clientX, clientY, options = {}) { world.pointer.x = p?.x ?? world.pointer.x; world.pointer.y = p?.y ?? world.pointer.y; world.pointer.inside = Boolean(p?.inside); const target = uiCache.grabTarget; if (!target || target.dead) { uiCache.grabbing = false; uiCache.grabTarget = null; if (options.touch) uiCache.touchMode = ""; canvas.style.cursor = world.tool === "pinch" ? "grab" : "default"; return false; } const dx = p.x - (uiCache.grabLastWorldX ?? p.x); const dy = p.y - (uiCache.grabLastWorldY ?? p.y); const moveNow = performance?.now?.() ?? Date.now(); const moveDt = Math.max(0.012, Math.min(0.18, (moveNow - (uiCache.grabLastMoveAt || moveNow)) / 1000)); uiCache.grabLastMoveAt = moveNow; uiCache.grabVelocityX = clamp(dx / moveDt, -820, 820); uiCache.grabVelocityY = clamp(dy / moveDt, -820, 820); uiCache.grabLastWorldX = p.x; uiCache.grabLastWorldY = p.y; const beforeX = Number(target.x || 0) || 0; const beforeY = Number(target.y || 0) || 0; const pad = target.radius || target.r || 16; let desired = (uiCache.grabKind === "tarinai" && !p.inside) ? clientToWorldRaw(clientX, clientY) : { x: p.x, y: p.y }; if (String(world.toolPickMode || "free") !== "free") { desired = global.TarinaiPlacementPreviewSystem?.snapPoint?.(world, desired.x, desired.y) || desired; } target.x = clamp(desired.x, CONFIG.worldPadding + pad * 0.15, world.w - CONFIG.worldPadding - pad * 0.15); target.y = clamp(desired.y, CONFIG.worldPadding + pad * 0.15, world.h - CONFIG.worldPadding - pad * 0.15); const movedX = (Number(target.x || 0) || 0) - beforeX; const movedY = (Number(target.y || 0) || 0) - beforeY; if (uiCache.grabKind === "item" && target.type === "reciprocator") { const pb = (typeof window !== "undefined" ? window : globalThis).TarinaiPhysicsBodySystem; pb?.setScalar?.(target, "railAnchorX", (Number(pb?.scalar?.(target, "railAnchorX", beforeX)) || beforeX) + movedX, "grab-move"); pb?.setScalar?.(target, "railAnchorY", (Number(pb?.scalar?.(target, "railAnchorY", beforeY)) || beforeY) + movedY, "grab-move"); target.prevX = target.x; target.prevY = target.y; } if (uiCache.grabKind === "item") syncGrabbedPhysicsItem(target, "pinch-move"); if (uiCache.grabKind === "tarinai") { // While a Tarinai is held, its AI/passive physics must not keep the last // drag velocity. Otherwise the body starts sliding when the cursor is // stopped but the button is still down. target.vx = 0; target.vy = 0; target.impulseVx = 0; target.impulseVy = 0; target._lastHeldByPlayerAt = world.time || 0; target.goIdle?.("\u3064\u307e\u307e\u308c\u3066\u3044\u308b"); if (options.touch) target.pinchThoughtTimer = Math.max(target.pinchThoughtTimer || 0, 0.8); } else if (String(world.toolPickMode || "free") !== "free") { target.vx = 0; target.vy = 0; uiCache.grabVelocityX = 0; uiCache.grabVelocityY = 0; } else { target.vx = clamp(uiCache.grabVelocityX || dx * 16, -820, 820); target.vy = clamp(uiCache.grabVelocityY || dy * 16, -820, 820); } if ((target.type === "ball" || target.type === "balloon")) { target.prevX = target.x - dx; target.prevY = target.y - dy; target.spinVelocity = clamp((target.spinVelocity || 0) + dx * 0.06, -38, 38); } setGiveHover(uiCache.grabKind === "item" ? directFeedTargetAt(p, target) : null); world.drawListDirty = true; const now = performance.now(); if (!uiCache.grabLastSpatialAt || now - uiCache.grabLastSpatialAt > 80) { world.rebuildSpatial?.(); uiCache.grabLastSpatialAt = now; } canvas.style.cursor = "grabbing"; if (options.renderFrame) render(); return true; } function endGrab(clientX, clientY) { if (!uiCache.grabbing) return false; const target = uiCache.grabTarget; let directlyGiven = false; if (target) { if (uiCache.grabKind === "tarinai") { target.target = null; target.targetKey = ""; target.surpriseTimer = Math.max(target.surpriseTimer || 0, 0.15); finishTarinaiGrab(target, clientX, clientY); } else if (uiCache.grabKind === "item") { const releasePoint = screenToWorldClient(clientX, clientY); const giveTarget = directFeedTargetAt(releasePoint, target); const result = giveTarget ? global.TarinaiDirectFeedingSystem?.give?.(world, giveTarget, target, { source: "pinch" }) : null; directlyGiven = Boolean(result); if (!directlyGiven) { const gridRelease = String(world.toolPickMode || "free") !== "free"; const releaseVx = gridRelease ? 0 : clamp(Number(uiCache.grabVelocityX || target.vx || 0) || 0, -900, 900); const releaseVy = gridRelease ? 0 : clamp(Number(uiCache.grabVelocityY || target.vy || 0) || 0, -900, 900); const releaseSpeed = Math.hypot(releaseVx, releaseVy); if (target.type === "plushie" && typeof target.fling === "function") { const sourceX = Number(uiCache.grabLastWorldX ?? target.x) || target.x; const sourceY = Number(uiCache.grabLastWorldY ?? target.y) || target.y; const vx = releaseSpeed > 80 ? releaseVx : 0; const vy = releaseSpeed > 80 ? releaseVy : 0; target.fling(world, sourceX, sourceY, { source: "pinch", vx, vy, speed: Math.max(620, releaseSpeed * 1.05), life: 2.8 }); } else if (releaseSpeed > 18) { target.vx = releaseVx; target.vy = releaseVy; const frameDt = Math.max(1 / 90, Math.min(1 / 30, Number(world.dt || 1 / 60) || 1 / 60)); target.prevX = target.x - releaseVx * frameDt; target.prevY = target.y - releaseVy * frameDt; if ((target.type === "ball" || target.type === "balloon")) target.spinVelocity = clamp((target.spinVelocity || 0) + releaseVx * 0.018, -42, 42); } else if ((target.type === "ball" || target.type === "balloon")) { target.prevX = target.x; target.prevY = target.y; } } } if (uiCache.grabKind === "item" && !directlyGiven && !target.dead) syncGrabbedPhysicsItem(target, "pinch-release"); target.playerHeld = false; target._heldByPlayer = false; if (uiCache.grabKind === "tarinai") { target.vx = 0; target.vy = 0; target.impulseVx = 0; target.impulseVy = 0; target.pinchThoughtTimer = 0; if (target.thought === "\u3064\u307e\u307e\u308c\u3066\u3044\u308b") target.thought = ""; const behavior = typeof currentTarinaiBehavior === "function" ? currentTarinaiBehavior(target) : null; const reason = String(behavior?.reason || behavior?.text || behavior?.label || ""); if (reason === "\u3064\u307e\u307e\u308c\u3066\u3044\u308b" && typeof clearBehavior === "function") clearBehavior(target); } if (target.type === "duplicator") { target.duplicatorLoadSuppressedUntil = Math.max(target.duplicatorLoadSuppressedUntil || 0, (world.time || 0) + 1.2); } } if (!directlyGiven) audio.drop?.(); uiCache.grabbing = false; uiCache.grabTarget = null; uiCache.grabKind = ""; uiCache.grabVelocityX = 0; uiCache.grabVelocityY = 0; uiCache.grabLastMoveAt = 0; setGiveHover(null); world.rebuildSpatial?.(); uiCache.grabLastSpatialAt = 0; canvas.style.cursor = cursorForTool(world.tool); renderStats(); return true; } function beginHose(p, clientX, clientY, options = {}) { global.TarinaiHistory.capture(world, "water-hose"); audio.waterHose?.(); uiCache.hosing = true; uiCache.hoseMoved = false; uiCache.hoseLastWorldX = p.x; uiCache.hoseLastWorldY = p.y; uiCache.hoseStartX = clientX; uiCache.hoseStartY = clientY; uiCache.hoseLastApplyAt = global.performance?.now?.() || Date.now(); world.applyWaterHose?.(p.x, p.y, 0, 0, 0.18); if (options.touchMode) uiCache.touchMode = options.touchMode; render(); return true; } function moveHose(p, clientX, clientY, options = {}) { uiCache.hoseLastClientX = clientX; uiCache.hoseLastClientY = clientY; const dx = p.x - (uiCache.hoseLastWorldX ?? p.x); const dy = p.y - (uiCache.hoseLastWorldY ?? p.y); uiCache.hoseLastWorldX = p.x; uiCache.hoseLastWorldY = p.y; const totalMove = Math.hypot(clientX - (uiCache.hoseStartX || clientX), clientY - (uiCache.hoseStartY || clientY)); if (totalMove > 3) { uiCache.hoseMoved = true; if (options.touch) uiCache.touchMoved = true; } if (p.inside) { const now = global.performance?.now?.() || Date.now(); const elapsed = Math.max(0.06, Math.min(0.22, (now - (uiCache.hoseLastApplyAt || now)) / 1000)); uiCache.hoseLastApplyAt = now; world.applyWaterHose?.(p.x, p.y, dx, dy, elapsed); } canvas.style.cursor = "crosshair"; render(); } function endHose() { if (!uiCache.hosing) return false; uiCache.hosing = false; uiCache.hoseLastApplyAt = 0; canvas.style.cursor = cursorForTool(world.tool); world.rebuildSpatial?.(); renderStats(); return true; } function beginShoot(p, clientX, clientY, options = {}) { if (!p?.inside) return false; global.TarinaiHistory.capture(world, "shoot"); uiCache.shooting = true; uiCache.shootSuppressClick = true; uiCache.shootFired = false; uiCache.shootStartX = clientX; uiCache.shootStartY = clientY; world.pointer.x = p.x; world.pointer.y = p.y; world.pointer.inside = true; world.setShootCursorFromPointer?.(p); uiCache.shootLastRawWorldX = p.x; uiCache.shootLastRawWorldY = p.y; uiCache.shootFired = Boolean(world.fireShootTool?.()) || Boolean(uiCache.shootFired); if (options.touchMode) uiCache.touchMode = options.touchMode; if (uiCache.shootHoldTimer) clearInterval(uiCache.shootHoldTimer); uiCache.shootHoldTimer = setInterval(() => { if (!uiCache.shooting) { clearInterval(uiCache.shootHoldTimer); uiCache.shootHoldTimer = null; return; } uiCache.shootFired = Boolean(world.fireShootTool?.()) || Boolean(uiCache.shootFired); }, 120); canvas.style.cursor = "none"; render(); return true; } function moveShoot(p, clientX, clientY, options = {}) { const rawDx = (Number(p.x) || 0) - (Number(uiCache.shootLastRawWorldX ?? p.x) || 0); const rawDy = (Number(p.y) || 0) - (Number(uiCache.shootLastRawWorldY ?? p.y) || 0); uiCache.shootLastRawWorldX = p.x; uiCache.shootLastRawWorldY = p.y; world.pointer.x = p.x; world.pointer.y = p.y; world.pointer.inside = !!p.inside; if (p.inside) world.moveShootCursorBy?.(rawDx, rawDy); const totalMove = Math.hypot(clientX - (uiCache.shootStartX || clientX), clientY - (uiCache.shootStartY || clientY)); if (totalMove > 3) { if (options.touch) uiCache.touchMoved = true; } canvas.style.cursor = "none"; render(); return true; } function endShoot() { if (!uiCache.shooting) return false; uiCache.shooting = false; if (uiCache.shootHoldTimer) { clearInterval(uiCache.shootHoldTimer); uiCache.shootHoldTimer = null; } if (uiCache.shootFired) audio.boltAction?.(); uiCache.shootFired = false; canvas.style.cursor = cursorForTool(world.tool); renderStats(); return true; } function beginAreaDelete(p, clientX, clientY, options = {}) { global.TarinaiHistory.capture(world, "area-delete"); uiCache.areaDeleting = true; uiCache.areaDeleteMoved = false; uiCache.areaDeleteStartX = clientX; uiCache.areaDeleteStartY = clientY; world._areaDeleteDragRemoved = 0; if (p.inside) world.deleteItemsInArea?.(p.x, p.y, { silentEmpty: true, silentLog: true, drag: true }); if (options.touchMode) uiCache.touchMode = options.touchMode; render(); return true; } function moveAreaDelete(p, clientX, clientY, options = {}) { const totalMove = Math.hypot(clientX - (uiCache.areaDeleteStartX || clientX), clientY - (uiCache.areaDeleteStartY || clientY)); if (totalMove > 3) { uiCache.areaDeleteMoved = true; if (options.touch) uiCache.touchMoved = true; } if (p.inside) world.deleteItemsInArea?.(p.x, p.y, { silentEmpty: true, silentLog: true, drag: true }); canvas.style.cursor = "crosshair"; render(); return true; } function endAreaDelete() { if (!uiCache.areaDeleting) return false; uiCache.areaDeleting = false; const removed = world._areaDeleteDragRemoved || 0; world._areaDeleteDragRemoved = 0; canvas.style.cursor = cursorForTool(world.tool); world.rebuildSpatial?.(); renderStats(); if (removed > 0) { world.log?.(`\u7bc4\u56f2\u524a\u9664\u3067${removed}\u500b\u6d88\u3057\u305f\u3002`, "observe"); showToast(`${removed}\u500b\u6d88\u3057\u307e\u3057\u305f\u3002`); } return true; } function beginPan(clientX, clientY, button = null) { uiCache.panning = true; uiCache.panMoved = false; uiCache.panButton = button; uiCache.panStartX = clientX; uiCache.panStartY = clientY; uiCache.panLastX = clientX; uiCache.panLastY = clientY; } function movePan(clientX, clientY) { if (!uiCache.panning) return false; const dx = clientX - uiCache.panLastX; const dy = clientY - uiCache.panLastY; uiCache.panLastX = clientX; uiCache.panLastY = clientY; const totalMove = Math.hypot(clientX - uiCache.panStartX, clientY - uiCache.panStartY); if (totalMove > 8) { canvas.style.cursor = "grabbing"; world.panCamera?.(-dx, -dy); uiCache.panMoved = true; render(); } return true; } function endPan(button = null) { if (!uiCache.panning) return false; if (uiCache.panButton !== null && button !== null && button !== uiCache.panButton) return false; uiCache.panning = false; uiCache.panButton = null; canvas.style.cursor = cursorForTool(world.tool); return true; } function updatePointerFromEvent(e) { const p = screenToWorldEvent(e); const prevX = world.pointer.x; const prevY = world.pointer.y; world.pointer.x = p.x; world.pointer.y = p.y; world.pointer.inside = !!p.inside; const delta = Math.hypot((p.x || 0) - (prevX || 0), (p.y || 0) - (prevY || 0)); world.pointer.motion = clamp(delta / 18, 0, 2); const inactivePointerAction = !uiCache.grabbing && !uiCache.panning && !uiCache.hosing && !uiCache.areaDeleting && !uiCache.shooting; const giveTarget = inactivePointerAction ? directFeedTargetAt(p) : null; if (world.tool === "shoot") { setGiveHover(null); if (p.inside) { if (world.shootCursorActive) { const lastRawX = Number.isFinite(Number(world.shootPointerRawX)) ? Number(world.shootPointerRawX) : prevX; const lastRawY = Number.isFinite(Number(world.shootPointerRawY)) ? Number(world.shootPointerRawY) : prevY; world.moveShootCursorBy?.((Number(p.x) || 0) - (Number(lastRawX) || 0), (Number(p.y) || 0) - (Number(lastRawY) || 0)); } else { world.setShootCursorFromPointer?.(p); } world.shootPointerRawX = p.x; world.shootPointerRawY = p.y; } canvas.style.cursor = "none"; } else if (giveTarget) { setGiveHover(giveTarget); if (world.hoverGrabTarget) { world.hoverGrabTarget = null; world.drawListDirty = true; } if (world.hoverDeleteTarget) { world.hoverDeleteTarget = null; world.drawListDirty = true; } if (world.hoverPokeTarget) { world.hoverPokeTarget = null; world.drawListDirty = true; } canvas.style.cursor = "pointer"; } else if (world.tool === "poke" && p.inside && inactivePointerAction) { setGiveHover(null); const target = world.findPokeTargetAt ? world.findPokeTargetAt(p.x, p.y) : null; if (world.hoverPokeTarget !== target) { world.hoverPokeTarget = target || null; world.drawListDirty = true; } if (world.hoverGrabTarget) { world.hoverGrabTarget = null; world.drawListDirty = true; } if (world.hoverDeleteTarget) { world.hoverDeleteTarget = null; world.drawListDirty = true; } canvas.style.cursor = target ? "pointer" : "default"; } else if (world.tool === "pinch" && p.inside && inactivePointerAction) { setGiveHover(null); const target = world.findGrabTargetAt ? world.findGrabTargetAt(p.x, p.y) : null; if (world.hoverGrabTarget !== target) { world.hoverGrabTarget = target || null; world.drawListDirty = true; } if (world.hoverDeleteTarget) { world.hoverDeleteTarget = null; world.drawListDirty = true; } if (world.hoverPokeTarget) { world.hoverPokeTarget = null; world.drawListDirty = true; } canvas.style.cursor = target ? "grab" : "default"; } else if (world.tool === "delete" && p.inside && inactivePointerAction) { setGiveHover(null); const target = world.findDeleteToolTargetAt ? world.findDeleteToolTargetAt(p.x, p.y) : null; if (world.hoverDeleteTarget !== target) { world.hoverDeleteTarget = target || null; world.drawListDirty = true; } if (world.hoverGrabTarget) { world.hoverGrabTarget = null; world.drawListDirty = true; } if (world.hoverPokeTarget) { world.hoverPokeTarget = null; world.drawListDirty = true; } canvas.style.cursor = target ? "pointer" : "default"; } else { setGiveHover(null); if (world.hoverGrabTarget) { world.hoverGrabTarget = null; world.drawListDirty = true; } if (world.hoverDeleteTarget) { world.hoverDeleteTarget = null; world.drawListDirty = true; } if (world.hoverPokeTarget) { world.hoverPokeTarget = null; world.drawListDirty = true; } } } function cancelPointerActions() { uiCache.touchMode = ""; uiCache.touchMoved = false; uiCache.hosing = false; if (uiCache.hoseHoldTimer) { clearInterval(uiCache.hoseHoldTimer); uiCache.hoseHoldTimer = null; } uiCache.shooting = false; uiCache.shootFired = false; if (uiCache.shootHoldTimer) { clearInterval(uiCache.shootHoldTimer); uiCache.shootHoldTimer = null; } uiCache.areaDeleting = false; uiCache.grabbing = false; uiCache.grabTarget = null; uiCache.grabKind = ""; uiCache.grabVelocityX = 0; uiCache.grabVelocityY = 0; uiCache.grabLastMoveAt = 0; if (world) { world.hoverGrabTarget = null; world.hoverDeleteTarget = null; world.hoverGiveTarget = null; world.hoverPokeTarget = null; } } return { canvas, world, ui, uiCache, cursorForTool, setMobileInputMode, updateTouchMobileUiClass, mobileInputMode, bindMobileModeControls, touchPoint, touchCenter, touchDistance, screenToWorldClient, screenToWorldEvent, clientToWorldRaw, setZoomAroundClientPoint, beginGrab, moveGrab, endGrab, beginHose, moveHose, endHose, beginShoot, moveShoot, endShoot, beginAreaDelete, moveAreaDelete, endAreaDelete, beginPan, movePan, endPan, updatePointerFromEvent, cancelPointerActions, }; } global.TarinaiUIInputShared = Object.freeze({ createInputContext, cursorForTool }); })(window);