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
4 changed files with 138 additions and 28 deletions
Showing only changes of commit 311cd9c9a5 - Show all commits

View File

@ -0,0 +1,14 @@
version: '3'
tasks:
build:
cmds:
- cargo build
test:
cmds:
- cargo watch -x test
server:
cmds:
- cargo watch -x run

7
visions/ui/Taskfile.yml Normal file
View File

@ -0,0 +1,7 @@
version: '3'
tasks:
dev:
cmds:
- npm run start

View File

@ -16,4 +16,40 @@
width: 33%; width: 33%;
} }
.action-group {
position: relative;
border: 2px solid black;
border-radius: 4px;
padding-left: 8px;
padding-bottom: 8px;
}
.action-group:before {
content: " ";
position: absolute;
z-index: -1;
top: 2px;
left: 2px;
right: 2px;
bottom: 2px;
border: 2px solid black;
}
.action-group > h1 {
margin: 4px;
margin-left: -4px;
padding-left: 4px;
background-color: black;
color: white;
}
.action-group__action {
margin: 2px;
}
.action-group__dots {
margin: 0px;
padding: 0px;
padding-left: 16px;
}

View File

@ -7,7 +7,7 @@ export type Guage = {
} }
export type Action = { export type Action = {
guilded: boolean, gilded: boolean,
score: number, score: number,
} }
@ -20,6 +20,7 @@ export type ActionGroup = {
} }
type Nerve = { type Nerve = {
type_: "nerve",
drives: Guage, drives: Guage,
resistances: Guage, resistances: Guage,
move: Action, move: Action,
@ -28,6 +29,7 @@ type Nerve = {
} }
type Cunning = { type Cunning = {
type_: "cunning",
drives: Guage, drives: Guage,
resistances: Guage, resistances: Guage,
sway: Action, sway: Action,
@ -36,6 +38,7 @@ type Cunning = {
} }
type Intuition = { type Intuition = {
type_: "intuition",
drives: Guage, drives: Guage,
resistances: Guage, resistances: Guage,
survey: Action, survey: Action,
@ -62,11 +65,6 @@ interface CharsheetProps {
sheet: Charsheet, sheet: Charsheet,
} }
interface ActionDriveProps {
groupName: string,
group: Nerve | Cunning | Intuition,
}
interface GuageProps { interface GuageProps {
current: number, current: number,
max: number, max: number,
@ -76,15 +74,67 @@ const GuageElement = ({ current, max }: GuageProps) => {
} }
const ActionDriveElement = ({ groupName, group }: ActionDriveProps) => { function assertNever(value: never) {
throw new Error("Unexpected value: " + value);
}
if ("move" in group) { interface ActionElementProps {
return <div> -- Nerve -- </div>; name: string,
} else if ("sway" in group) { gilded: boolean,
return <div> -- Cunning -- </div>; value: number,
} else { }
return <div> -- Intuition -- </div>;
const ActionElement = ({name, gilded, value}: ActionElementProps) => {
let dots = [];
for (let i = 0; i < value; i++) {
dots.push("\u25ef");
} }
let diamond = gilded ? "\u25c6" : "\u25c7";
return (<div>
<h2 className="action-group__action"> {diamond} {name} </h2>
<div className="action-group__dots"> {dots} </div>
</div>);
}
interface ActionGroupElementProps {
group: Nerve | Cunning | Intuition;
}
const ActionGroupElement = ({group}: ActionGroupElementProps) => {
var title;
var elements = [];
switch (group.type_) {
case "nerve": {
title = <div> Nerve </div>
elements.push(<ActionElement name="Move" gilded={group.move.gilded} value={group.move.score} />);
elements.push(<ActionElement name="Strike" gilded={group.strike.gilded} value={group.strike.score} />);
elements.push(<ActionElement name="Control" gilded={group.control.gilded} value={group.control.score} />);
break
}
case "cunning": {
title = <div> Cunning </div>
elements.push(<ActionElement name="Sway" gilded={group.sway.gilded} value={group.sway.score} />);
elements.push(<ActionElement name="Read" gilded={group.read.gilded} value={group.read.score} />);
elements.push(<ActionElement name="Hide" gilded={group.hide.gilded} value={group.hide.score} />);
break
}
case "intuition": {
title = <div> Intuition </div>
elements.push(<ActionElement name="Survey" gilded={group.survey.gilded} value={group.survey.score} />);
elements.push(<ActionElement name="Focus" gilded={group.focus.gilded} value={group.focus.score} />);
elements.push(<ActionElement name="Sense" gilded={group.sense.gilded} value={group.sense.score} />);
break
}
default: {
assertNever(group);
}
}
return (<div className="action-group">
<h1> {title} </h1>
{elements}
</div>)
} }
const CharsheetElement_ = ({ sheet }: CharsheetProps) => { const CharsheetElement_ = ({ sheet }: CharsheetProps) => {
@ -104,9 +154,9 @@ const CharsheetElement_ = ({ sheet }: CharsheetProps) => {
</div > </div >
<div className="charsheet__body"> <div className="charsheet__body">
<div> <div>
<ActionDriveElement groupName="Nerve" group={sheet.nerve} /> <ActionGroupElement group={sheet.nerve} />
<ActionDriveElement groupName="Cunning" group={sheet.cunning} /> <ActionGroupElement group={sheet.cunning} />
<ActionDriveElement groupName="Intuition" group={sheet.intuition} /> <ActionGroupElement group={sheet.intuition} />
</div> </div>
<div> Role and Specialty </div> <div> Role and Specialty </div>
<div> Marks, Scars, Relationships </div> <div> Marks, Scars, Relationships </div>
@ -125,26 +175,29 @@ export const CharsheetElement = () => {
question: 'What were the contents of that book?', question: 'What were the contents of that book?',
role: 'Slink', role: 'Slink',
nerve: { nerve: {
type_: "nerve",
drives: { current: 2, max: 2 }, drives: { current: 2, max: 2 },
resistances: { current: 0, max: 3 }, resistances: { current: 0, max: 3 },
move: { guilded: false, score: 0 }, move: { gilded: false, score: 2 },
strike: { guilded: false, score: 0 }, strike: { gilded: false, score: 1 },
control: { guilded: true, score: 0 }, control: { gilded: true, score: 0 },
}, } as Nerve,
cunning: { cunning: {
type_: "cunning",
drives: { current: 1, max: 1 }, drives: { current: 1, max: 1 },
resistances: { current: 0, max: 3 }, resistances: { current: 0, max: 3 },
sway: { guilded: false, score: 0 }, sway: { gilded: false, score: 0 },
read: { guilded: false, score: 0 }, read: { gilded: false, score: 0 },
hide: { guilded: false, score: 0 }, hide: { gilded: false, score: 0 },
}, } as Cunning,
intuition: { intuition: {
type_: "intuition",
drives: { current: 0, max: 0 }, drives: { current: 0, max: 0 },
resistances: { current: 0, max: 3 }, resistances: { current: 0, max: 3 },
survey: { guilded: false, score: 0 }, survey: { gilded: false, score: 0 },
focus: { guilded: false, score: 0 }, focus: { gilded: false, score: 0 },
sense: { guilded: false, score: 0 }, sense: { gilded: false, score: 0 },
} } as Intuition
}; };
return <CharsheetElement_ sheet={sheet} /> return <CharsheetElement_ sheet={sheet} />