Spaces:
Sleeping
Sleeping
import { Character } from './Character.tsx'; | |
import { orientationDegrees } from '../../convex/util/geometry.ts'; | |
import { characters } from '../../data/characters.ts'; | |
import { toast } from 'react-toastify'; | |
import { Player as ServerPlayer } from '../../convex/aiTown/player.ts'; | |
import { GameId } from '../../convex/aiTown/ids.ts'; | |
import { Id } from '../../convex/_generated/dataModel'; | |
import { Location, locationFields, playerLocation } from '../../convex/aiTown/location.ts'; | |
import { useHistoricalValue } from '../hooks/useHistoricalValue.ts'; | |
import { PlayerDescription } from '../../convex/aiTown/playerDescription.ts'; | |
import { WorldMap } from '../../convex/aiTown/worldMap.ts'; | |
import { ServerGame } from '../hooks/serverGame.ts'; | |
export type SelectElement = (element?: { kind: 'player'; id: GameId<'players'> }) => void; | |
const logged = new Set<string>(); | |
export const Player = ({ | |
game, | |
isViewer, | |
player, | |
onClick, | |
historicalTime, | |
}: { | |
game: ServerGame; | |
isViewer: boolean; | |
player: ServerPlayer; | |
onClick: SelectElement; | |
historicalTime?: number; | |
}) => { | |
const playerCharacter = game.playerDescriptions.get(player.id)?.character; | |
if (!playerCharacter) { | |
throw new Error(`Player ${player.id} has no character`); | |
} | |
const character = characters.find((c) => c.name === playerCharacter); | |
const locationBuffer = game.world.historicalLocations?.get(player.id); | |
const historicalLocation = useHistoricalValue<Location>( | |
locationFields, | |
historicalTime, | |
playerLocation(player), | |
locationBuffer, | |
); | |
if (!character) { | |
if (!logged.has(playerCharacter)) { | |
logged.add(playerCharacter); | |
toast.error(`Unknown character ${playerCharacter}`); | |
} | |
return null; | |
} | |
if (!historicalLocation) { | |
return null; | |
} | |
const isSpeaking = !![...game.world.conversations.values()].find( | |
(c) => c.isTyping?.playerId === player.id, | |
); | |
const isThinking = | |
!isSpeaking && | |
!![...game.world.agents.values()].find( | |
(a) => a.playerId === player.id && !!a.inProgressOperation, | |
); | |
const tileDim = game.worldMap.tileDim; | |
const historicalFacing = { dx: historicalLocation.dx, dy: historicalLocation.dy }; | |
return ( | |
<> | |
<Character | |
x={historicalLocation.x * tileDim + tileDim / 2} | |
y={historicalLocation.y * tileDim + tileDim / 2} | |
orientation={orientationDegrees(historicalFacing)} | |
isMoving={historicalLocation.speed > 0} | |
isThinking={isThinking} | |
isSpeaking={isSpeaking} | |
emoji={ | |
player.activity && player.activity.until > (historicalTime ?? Date.now()) | |
? player.activity?.emoji | |
: undefined | |
} | |
isViewer={isViewer} | |
textureUrl={character.textureUrl} | |
spritesheetData={character.spritesheetData} | |
speed={character.speed} | |
onClick={() => { | |
onClick({ kind: 'player', id: player.id }); | |
}} | |
/> | |
</> | |
); | |
}; | |