Create a renderer for Candela Obscura character sheets #275

Merged
savanni merged 6 commits from charsheet-plugin into main 2024-11-29 14:42:49 +00:00
7 changed files with 182 additions and 1 deletions
Showing only changes of commit db8e67420f - Show all commits

1
.envrc
View File

@ -1 +1,2 @@
mkdir .direnv
use flake use flake

View File

@ -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 {

View File

@ -1,5 +1,4 @@
.App { .App {
text-align: center;
} }
.App-logo { .App-logo {

View File

@ -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 (

View File

@ -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%;
}

View File

@ -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} />
}

View File

@ -0,0 +1,4 @@
import { Charsheet, CharsheetElement } from './Charsheet';
export default { CharsheetElement };