import { useRef, useState } from 'react'; import PixiGame from './PixiGame.tsx'; import { useElementSize } from 'usehooks-ts'; import { Stage } from '@pixi/react'; import { ConvexProvider, useConvex, useQuery } from 'convex/react'; import PlayerDetails from './PlayerDetails.tsx'; import { api } from '../../convex/_generated/api'; import { useWorldHeartbeat } from '../hooks/useWorldHeartbeat.ts'; import { useHistoricalTime } from '../hooks/useHistoricalTime.ts'; import { DebugTimeManager } from './DebugTimeManager.tsx'; import { GameId } from '../../convex/aiTown/ids.ts'; import { Game as GameObj} from '../../convex/aiTown/game.ts'; import { ServerGame, useServerGame } from '../hooks/serverGame.ts'; import { GameCycle } from '../../convex/aiTown/gameCycle.ts'; import { PlayerDescription } from '../../convex/aiTown/playerDescription.ts'; import { Cloud } from './Cloud.tsx'; import VotingPopover from './LLMVote.tsx'; import { createPortal } from 'react-dom'; import GameVote from './GameVote.tsx'; import { EndGame } from './EndGame.tsx'; export const SHOW_DEBUG_UI = !!import.meta.env.VITE_SHOW_DEBUG_UI; export function GameStateLabel(game: GameObj, me: PlayerDescription | undefined) { switch (game.world.gameCycle.cycleState) { case 'Day': return { label: 'Day', desc: 'Find out who is a werewolf', }; case 'WerewolfVoting': return { label: 'Werewolf Vote', desc: 'Select a player who is a warewolf', }; case 'PlayerKillVoting': return { label: 'Player Kill Vote', desc: me?.type === 'werewolf' ? 'Select a player to kill' : 'Hide in your home!!', }; case 'LobbyState': return { label: 'Lobby (waiting for start)', desc: 'Waiting for the game to start', }; case 'Night': return { label: 'Night', desc: me?.type === 'werewolf' ? 'Discuss who to kill with other warewolves' : 'Hide in your home!!', }; case 'EndGame': return { label: 'The End', desc: `Winners are ${ game.world.winner }!`, }; } } export function canVote(game: ServerGame, me: PlayerDescription | undefined) { return me && (game.world.gameCycle.cycleState === "WerewolfVoting" || (game.world.gameCycle.cycleState === "PlayerKillVoting" && me.type === "werewolf")); } export function isEndGame(game: ServerGame) { return game.world.gameCycle.cycleState === "EndGame"; } function showMap(gameCycle: GameCycle, me: PlayerDescription | undefined) { // Here also check for player description return (gameCycle.cycleState === "Day" || gameCycle.cycleState === "WerewolfVoting") || me?.type === "werewolf"; } export default function Game() { const convex = useConvex(); const [selectedElement, setSelectedElement] = useState<{ kind: 'player'; id: GameId<'players'>; }>(); const [gameWrapperRef, { width, height }] = useElementSize(); const worldStatus = useQuery(api.world.defaultWorldStatus); const worldId = worldStatus?.worldId; const engineId = worldStatus?.engineId; const game = useServerGame(worldId); // Send a periodic heartbeat to our world to keep it alive. useWorldHeartbeat(); const worldState = useQuery(api.world.worldState, worldId ? { worldId } : 'skip'); const { historicalTime, timeManager } = useHistoricalTime(worldState?.engine); const scrollViewRef = useRef(null); const humanTokenIdentifier = useQuery(api.world.userStatus, worldId ? { worldId } : 'skip'); if (!worldId || !engineId || !game ) { return null; } const playerId = [...game.world.players.values()].find( (p) => p.human === humanTokenIdentifier, )?.id; const meDescription = playerId ? game?.playerDescriptions.get(playerId) : undefined; return ( <> {SHOW_DEBUG_UI && }
{/* Game area */}
{/* Re-propagate context because contexts are not shared between renderers. https://github.com/michalochman/react-pixi-fiber/issues/145#issuecomment-531549215 */}
{/* Right column area */}

{GameStateLabel(game as GameObj, meDescription).label}

{GameStateLabel(game as GameObj, meDescription).desc}

{playerId && !isEndGame(game) && canVote(game, meDescription) && } {!isEndGame(game) && !canVote(game, meDescription) && } {playerId && isEndGame(game) && }
{createPortal(
{playerId && }
, document.getElementById('footer-buttons') )} ); }