|
import * as PIXI from 'pixi.js'; |
|
import { useApp } from '@pixi/react'; |
|
import { Player, SelectElement } from './Player.tsx'; |
|
import { useEffect, useRef, useState } from 'react'; |
|
import { PixiStaticMap } from './PixiStaticMap.tsx'; |
|
import PixiViewport from './PixiViewport.tsx'; |
|
import { Viewport } from 'pixi-viewport'; |
|
import { Id } from '../../convex/_generated/dataModel'; |
|
import { useQuery } from 'convex/react'; |
|
import { api } from '../../convex/_generated/api.js'; |
|
import { useSendInput } from '../hooks/sendInput.ts'; |
|
import { toastOnError } from '../toasts.ts'; |
|
import { DebugPath } from './DebugPath.tsx'; |
|
import { PositionIndicator } from './PositionIndicator.tsx'; |
|
import { SHOW_DEBUG_UI } from './Game.tsx'; |
|
import { ServerGame } from '../hooks/serverGame.ts'; |
|
|
|
export const PixiGame = (props: { |
|
worldId: Id<'worlds'>; |
|
engineId: Id<'engines'>; |
|
game: ServerGame; |
|
historicalTime: number | undefined; |
|
width: number; |
|
height: number; |
|
setSelectedElement: SelectElement; |
|
}) => { |
|
|
|
const pixiApp = useApp(); |
|
const viewportRef = useRef<Viewport | undefined>(); |
|
const oauth = JSON.parse(localStorage.getItem('oauth')); |
|
const oauthToken = oauth ? oauth.userInfo.fullname : undefined; |
|
const humanTokenIdentifier = useQuery(api.world.userStatus, { worldId: props.worldId, oauthToken }) ?? null; |
|
const humanPlayerId = [...props.game.world.players.values()].find( |
|
(p) => p.human === humanTokenIdentifier, |
|
)?.id; |
|
|
|
const moveTo = useSendInput(props.engineId, 'moveTo'); |
|
|
|
|
|
const dragStart = useRef<{ screenX: number; screenY: number } | null>(null); |
|
const onMapPointerDown = (e: any) => { |
|
|
|
dragStart.current = { screenX: e.screenX, screenY: e.screenY }; |
|
}; |
|
|
|
const [lastDestination, setLastDestination] = useState<{ |
|
x: number; |
|
y: number; |
|
t: number; |
|
} | null>(null); |
|
const onMapPointerUp = async (e: any) => { |
|
if (dragStart.current) { |
|
const { screenX, screenY } = dragStart.current; |
|
dragStart.current = null; |
|
const [dx, dy] = [screenX - e.screenX, screenY - e.screenY]; |
|
const dist = Math.sqrt(dx * dx + dy * dy); |
|
if (dist > 10) { |
|
console.log(`Skipping navigation on drag event (${dist}px)`); |
|
return; |
|
} |
|
} |
|
if (!humanPlayerId) { |
|
return; |
|
} |
|
const viewport = viewportRef.current; |
|
if (!viewport) { |
|
return; |
|
} |
|
const gameSpacePx = viewport.toWorld(e.screenX, e.screenY); |
|
const tileDim = props.game.worldMap.tileDim; |
|
const gameSpaceTiles = { |
|
x: gameSpacePx.x / tileDim, |
|
y: gameSpacePx.y / tileDim, |
|
}; |
|
setLastDestination({ t: Date.now(), ...gameSpaceTiles }); |
|
const roundedTiles = { |
|
x: Math.floor(gameSpaceTiles.x), |
|
y: Math.floor(gameSpaceTiles.y), |
|
}; |
|
console.log(`Moving to ${JSON.stringify(roundedTiles)}`); |
|
await toastOnError(moveTo({ playerId: humanPlayerId, destination: roundedTiles })); |
|
}; |
|
const { width, height, tileDim } = props.game.worldMap; |
|
const players = [...props.game.world.players.values()]; |
|
|
|
|
|
useEffect(() => { |
|
if (!viewportRef.current || humanPlayerId === undefined) return; |
|
|
|
const humanPlayer = props.game.world.players.get(humanPlayerId)!; |
|
viewportRef.current.animate({ |
|
position: new PIXI.Point(humanPlayer.position.x * tileDim, humanPlayer.position.y * tileDim), |
|
scale: 1.5, |
|
}); |
|
}, [humanPlayerId]); |
|
|
|
return ( |
|
<PixiViewport |
|
app={pixiApp} |
|
screenWidth={props.width} |
|
screenHeight={props.height} |
|
worldWidth={width * tileDim} |
|
worldHeight={height * tileDim} |
|
viewportRef={viewportRef} |
|
> |
|
<PixiStaticMap |
|
map={props.game.worldMap} |
|
onpointerup={onMapPointerUp} |
|
onpointerdown={onMapPointerDown} |
|
/> |
|
{players.map( |
|
(p) => |
|
// Only show the path for the human player in non-debug mode. |
|
(SHOW_DEBUG_UI || p.id === humanPlayerId) && ( |
|
<DebugPath key={`path-${p.id}`} player={p} tileDim={tileDim} /> |
|
), |
|
)} |
|
{lastDestination && <PositionIndicator destination={lastDestination} tileDim={tileDim} />} |
|
{players.map((p) => ( |
|
<Player |
|
key={`player-${p.id}`} |
|
game={props.game} |
|
player={p} |
|
isViewer={p.id === humanPlayerId} |
|
onClick={props.setSelectedElement} |
|
historicalTime={props.historicalTime} |
|
/> |
|
))} |
|
</PixiViewport> |
|
); |
|
}; |
|
export default PixiGame; |