From e872867c5ef6e5bca9cff6a02d562919466fe60d Mon Sep 17 00:00:00 2001 From: "azykov@mail.ru" Date: Fri, 4 Sep 2026 16:27:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=B2=D0=B8=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B7=D0=B0=20=D0=BA=D1=83=D1=80=D1=81=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=BC=20=D0=BC=D1=8B=D1=88=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/GameMap.tsx | 70 +++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/components/GameMap.tsx b/src/components/GameMap.tsx index e89d594..5048de4 100644 --- a/src/components/GameMap.tsx +++ b/src/components/GameMap.tsx @@ -25,17 +25,7 @@ const LogEntity = observer(function LogEntity({ log }: { log: LogDrop }) { ); }); -const TreeEntity = observer(function TreeEntity({ - tree, - store, - onPointerDown, - onPointerUp, -}: { - tree: TreeState; - store: GameStore; - onPointerDown: (e: PointerEvent, id: string) => void; - onPointerUp: (e: PointerEvent) => void; -}) { +const TreeEntity = observer(function TreeEntity({ tree, store }: { tree: TreeState; store: GameStore }) { const isChopping = tree.id === store.heldTreeId && store.isChopping; return ( @@ -44,9 +34,7 @@ const TreeEntity = observer(function TreeEntity({
onPointerDown(e, tree.id)} - onPointerUp={onPointerUp} - onPointerCancel={onPointerUp} + data-tree-id={tree.id} >
@@ -102,35 +90,45 @@ function GameMap({ store }: Props) { }; }, [store]); - function handleTreePointerDown(e: PointerEvent, id: string) { - if (e.button !== 0) - return; - // Stop this from also bubbling to the map's own pointer handlers below (which would - // otherwise interpret the same press as "walk to this empty ground point"). - e.stopPropagation(); - e.currentTarget.setPointerCapture(e.pointerId); - store.holdTree(id); + /** Finds the tree (if any) the pointer is currently over, by DOM hit-testing at its client + * coordinates. */ + function treeIdAtPoint(clientX: number, clientY: number): string | null { + const hit = document.elementFromPoint(clientX, clientY)?.closest('[data-tree-id]'); + return hit?.getAttribute('data-tree-id') ?? null; } - function handleTreePointerUp(e: PointerEvent) { - e.stopPropagation(); - store.release(); - } - - function handleMapPointerDown(e: PointerEvent) { - if (e.button !== 0) + /** The character's action is purely a function of the current pointer state: if the primary + * button isn't held, nothing happens; otherwise it heads for whatever's under the cursor right + * now — a tree to chop, or open ground to walk to. Shared by pointerdown/move/up so panning the + * cursor across a tree mid-drag starts/stops chopping it, and releasing the button stops it. */ + function followPointer(e: PointerEvent) { + if ((e.buttons & 1) === 0) { + store.release(); return; + } + + const treeId = treeIdAtPoint(e.clientX, e.clientY); + if (treeId) { + store.holdTree(treeId); + return; + } + const rect = e.currentTarget.getBoundingClientRect(); - e.currentTarget.setPointerCapture(e.pointerId); store.moveTo(e.clientX - rect.left, e.clientY - rect.top); } + function handlePointerDown(e: PointerEvent) { + e.currentTarget.setPointerCapture(e.pointerId); + followPointer(e); + } + return (
{ready && ( @@ -140,13 +138,7 @@ function GameMap({ store }: Props) { ))} {store.trees.map((tree) => ( - + ))}