Skip to content

Commit

Permalink
add a dialog for prompts with action menu
Browse files Browse the repository at this point in the history
  • Loading branch information
ssube committed Jun 2, 2024
1 parent fc61cae commit 6431609
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 44 deletions.
26 changes: 19 additions & 7 deletions client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import { PlayerPanel } from './player.js';
import { Statusbar } from './status.js';
import { StoreState, store } from './store.js';
import { WorldPanel } from './world.js';
import { DetailDialog } from './details.js';
import { PromptDialog } from './prompt.js';

import 'allotment/dist/style.css';
import { DetailDialog } from './details.js';
import './main.css';

const useWebSocket = (useWebSocketModule as any).default;
Expand Down Expand Up @@ -63,13 +64,18 @@ export function App(props: AppProps) {
}

function sendInput(input: string) {
const { playerCharacter: character, setActiveTurn } = store.getState();
const { playerCharacter: character, setPromptEvent } = store.getState();
if (doesExist(character)) {
sendMessage(JSON.stringify({ type: 'input', input }));
setActiveTurn(false);
setPromptEvent(undefined);
}
}

function skipPrompt() {
const { setPromptEvent } = store.getState();
setPromptEvent(undefined);
}

function setName(name: string) {
const { setClientName } = store.getState();
sendMessage(JSON.stringify({ type: 'player', name }));
Expand All @@ -83,7 +89,7 @@ export function App(props: AppProps) {
});

useEffect(() => {
const { setClientId, setActiveTurn, setPlayers, appendEvent, setTurn, setWorld, world, clientId, setPlayerCharacter: setCharacter } = store.getState();
const { setClientId, setPromptEvent, setPlayers, appendEvent, setTurn, setWorld, world, clientId, setPlayerCharacter: setCharacter } = store.getState();
if (doesExist(lastMessage)) {
const event = JSON.parse(lastMessage.data);

Expand All @@ -95,8 +101,13 @@ export function App(props: AppProps) {
return;
case 'prompt':
// prompts are broadcast to all players
// only notify the active player
setActiveTurn(event.client === clientId);
if (event.client === clientId) {
// only notify the active player
setPromptEvent(event);
} else {
// if it has moved on to another player, clear the prompt
setPromptEvent(undefined);
}
break;
case 'player':
if (doesExist(world) && event.client === clientId) {
Expand Down Expand Up @@ -155,13 +166,14 @@ export function App(props: AppProps) {
return <ThemeProvider theme={theme}>
<CssBaseline />
<DetailDialog renderEntity={renderEntity} />
<PromptDialog sendInput={sendInput} skipPrompt={skipPrompt} />
<Container maxWidth='xl'>
<Stack direction="column">
<Statusbar setName={setName} />
<Stack direction="row" spacing={2}>
{innerLayout(
<Stack direction="column" spacing={2} sx={{ minWidth: leftWidth }} className="scroll-history">
<PlayerPanel sendInput={sendInput} />
<PlayerPanel />
<WorldPanel setPlayer={setPlayer} />
</Stack>,
<Stack direction="column" sx={{ minWidth: rightWidth }} className="scroll-history">
Expand Down
8 changes: 8 additions & 0 deletions client/src/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ export function WorldDetails(props: WorldDetailsProps) {
<Typography variant='body2'>
Theme: {world.theme}
</Typography>
<Typography variant='body2'>
Order:
</Typography>
<ol>
{world.order.map((name) => (
<li key={name}>{name}</li>
))}
</ol>
<div id="graph" />
</DialogContent>
</Fragment>;
Expand Down
36 changes: 36 additions & 0 deletions client/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,43 @@ export interface World {
turn: number;
}

//

export interface StringParameter {
type: 'string';
default?: string;
enum?: Array<string>;
}

export interface NumberParameter {
type: 'number';
default?: string;
enum?: Array<string>;
}

export type Parameter = NumberParameter | StringParameter;

export interface Action {
type: 'function';
function: {
name: string;
description: string;
parameters: {
type: 'object';
properties: Record<string, Parameter>;
};
};
}

// TODO: copy event types from server
export interface GameEvent {
type: string;
}

export interface PromptEvent {
type: 'prompt';
prompt: string;
actions: Array<Action>;
character: Character;
room: Room;
}
37 changes: 6 additions & 31 deletions client/src/player.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,27 @@
import { doesExist } from '@apextoaster/js-utils';
import { Alert, Button, Card, CardContent, Stack, TextField, Typography } from '@mui/material';
import React, { useState } from 'react';
import { Alert, Card, CardContent, Stack, Typography } from '@mui/material';
import React from 'react';
import { useStore } from 'zustand';
import { store, StoreState } from './store';

export interface PlayerPanelProps {
sendInput: (input: string) => void;
}

export function playerStateSelector(s: StoreState) {
return {
character: s.playerCharacter,
activeTurn: s.activeTurn,
promptEvent: s.promptEvent,
};
}

export function PlayerPanel(props: PlayerPanelProps) {
export function PlayerPanel() {
const state = useStore(store, playerStateSelector);
const { character, activeTurn } = state;
const { sendInput } = props;
const [input, setInput] = useState<string>('');
const { character, promptEvent } = state;

if (doesExist(character)) {
return <Card style={{ minHeight: '6vh', overflow: 'auto' }}>
<CardContent>
<Stack direction="column" spacing={2}>
{activeTurn && <Alert severity="warning">It's your turn!</Alert>}
{doesExist(promptEvent) && <Alert severity="warning">It's your turn!</Alert>}
<Typography variant="h6">Playing as: {character.name}</Typography>
<Typography variant="body1">{character.backstory}</Typography>
<Stack direction="row" spacing={2}>
<TextField
fullWidth
label="Input"
variant="outlined"
value={input}
onChange={(event) => setInput(event.target.value)}
onKeyDown={(event) => {
if (event.key === 'Enter') {
sendInput(input);
setInput('');
}
}}
/>
<Button variant="contained" onClick={() => {
sendInput(input);
setInput('');
}}>Send</Button>
</Stack>
</Stack>
</CardContent>
</Card>;
Expand Down
Loading

0 comments on commit 6431609

Please sign in to comment.