движение за курсором мышки

This commit is contained in:
azykov@mail.ru
2026-09-04 16:27:07 +03:00
parent b8e0d01359
commit e872867c5e
+28 -36
View File
@@ -25,17 +25,7 @@ const LogEntity = observer(function LogEntity({ log }: { log: LogDrop }) {
); );
}); });
const TreeEntity = observer(function TreeEntity({ const TreeEntity = observer(function TreeEntity({ tree, store }: { tree: TreeState; store: GameStore }) {
tree,
store,
onPointerDown,
onPointerUp,
}: {
tree: TreeState;
store: GameStore;
onPointerDown: (e: PointerEvent<HTMLDivElement>, id: string) => void;
onPointerUp: (e: PointerEvent<HTMLDivElement>) => void;
}) {
const isChopping = tree.id === store.heldTreeId && store.isChopping; const isChopping = tree.id === store.heldTreeId && store.isChopping;
return ( return (
@@ -44,9 +34,7 @@ const TreeEntity = observer(function TreeEntity({
<div <div
className={`entity tree ${isChopping ? 'tree--chopping' : ''}`} className={`entity tree ${isChopping ? 'tree--chopping' : ''}`}
style={{ left: tree.x, top: tree.y, zIndex: Math.floor(tree.y) }} style={{ left: tree.x, top: tree.y, zIndex: Math.floor(tree.y) }}
onPointerDown={(e) => onPointerDown(e, tree.id)} data-tree-id={tree.id}
onPointerUp={onPointerUp}
onPointerCancel={onPointerUp}
> >
<PixelSprite pixels={TREE_SPRITE} size={64} /> <PixelSprite pixels={TREE_SPRITE} size={64} />
</div> </div>
@@ -102,35 +90,45 @@ function GameMap({ store }: Props) {
}; };
}, [store]); }, [store]);
function handleTreePointerDown(e: PointerEvent<HTMLDivElement>, id: string) { /** Finds the tree (if any) the pointer is currently over, by DOM hit-testing at its client
if (e.button !== 0) * coordinates. */
return; function treeIdAtPoint(clientX: number, clientY: number): string | null {
// Stop this from also bubbling to the map's own pointer handlers below (which would const hit = document.elementFromPoint(clientX, clientY)?.closest('[data-tree-id]');
// otherwise interpret the same press as "walk to this empty ground point"). return hit?.getAttribute('data-tree-id') ?? null;
e.stopPropagation();
e.currentTarget.setPointerCapture(e.pointerId);
store.holdTree(id);
} }
function handleTreePointerUp(e: PointerEvent<HTMLDivElement>) { /** The character's action is purely a function of the current pointer state: if the primary
e.stopPropagation(); * 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<HTMLDivElement>) {
if ((e.buttons & 1) === 0) {
store.release(); store.release();
return;
} }
function handleMapPointerDown(e: PointerEvent<HTMLDivElement>) { const treeId = treeIdAtPoint(e.clientX, e.clientY);
if (e.button !== 0) if (treeId) {
store.holdTree(treeId);
return; return;
}
const rect = e.currentTarget.getBoundingClientRect(); const rect = e.currentTarget.getBoundingClientRect();
e.currentTarget.setPointerCapture(e.pointerId);
store.moveTo(e.clientX - rect.left, e.clientY - rect.top); store.moveTo(e.clientX - rect.left, e.clientY - rect.top);
} }
function handlePointerDown(e: PointerEvent<HTMLDivElement>) {
e.currentTarget.setPointerCapture(e.pointerId);
followPointer(e);
}
return ( return (
<div <div
className="game-map" className="game-map"
ref={containerRef} ref={containerRef}
onPointerDown={handleMapPointerDown} onPointerDown={handlePointerDown}
onPointerUp={store.release} onPointerMove={followPointer}
onPointerUp={followPointer}
onPointerCancel={store.release} onPointerCancel={store.release}
> >
{ready && ( {ready && (
@@ -140,13 +138,7 @@ function GameMap({ store }: Props) {
))} ))}
{store.trees.map((tree) => ( {store.trees.map((tree) => (
<TreeEntity <TreeEntity key={tree.id} tree={tree} store={store} />
key={tree.id}
tree={tree}
store={store}
onPointerDown={handleTreePointerDown}
onPointerUp={handleTreePointerUp}
/>
))} ))}
<CharacterEntity store={store} /> <CharacterEntity store={store} />