1
0
Fork 0
taleweave-ai/client/src/world.tsx

122 lines
4.0 KiB
TypeScript
Raw Normal View History

import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
import { TreeItem } from '@mui/x-tree-view/TreeItem';
import { Card, CardContent, CardHeader, Stack, Typography } from '@mui/material';
import { Maybe, doesExist } from '@apextoaster/js-utils';
import React from 'react';
export interface Item {
name: string;
description: string;
}
export interface Actor {
name: string;
backstory: string;
description: string;
items: Array<Item>;
}
export interface Room {
name: string;
description: string;
portals: Record<string, string>;
actors: Array<Actor>;
items: Array<Item>;
}
export interface World {
name: string;
order: Array<string>;
rooms: Array<Room>;
theme: string;
}
export type SetDetails = (entity: Maybe<Item | Actor | Room>) => void;
export type SetPlayer = (actor: Maybe<Actor>) => void;
export interface BaseEntityItemProps {
activeCharacter: Maybe<Actor>;
setDetails: SetDetails;
setPlayer: SetPlayer;
}
export function formatLabel(name: string, active = false): string {
if (active) {
return `${name} (!)`;
}
return name;
}
export function ItemItem(props: { item: Item } & BaseEntityItemProps) {
const { item, setDetails } = props;
return <TreeItem itemId={item.name} label={item.name}>
<TreeItem itemId={`${item.name}-details`} label="Details" onClick={() => setDetails(item)} />
</TreeItem>;
}
export function ActorItem(props: { actor: Actor } & BaseEntityItemProps) {
const { actor, activeCharacter, setDetails, setPlayer } = props;
const active = doesExist(activeCharacter) && actor.name === activeCharacter.name;
const label = formatLabel(actor.name, active);
let playButton;
if (active === false) {
playButton = <TreeItem itemId={`${actor.name}-play`} label="Play!" onClick={() => setPlayer(actor)} />;
}
return <TreeItem itemId={actor.name} label={label}>
{playButton}
<TreeItem itemId={`${actor.name}-details`} label="Details" onClick={() => setDetails(actor)} />
<TreeItem itemId={`${actor.name}-items`} label="Items">
{actor.items.map((item) => <ItemItem key={item.name} item={item} activeCharacter={activeCharacter} setDetails={setDetails} setPlayer={setPlayer} />)}
</TreeItem>
</TreeItem>;
}
export function RoomItem(props: { room: Room } & BaseEntityItemProps) {
const { room, activeCharacter, setDetails, setPlayer } = props;
const active = doesExist(activeCharacter) && room.actors.some((it) => it.name === activeCharacter.name);
const label = formatLabel(room.name, active);
return <TreeItem itemId={room.name} label={label}>
<TreeItem itemId={`${room.name}-details`} label="Details" onClick={() => setDetails(room)} />
<TreeItem itemId={`${room.name}-actors`} label="Actors">
{room.actors.map((actor) => <ActorItem key={actor.name} actor={actor} activeCharacter={activeCharacter} setDetails={setDetails} setPlayer={setPlayer} />)}
</TreeItem>
<TreeItem itemId={`${room.name}-items`} label="Items">
{room.items.map((item) => <ItemItem key={item.name} item={item} activeCharacter={activeCharacter} setDetails={setDetails} setPlayer={setPlayer} />)}
</TreeItem>
</TreeItem>;
}
export function WorldPanel(props: { world: Maybe<World> } & BaseEntityItemProps) {
const { world, activeCharacter, setDetails, setPlayer } = props;
// eslint-disable-next-line no-restricted-syntax
if (!doesExist(world)) {
return <Card style={{ minHeight: '6vh', overflow: 'auto' }}>
<CardContent>
<Typography variant="h6">
No world data available
</Typography>
</CardContent>
</Card>;
}
return <Card style={{ minHeight: 200, overflow: 'auto' }}>
<CardContent>
<Typography gutterBottom variant="h5" component="div">{world.name}</Typography>
<Typography variant="body1">
Theme: {world.theme}
</Typography>
<SimpleTreeView>
{world.rooms.map((room) => <RoomItem key={room.name} room={room} activeCharacter={activeCharacter} setDetails={setDetails} setPlayer={setPlayer} />)}
</SimpleTreeView>
</CardContent>
</Card>;
}