"use strict"; (function (global) { function bindTouchInput(ctx) { if (!ctx?.canvas) return false; const { canvas, world, uiCache } = ctx; const actions = global.TarinaiPointerActionSystem.create(ctx); ctx.bindMobileModeControls(); const isMobileAutoScrollMode = () => { const mode = ctx.mobileInputMode?.() || "auto"; const touchMobile = document.body.classList.contains("touch-mobile-ui") || window.matchMedia?.("(max-width: 980px)")?.matches; return touchMobile && mode === "auto" && (!world.tool || world.tool === "observe"); }; canvas.addEventListener("touchstart", (e) => { if (!e.touches?.length) return; uiCache.touchMoved = false; const mode = ctx.mobileInputMode(); const autoPageScroll = isMobileAutoScrollMode(); if (!autoPageScroll) e.preventDefault(); if (mode === "camera" && e.touches.length === 1) { const t = e.touches[0]; uiCache.touchStartX = t.clientX; uiCache.touchStartY = t.clientY; uiCache.touchLastX = t.clientX; uiCache.touchLastY = t.clientY; uiCache.touchMode = "cameraPan"; return; } if (e.touches.length >= 2) { const c = ctx.touchCenter(e.touches); uiCache.touchMode = "pinchZoom"; uiCache.pinchStartDistance = Math.max(1, ctx.touchDistance(e.touches)); uiCache.pinchStartZoom = world.fieldZoom || 1; uiCache.pinchLastCenterX = c?.clientX || 0; uiCache.pinchLastCenterY = c?.clientY || 0; return; } const t = e.touches[0]; const p = ctx.screenToWorldClient(t.clientX, t.clientY); uiCache.touchStartX = t.clientX; uiCache.touchStartY = t.clientY; uiCache.touchLastX = t.clientX; uiCache.touchLastY = t.clientY; if (mode === "tool" && world.tool === "observe") { uiCache.touchMode = "toolTapOnly"; return; } const touchModeByTool = { shoot: "shoot", area_delete: "areaDelete", water_hose: "hose", pinch: "grab" }; const touchMode = touchModeByTool[world.tool]; if (touchMode && actions?.beginToolAt?.(p, t.clientX, t.clientY, { touch: true, touchMode })) return; uiCache.touchMode = autoPageScroll ? "autoTapOrScroll" : (mode === "tool" ? "toolTapOnly" : "panOrTap"); }, { passive: false }); canvas.addEventListener("touchmove", (e) => { if (!e.touches?.length || !uiCache.touchMode) return; if (uiCache.touchMode !== "autoTapOrScroll") e.preventDefault(); if (uiCache.touchMode === "pinchZoom" && e.touches.length >= 2) { const c = ctx.touchCenter(e.touches); const distNow = Math.max(1, ctx.touchDistance(e.touches)); const ratio = distNow / Math.max(1, uiCache.pinchStartDistance || distNow); ctx.setZoomAroundClientPoint(c.clientX, c.clientY, (uiCache.pinchStartZoom || 1) * ratio); const dx = c.clientX - (uiCache.pinchLastCenterX || c.clientX); const dy = c.clientY - (uiCache.pinchLastCenterY || c.clientY); uiCache.pinchLastCenterX = c.clientX; uiCache.pinchLastCenterY = c.clientY; if (Math.hypot(dx, dy) > 0.1) world.panCamera?.(-dx, -dy); uiCache.touchMoved = true; render(); return; } const t = e.touches[0]; const p = ctx.screenToWorldClient(t.clientX, t.clientY); const screenDx = t.clientX - (uiCache.touchLastX || t.clientX); const screenDy = t.clientY - (uiCache.touchLastY || t.clientY); const totalMove = Math.hypot(t.clientX - (uiCache.touchStartX || t.clientX), t.clientY - (uiCache.touchStartY || t.clientY)); uiCache.touchLastX = t.clientX; uiCache.touchLastY = t.clientY; if (uiCache.touchMode === "cameraPan") { if (totalMove > 2) uiCache.touchMoved = true; world.panCamera?.(-screenDx, -screenDy); render(); return; } if (uiCache.touchMode === "toolTapOnly") { if (totalMove > 8) uiCache.touchMoved = true; return; } if (actions?.moveActive?.(p, t.clientX, t.clientY, { touch: true, renderFrame: uiCache.touchMode === "grab" })) return; if (uiCache.touchMode === "autoTapOrScroll") { if (totalMove > 7) uiCache.touchMoved = true; return; } if (uiCache.touchMode === "panOrTap") { if (totalMove > 7) { uiCache.touchMoved = true; world.panCamera?.(-screenDx, -screenDy); render(); } } }, { passive: false }); canvas.addEventListener("touchend", (e) => { if (uiCache.touchMode !== "autoTapOrScroll") e.preventDefault(); if (actions?.endActive?.(e.changedTouches?.[0]?.clientX || uiCache.touchLastX || 0, e.changedTouches?.[0]?.clientY || uiCache.touchLastY || 0)) { // Active tool ended; click suppression is handled through movement flags. } else if (uiCache.touchMode === "toolTapOnly" && !uiCache.touchMoved) { actions?.dispatchTapClient?.(uiCache.touchLastX || uiCache.touchStartX, uiCache.touchLastY || uiCache.touchStartY); } else if (uiCache.touchMode === "autoTapOrScroll" && !uiCache.touchMoved) { e.preventDefault(); actions?.dispatchTapClient?.(uiCache.touchLastX || uiCache.touchStartX, uiCache.touchLastY || uiCache.touchStartY); } else if (uiCache.touchMode === "panOrTap" && !uiCache.touchMoved) { actions?.dispatchTapClient?.(uiCache.touchLastX || uiCache.touchStartX, uiCache.touchLastY || uiCache.touchStartY); } if (!e.touches?.length) { uiCache.touchMode = ""; uiCache.touchMoved = false; } }, { passive: false }); canvas.addEventListener("touchcancel", () => actions?.cancel?.(), { passive: true }); return true; } global.TarinaiTouchInput = Object.freeze({ bindTouchInput }); })(window);