Create a renderer for Candela Obscura character sheets #275
|
@ -7,6 +7,7 @@ use std::{
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use typeshare::typeshare;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -32,6 +33,7 @@ impl From<std::io::Error> for Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
#[typeshare]
|
||||||
pub struct AssetId(String);
|
pub struct AssetId(String);
|
||||||
|
|
||||||
impl Display for AssetId {
|
impl Display for AssetId {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
.App {
|
.App {
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-logo {
|
.App-logo {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||||
import { GmView } from './views/GmView/GmView';
|
import { GmView } from './views/GmView/GmView';
|
||||||
import { WebsocketProvider } from './components/WebsocketProvider';
|
import { WebsocketProvider } from './components/WebsocketProvider';
|
||||||
import { PlayerView } from './views/PlayerView/PlayerView';
|
import { PlayerView } from './views/PlayerView/PlayerView';
|
||||||
|
import Candela from './plugins/Candela';
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
client: Client;
|
client: Client;
|
||||||
|
@ -27,6 +28,10 @@ const App = ({ client }: AppProps) => {
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <PlayerView client={client} /> </WebsocketProvider> : <div> </div>
|
element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <PlayerView client={client} /> </WebsocketProvider> : <div> </div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/candela",
|
||||||
|
element: <Candela.CharsheetElement />
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
.charsheet__header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charsheet__header > div {
|
||||||
|
margin: 8px;
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charsheet__body {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charsheet__body > div {
|
||||||
|
margin: 8px;
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
import React from 'react';
|
||||||
|
import './Charsheet.css';
|
||||||
|
|
||||||
|
export type Guage = {
|
||||||
|
current: number,
|
||||||
|
max: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Action = {
|
||||||
|
guilded: boolean,
|
||||||
|
score: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Actions = { [key: string]: Action }
|
||||||
|
|
||||||
|
export type ActionGroup = {
|
||||||
|
drives: Guage,
|
||||||
|
resistances: Guage,
|
||||||
|
actions: Actions,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Nerve = {
|
||||||
|
drives: Guage,
|
||||||
|
resistances: Guage,
|
||||||
|
move: Action,
|
||||||
|
strike: Action,
|
||||||
|
control: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cunning = {
|
||||||
|
drives: Guage,
|
||||||
|
resistances: Guage,
|
||||||
|
sway: Action,
|
||||||
|
read: Action,
|
||||||
|
hide: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Intuition = {
|
||||||
|
drives: Guage,
|
||||||
|
resistances: Guage,
|
||||||
|
survey: Action,
|
||||||
|
focus: Action,
|
||||||
|
sense: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Charsheet = {
|
||||||
|
type_: string,
|
||||||
|
name: string,
|
||||||
|
pronouns: string
|
||||||
|
circle: string
|
||||||
|
style: string,
|
||||||
|
catalyst: string,
|
||||||
|
question: string,
|
||||||
|
role: string,
|
||||||
|
|
||||||
|
nerve: Nerve,
|
||||||
|
cunning: Cunning,
|
||||||
|
intuition: Intuition,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CharsheetProps {
|
||||||
|
sheet: Charsheet,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActionDriveProps {
|
||||||
|
groupName: string,
|
||||||
|
group: Nerve | Cunning | Intuition,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GuageProps {
|
||||||
|
current: number,
|
||||||
|
max: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
const GuageElement = ({ current, max }: GuageProps) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionDriveElement = ({ groupName, group }: ActionDriveProps) => {
|
||||||
|
|
||||||
|
if ("move" in group) {
|
||||||
|
return <div> -- Nerve -- </div>;
|
||||||
|
} else if ("sway" in group) {
|
||||||
|
return <div> -- Cunning -- </div>;
|
||||||
|
} else {
|
||||||
|
return <div> -- Intuition -- </div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CharsheetElement_ = ({ sheet }: CharsheetProps) => {
|
||||||
|
return (<div>
|
||||||
|
<div className="charsheet__header">
|
||||||
|
<div> Candela Obscura </div>
|
||||||
|
<div>
|
||||||
|
<p> {sheet.name} </p>
|
||||||
|
<p> {sheet.pronouns} </p>
|
||||||
|
<p> {sheet.circle} </p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p> {sheet.style} </p>
|
||||||
|
<p> {sheet.catalyst} </p>
|
||||||
|
<p> {sheet.question} </p>
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
<div className="charsheet__body">
|
||||||
|
<div>
|
||||||
|
<ActionDriveElement groupName="Nerve" group={sheet.nerve} />
|
||||||
|
<ActionDriveElement groupName="Cunning" group={sheet.cunning} />
|
||||||
|
<ActionDriveElement groupName="Intuition" group={sheet.intuition} />
|
||||||
|
</div>
|
||||||
|
<div> Role and Specialty </div>
|
||||||
|
<div> Marks, Scars, Relationships </div>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CharsheetElement = () => {
|
||||||
|
const sheet = {
|
||||||
|
type_: 'Candela',
|
||||||
|
name: "Soren Jensen",
|
||||||
|
pronouns: 'he/him',
|
||||||
|
circle: 'Circle of the Bluest Sky',
|
||||||
|
style: 'dapper gentleman',
|
||||||
|
catalyst: 'a cursed book',
|
||||||
|
question: 'What were the contents of that book?',
|
||||||
|
role: 'Slink',
|
||||||
|
nerve: {
|
||||||
|
drives: { current: 2, max: 2 },
|
||||||
|
resistances: { current: 0, max: 3 },
|
||||||
|
move: { guilded: false, score: 0 },
|
||||||
|
strike: { guilded: false, score: 0 },
|
||||||
|
control: { guilded: true, score: 0 },
|
||||||
|
},
|
||||||
|
cunning: {
|
||||||
|
drives: { current: 1, max: 1 },
|
||||||
|
resistances: { current: 0, max: 3 },
|
||||||
|
sway: { guilded: false, score: 0 },
|
||||||
|
read: { guilded: false, score: 0 },
|
||||||
|
hide: { guilded: false, score: 0 },
|
||||||
|
},
|
||||||
|
intuition: {
|
||||||
|
drives: { current: 0, max: 0 },
|
||||||
|
resistances: { current: 0, max: 3 },
|
||||||
|
survey: { guilded: false, score: 0 },
|
||||||
|
focus: { guilded: false, score: 0 },
|
||||||
|
sense: { guilded: false, score: 0 },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <CharsheetElement_ sheet={sheet} />
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { Charsheet, CharsheetElement } from './Charsheet';
|
||||||
|
|
||||||
|
export default { CharsheetElement };
|
||||||
|
|
Loading…
Reference in New Issue