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%;
}
.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 = {
guilded: boolean,
gilded: boolean,
score: number,
}
@ -20,6 +20,7 @@ export type ActionGroup = {
}
type Nerve = {
type_: "nerve",
drives: Guage,
resistances: Guage,
move: Action,
@ -28,6 +29,7 @@ type Nerve = {
}
type Cunning = {
type_: "cunning",
drives: Guage,
resistances: Guage,
sway: Action,
@ -36,6 +38,7 @@ type Cunning = {
}
type Intuition = {
type_: "intuition",
drives: Guage,
resistances: Guage,
survey: Action,
@ -62,11 +65,6 @@ interface CharsheetProps {
sheet: Charsheet,
}
interface ActionDriveProps {
groupName: string,
group: Nerve | Cunning | Intuition,
}
interface GuageProps {
current: 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) {
return <div> -- Nerve -- </div>;
} else if ("sway" in group) {
return <div> -- Cunning -- </div>;
} else {
return <div> -- Intuition -- </div>;
interface ActionElementProps {
name: string,
gilded: boolean,
value: number,
}
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) => {
@ -104,9 +154,9 @@ const CharsheetElement_ = ({ sheet }: CharsheetProps) => {
</div >
<div className="charsheet__body">
<div>
<ActionDriveElement groupName="Nerve" group={sheet.nerve} />
<ActionDriveElement groupName="Cunning" group={sheet.cunning} />
<ActionDriveElement groupName="Intuition" group={sheet.intuition} />
<ActionGroupElement group={sheet.nerve} />
<ActionGroupElement group={sheet.cunning} />
<ActionGroupElement group={sheet.intuition} />
</div>
<div> Role and Specialty </div>
<div> Marks, Scars, Relationships </div>
@ -125,26 +175,29 @@ export const CharsheetElement = () => {
question: 'What were the contents of that book?',
role: 'Slink',
nerve: {
type_: "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 },
},
move: { gilded: false, score: 2 },
strike: { gilded: false, score: 1 },
control: { gilded: true, score: 0 },
} as Nerve,
cunning: {
type_: "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 },
},
sway: { gilded: false, score: 0 },
read: { gilded: false, score: 0 },
hide: { gilded: false, score: 0 },
} as Cunning,
intuition: {
type_: "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 },
}
survey: { gilded: false, score: 0 },
focus: { gilded: false, score: 0 },
sense: { gilded: false, score: 0 },
} as Intuition
};
return <CharsheetElement_ sheet={sheet} />