121 lines
3.9 KiB
JavaScript
121 lines
3.9 KiB
JavaScript
"use strict";
|
|
|
|
// Shared pointer action router for mouse and touch. Keeps long-hold timers,
|
|
// drag movement thresholds, and tap suppression consistent across input types.
|
|
(function (global) {
|
|
const DRAG_THRESHOLD = 3;
|
|
|
|
function createPointerActionManager(ctx) {
|
|
if (!ctx?.canvas || !ctx?.world || !ctx?.uiCache) return null;
|
|
const { canvas, world, uiCache } = ctx;
|
|
|
|
function clearHoseHoldTimer() {
|
|
if (uiCache.hoseHoldTimer) {
|
|
clearInterval(uiCache.hoseHoldTimer);
|
|
uiCache.hoseHoldTimer = null;
|
|
}
|
|
}
|
|
|
|
function ensureHoseHoldTimer(clientX, clientY, options = {}) {
|
|
clearHoseHoldTimer();
|
|
uiCache.hoseHoldTimer = setInterval(() => {
|
|
if (!uiCache.hosing) {
|
|
clearHoseHoldTimer();
|
|
return;
|
|
}
|
|
const cx = uiCache.hoseLastClientX || uiCache.touchLastX || clientX;
|
|
const cy = uiCache.hoseLastClientY || uiCache.touchLastY || clientY;
|
|
const hp = ctx.screenToWorldClient(cx, cy);
|
|
ctx.moveHose(hp, cx, cy, { touch: Boolean(options.touch) });
|
|
}, 90);
|
|
}
|
|
|
|
function beginToolAt(p, clientX, clientY, options = {}) {
|
|
if (!p?.inside) return false;
|
|
const tool = String(world.tool || "");
|
|
if (tool === "area_delete") {
|
|
return Boolean(ctx.beginAreaDelete(p, clientX, clientY, options));
|
|
}
|
|
if (tool === "water_hose") {
|
|
const started = Boolean(ctx.beginHose(p, clientX, clientY, options));
|
|
if (started) ensureHoseHoldTimer(clientX, clientY, options);
|
|
return started;
|
|
}
|
|
if (tool === "shoot") {
|
|
return Boolean(ctx.beginShoot(p, clientX, clientY, options));
|
|
}
|
|
if (tool === "pinch") {
|
|
const started = Boolean(ctx.beginGrab(p, clientX, clientY, options));
|
|
if (started) canvas.style.cursor = "grabbing";
|
|
return started;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function moveActive(p, clientX, clientY, options = {}) {
|
|
if (uiCache.areaDeleting) return Boolean(ctx.moveAreaDelete(p, clientX, clientY, options) || true);
|
|
if (uiCache.hosing) return Boolean(ctx.moveHose(p, clientX, clientY, options) || true);
|
|
if (uiCache.shooting) return Boolean(ctx.moveShoot(p, clientX, clientY, options));
|
|
if (uiCache.grabbing) {
|
|
const sx = uiCache.grabStartX || clientX;
|
|
const sy = uiCache.grabStartY || clientY;
|
|
if (Math.hypot(clientX - sx, clientY - sy) > DRAG_THRESHOLD) {
|
|
uiCache.grabMoved = true;
|
|
if (options.touch) uiCache.touchMoved = true;
|
|
}
|
|
return Boolean(ctx.moveGrab(p, clientX, clientY, options));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function endActive(clientX = 0, clientY = 0) {
|
|
if (ctx.endAreaDelete()) return true;
|
|
if (ctx.endHose()) {
|
|
clearHoseHoldTimer();
|
|
return true;
|
|
}
|
|
if (ctx.endShoot()) return true;
|
|
if (ctx.endGrab(clientX, clientY)) return true;
|
|
return false;
|
|
}
|
|
|
|
function shouldSuppressClick() {
|
|
const flags = ["areaDeleteMoved", "hoseMoved", "grabMoved", "shootSuppressClick", "panMoved"];
|
|
for (const key of flags) {
|
|
if (uiCache[key]) {
|
|
uiCache[key] = false;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function dispatchTapAt(p) {
|
|
if (!p?.inside) return false;
|
|
global.TarinaiCommands.dispatch(world, { type: "pointer.primary", x: p.x, y: p.y });
|
|
global.renderStats?.();
|
|
return true;
|
|
}
|
|
|
|
function dispatchTapClient(clientX, clientY) {
|
|
return dispatchTapAt(ctx.screenToWorldClient(clientX, clientY));
|
|
}
|
|
|
|
function cancel() {
|
|
clearHoseHoldTimer();
|
|
ctx.cancelPointerActions?.();
|
|
}
|
|
|
|
return Object.freeze({
|
|
beginToolAt,
|
|
moveActive,
|
|
endActive,
|
|
shouldSuppressClick,
|
|
dispatchTapAt,
|
|
dispatchTapClient,
|
|
cancel,
|
|
});
|
|
}
|
|
|
|
global.TarinaiPointerActionSystem = Object.freeze({ create: createPointerActionManager });
|
|
})(typeof window !== "undefined" ? window : globalThis);
|