2024-05-11 22:38:07 +00:00
|
|
|
import { Maybe, doesExist } from '@apextoaster/js-utils';
|
|
|
|
import { Card, CardContent, Typography } from '@mui/material';
|
2024-05-05 18:54:39 +00:00
|
|
|
import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView';
|
|
|
|
import { TreeItem } from '@mui/x-tree-view/TreeItem';
|
|
|
|
import React from 'react';
|
|
|
|
|
2024-05-11 22:38:07 +00:00
|
|
|
import { useStore } from 'zustand';
|
|
|
|
import { StoreState, store } from './store';
|
2024-05-12 20:47:18 +00:00
|
|
|
import { Actor, Item, Room } from './models';
|
2024-05-05 18:54:39 +00:00
|
|
|
|
|
|
|
export type SetDetails = (entity: Maybe<Item | Actor | Room>) => void;
|
|
|
|
export type SetPlayer = (actor: Maybe<Actor>) => void;
|
|
|
|
|
|
|
|
export interface BaseEntityItemProps {
|
|
|
|
setPlayer: SetPlayer;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function formatLabel(name: string, active = false): string {
|
|
|
|
if (active) {
|
|
|
|
return `${name} (!)`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2024-05-11 22:38:07 +00:00
|
|
|
export function itemStateSelector(s: StoreState) {
|
|
|
|
return {
|
|
|
|
character: s.character,
|
|
|
|
setDetailEntity: s.setDetailEntity,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-05-18 22:48:40 +00:00
|
|
|
export function actorStateSelector(s: StoreState) {
|
|
|
|
return {
|
|
|
|
character: s.character,
|
|
|
|
players: s.players,
|
|
|
|
setDetailEntity: s.setDetailEntity,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-05-11 22:38:07 +00:00
|
|
|
export function worldStateSelector(s: StoreState) {
|
|
|
|
return {
|
|
|
|
world: s.world,
|
2024-05-12 20:47:18 +00:00
|
|
|
setDetailEntity: s.setDetailEntity,
|
2024-05-11 22:38:07 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-05-05 18:54:39 +00:00
|
|
|
export function ItemItem(props: { item: Item } & BaseEntityItemProps) {
|
2024-05-11 22:38:07 +00:00
|
|
|
const { item } = props;
|
|
|
|
const state = useStore(store, itemStateSelector);
|
|
|
|
const { setDetailEntity } = state;
|
2024-05-05 18:54:39 +00:00
|
|
|
|
|
|
|
return <TreeItem itemId={item.name} label={item.name}>
|
2024-05-11 22:38:07 +00:00
|
|
|
<TreeItem itemId={`${item.name}-details`} label="Details" onClick={() => setDetailEntity(item)} />
|
2024-05-05 18:54:39 +00:00
|
|
|
</TreeItem>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function ActorItem(props: { actor: Actor } & BaseEntityItemProps) {
|
2024-05-11 22:38:07 +00:00
|
|
|
const { actor, setPlayer } = props;
|
2024-05-18 22:48:40 +00:00
|
|
|
const state = useStore(store, actorStateSelector);
|
|
|
|
const { character, players, setDetailEntity } = state;
|
2024-05-05 18:54:39 +00:00
|
|
|
|
2024-05-18 22:48:40 +00:00
|
|
|
const activeSelf = doesExist(character) && actor.name === character.name;
|
|
|
|
const activeOther = Object.values(players).some((it) => it === actor.name); // TODO: are these the keys or the values?
|
|
|
|
const label = formatLabel(actor.name, activeSelf);
|
2024-05-05 18:54:39 +00:00
|
|
|
|
|
|
|
let playButton;
|
2024-05-18 22:48:40 +00:00
|
|
|
if (activeSelf) {
|
|
|
|
playButton = <TreeItem itemId={`${actor.name}-stop`} label="Stop playing" onClick={() => setPlayer(undefined)} />;
|
|
|
|
} else {
|
|
|
|
if (activeOther) {
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
|
|
const player = Object.entries(players).find((it) => it[1] === actor.name)?.[0];
|
|
|
|
playButton = <TreeItem itemId={`${actor.name}-taken`} label={`Played by ${player}`} />;
|
|
|
|
} else {
|
|
|
|
playButton = <TreeItem itemId={`${actor.name}-play`} label="Play!" onClick={() => setPlayer(actor)} />;
|
|
|
|
}
|
2024-05-05 18:54:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return <TreeItem itemId={actor.name} label={label}>
|
|
|
|
{playButton}
|
2024-05-11 22:38:07 +00:00
|
|
|
<TreeItem itemId={`${actor.name}-details`} label="Details" onClick={() => setDetailEntity(actor)} />
|
2024-05-05 18:54:39 +00:00
|
|
|
<TreeItem itemId={`${actor.name}-items`} label="Items">
|
2024-05-11 22:38:07 +00:00
|
|
|
{actor.items.map((item) => <ItemItem key={item.name} item={item} setPlayer={setPlayer} />)}
|
2024-05-05 18:54:39 +00:00
|
|
|
</TreeItem>
|
|
|
|
</TreeItem>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function RoomItem(props: { room: Room } & BaseEntityItemProps) {
|
2024-05-11 22:38:07 +00:00
|
|
|
const { room, setPlayer } = props;
|
|
|
|
const state = useStore(store, itemStateSelector);
|
|
|
|
const { character, setDetailEntity } = state;
|
2024-05-05 18:54:39 +00:00
|
|
|
|
2024-05-11 22:38:07 +00:00
|
|
|
const active = doesExist(character) && room.actors.some((it) => it.name === character.name);
|
2024-05-05 18:54:39 +00:00
|
|
|
const label = formatLabel(room.name, active);
|
|
|
|
|
|
|
|
return <TreeItem itemId={room.name} label={label}>
|
2024-05-11 22:38:07 +00:00
|
|
|
<TreeItem itemId={`${room.name}-details`} label="Details" onClick={() => setDetailEntity(room)} />
|
2024-05-05 18:54:39 +00:00
|
|
|
<TreeItem itemId={`${room.name}-actors`} label="Actors">
|
2024-05-11 22:38:07 +00:00
|
|
|
{room.actors.map((actor) => <ActorItem key={actor.name} actor={actor} setPlayer={setPlayer} />)}
|
2024-05-05 18:54:39 +00:00
|
|
|
</TreeItem>
|
|
|
|
<TreeItem itemId={`${room.name}-items`} label="Items">
|
2024-05-11 22:38:07 +00:00
|
|
|
{room.items.map((item) => <ItemItem key={item.name} item={item} setPlayer={setPlayer} />)}
|
2024-05-05 18:54:39 +00:00
|
|
|
</TreeItem>
|
|
|
|
</TreeItem>;
|
|
|
|
}
|
|
|
|
|
2024-05-11 22:38:07 +00:00
|
|
|
export function WorldPanel(props: BaseEntityItemProps) {
|
|
|
|
const { setPlayer } = props;
|
|
|
|
const state = useStore(store, worldStateSelector);
|
2024-05-12 20:47:18 +00:00
|
|
|
const { world, setDetailEntity } = state;
|
2024-05-05 18:54:39 +00:00
|
|
|
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
|
|
if (!doesExist(world)) {
|
2024-05-06 01:17:00 +00:00
|
|
|
return <Card style={{ minHeight: '6vh', overflow: 'auto' }}>
|
|
|
|
<CardContent>
|
|
|
|
<Typography variant="h6">
|
|
|
|
No world data available
|
|
|
|
</Typography>
|
|
|
|
</CardContent>
|
|
|
|
</Card>;
|
2024-05-05 18:54:39 +00:00
|
|
|
}
|
|
|
|
|
2024-05-06 01:17:00 +00:00
|
|
|
return <Card style={{ minHeight: 200, overflow: 'auto' }}>
|
2024-05-05 18:54:39 +00:00
|
|
|
<CardContent>
|
|
|
|
<Typography gutterBottom variant="h5" component="div">{world.name}</Typography>
|
|
|
|
<Typography variant="body1">
|
|
|
|
Theme: {world.theme}
|
|
|
|
</Typography>
|
|
|
|
<SimpleTreeView>
|
2024-05-12 20:47:18 +00:00
|
|
|
<TreeItem itemId="world-graph" label="Graph" onClick={() => setDetailEntity(world)} />
|
2024-05-11 22:38:07 +00:00
|
|
|
{world.rooms.map((room) => <RoomItem key={room.name} room={room} setPlayer={setPlayer} />)}
|
2024-05-05 18:54:39 +00:00
|
|
|
</SimpleTreeView>
|
|
|
|
</CardContent>
|
|
|
|
</Card>;
|
|
|
|
}
|