You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
6.4 KiB
128 lines
6.4 KiB
import React, {useEffect, useMemo, useState} from 'react';
|
|
import './App.css';
|
|
import {Col, Container, Row, Stack} from "react-bootstrap";
|
|
import {CharacterStatus} from "./CharacterStatus";
|
|
import {GameState} from "../model/GameState";
|
|
import OverlayTrigger from "react-bootstrap/OverlayTrigger";
|
|
import Tooltip from "react-bootstrap/Tooltip";
|
|
import {TurnTimer} from "./TurnTimer";
|
|
import {CharacterSide} from "../model/Character";
|
|
import {evaluate} from "../grammar/interpreter";
|
|
|
|
function useJson<T>(url: string): T | null {
|
|
const [data, setData] = useState<T | null>(null);
|
|
useEffect(() => {
|
|
let ignore = false;
|
|
fetch(url)
|
|
.then(response => response.json() as T)
|
|
.then(json => {
|
|
if (!ignore) {
|
|
setData(json);
|
|
}
|
|
});
|
|
return () => {
|
|
ignore = true;
|
|
};
|
|
}, [url]);
|
|
return data as T | null;
|
|
}
|
|
|
|
function App() {
|
|
const origState = useJson<GameState>("/api/current.json")
|
|
const state = useMemo(() => {
|
|
try {
|
|
if (origState !== null) {
|
|
return evaluate(origState, Date.now(),
|
|
String.raw`Begin &echo @prandia: Lunch
|
|
&: 69/420 MP
|
|
&: -20 MP
|
|
@ HP: 35/70
|
|
@ HP: -30... water
|
|
@: ZP 5/6
|
|
@: ZP +1
|
|
&: 20 HP
|
|
gale, calor,gravitas HP: 3
|
|
&: ZP 5
|
|
&: ZP /7
|
|
&: /99 IP
|
|
& HP: /69
|
|
aelica IP: -
|
|
athetyz: - MP
|
|
linnet: HP -
|
|
&: Lv -
|
|
End`)
|
|
}
|
|
} catch (ex) {
|
|
console.log(ex)
|
|
}
|
|
}, [origState])
|
|
const startTime = Date.now()
|
|
const endTime = Date.now() + 2 * 60 * 1000
|
|
return <React.Fragment>
|
|
<Container fluid>
|
|
<Row>
|
|
<Col xs={{span: false}} xxl={{span: true}}>
|
|
<Stack direction="vertical">
|
|
<h1 className={"session-head"}>Session</h1>
|
|
<Row><Col xs={{span: true}} xxl={{span: false}}>
|
|
<Stack direction="horizontal">
|
|
<OverlayTrigger delay={{show: 750, hide: 0}} trigger={["hover", "click", "focus"]} overlay={
|
|
<Tooltip>
|
|
<div className={"appHelpHeader"}>
|
|
<span className={"appHelpName"}>Fabula Points spent</span>
|
|
<span className={"appHelpValue"}>{state?.session.usedSp.Fabula ?? 0}</span></div>
|
|
<div className={"appHelpDescription"}>
|
|
The party earns 1 EXP for each (#-players) Fabula Points spent during the session.
|
|
</div>
|
|
</Tooltip>
|
|
} placement={"right"}>
|
|
<div className={"totalFPSpent"}>{state?.session.usedSp.Fabula ?? 0}</div>
|
|
</OverlayTrigger>
|
|
<div className={"mx-auto totalSPSpent"}>Points Spent</div>
|
|
<OverlayTrigger delay={{show: 750, hide: 0}} trigger={["hover", "click", "focus"]} overlay={
|
|
<Tooltip>
|
|
<div className={"appHelpHeader"}>
|
|
<span className={"appHelpName"}>Ultima Points spent</span>
|
|
<span className={"appHelpValue"}>{state?.session.usedSp.Ultima ?? 0}</span></div>
|
|
<div className={"appHelpDescription"}>
|
|
The party earns 1 EXP for each Ultima Point spent during the session.
|
|
</div>
|
|
</Tooltip>
|
|
} placement={"left"}>
|
|
<div className={"totalUPSpent"}>{state?.session.usedSp.Ultima ?? 0}</div>
|
|
</OverlayTrigger>
|
|
</Stack>
|
|
</Col>
|
|
<Col xs={{span: true}} xxl={{span: false}}>
|
|
<TurnTimer title={"Timer"} startTime={startTime} endTime={endTime} />
|
|
</Col></Row>
|
|
</Stack>
|
|
</Col>
|
|
<Col xs={{span: true}} xxl={{span: true, order: "first"}}>
|
|
<Stack direction="vertical" className={"align-items-center"}>
|
|
<h1 className={"ally-head " + (state?.conflict?.activeSide === CharacterSide.Ally ? "active" : state?.conflict?.activeSide === CharacterSide.Enemy ? "inactive" : "")}>Allies</h1>
|
|
{state && state.characters.filter((character) => character.side === CharacterSide.Ally).map((character) =>
|
|
<CharacterStatus
|
|
key={character.id}
|
|
character={character}
|
|
statuses={state.statuses}
|
|
active={character.id === state.conflict?.activeCharacterId} />)}
|
|
</Stack>
|
|
</Col>
|
|
<Col xs={{span: true}} xxl={{span: true, order: "last"}}>
|
|
<Stack direction="vertical" className={"align-items-center"}>
|
|
<h1 className={"enemy-head " + (state?.conflict?.activeSide === CharacterSide.Enemy ? "active" : state?.conflict?.activeSide === CharacterSide.Ally ? "inactive" : "")}>Enemies</h1>
|
|
{state && state.characters.filter((character) => character.side === CharacterSide.Enemy).map((character) =>
|
|
<CharacterStatus
|
|
key={character.id}
|
|
character={character}
|
|
statuses={state.statuses}
|
|
active={character.id === state.conflict?.activeCharacterId} />)}
|
|
</Stack>
|
|
</Col>
|
|
</Row>
|
|
</Container>
|
|
</React.Fragment>;
|
|
}
|
|
|
|
export default App;
|
|
|