Skip to content

Instantly share code, notes, and snippets.

@mpelos
Last active July 14, 2022 23:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mpelos/d5ffbdab7f104fad9e0910608e3a59f5 to your computer and use it in GitHub Desktop.
Save mpelos/d5ffbdab7f104fad9e0910608e3a59f5 to your computer and use it in GitHub Desktop.
RPG Sheet Example
$id: /schemas/character-sheet
title: GURPS Character Sheet
type: object
properties:
characterName:
type: string
basicAttributes:
type: object
properties:
st:
$anchor: basicAttributes:st
type: integer
min: 1
default: 10
stCharacterPoints:
type: integer
dx:
$anchor: basicAttributes:dx
type: integer
min: 1
default: 10
ht:
$anchor: basicAttributes:ht
type: integer
min: 1
default: 10
required:
- st
- dx
- ht
formulas:
stCharacterPoints:
expression: ($st - 10) * 10
replacements:
st: "#basicAttributs:st"
secondaryCharacteristics:
type: object
properties:
hp:
type: integer
readOnly: true
min: 1
hpModifier:
$anchor: secondaryCharacteristics:hpModifier
type: integer
min: 0
basicLift:
$anchor: secondaryCharacteristics:basicLift
type: integer
readOnly: true
min: 1
basicLiftModifier:
$anchor: secondaryCharacteristics:basicLiftModifier
type: integer
min: 0
basicSpeed:
$anchor: secondaryCharacteristics:basicSpeed
type: integer
readOnly: true
min: 0
basicSpeedModifier:
$anchor: secondaryCharacteristics:basicSpeedModifier
type: integer
min: 0
dodge:
$anchor: secondaryCharacteristics:dodge
type: integer
readOnly: true
min: 0
damage:
type: object
properties:
thrust:
$anchor: secondaryCharacteristics:dodge:damage:thrust
$ref: "#$defs/diceRoll"
required:
- thrust
formulas:
thrust:
expression: >-
(() => {
const getNumberOfDices = () => {
if ($st <= 18) { return 1; }
return Math.floor(($st - 27) / 8) + 3;
}
const getMod = () => {
if ($st <= 2) { return -6; }
if ($st <= 4) { return -5; }
if ($st <= 6) { return -4; }
if ($st <= 8) { return -3; }
if ($st <= 10) { return -2; }
return [-1, 0, 1, 2][Math.floor(($st - 11) / 2) % 4];
}
return [getNumberOfDices(), getMod()];
})()
replacements:
st: "#basicAttributes:st"
formulas:
hp: $ref{#basicAttributes:st} + $ref{#secondaryCharacteristics:hpModifier}
basicLift:
expression: Math.round(Math.pow(($st + $mod) / 5)
replacements:
st: "#basicAttributes:st"
mod: "#secondaryCharacteristics:basicLiftModifier"
basicSpeed:
expression: (($dx + $ht) / 4) + (:mod * 0.25)
replacements:
dx: "#basicAttributes:dx"
ht: "#basicAttributes:ht"
mod: "#secondaryCharacteristics:basicSpeedModifier"
dodge:
expression: Math.floor($basicSpeed) + 3 - $encumbrance
replacements:
basicSpeed: "#secondaryCharacteristics:basicSpeed"
encumbrance: "#encumbrance"
advantages:
$anchor: advantages
type: array
items:
type: object
properties:
name:
type: string
readOnly: true
level:
type: integer
min: 1
characterPoints:
type: integer
readOnly: true
required:
- name
- characterPoints
skills:
$anchor: skills
type: array
items:
type: object
properties:
name:
type: string
readOnly: true
controllingAttribute:
$anchor: skills:controllingAttribute
type: string
enum:
- ST
- DX
- HT
difficultyLevel:
$anchor: skills:difficultyLevel
type: string
enum:
- Easy
- Average
- Hard
- Very Hard
rank:
$anchor: skills:rank
type: integer
min: 0
score:
$anchor: skills:score
type: integer
readOnly: true
min: 0
characterPoints:
$anchor: skills:characterPoints
type: integer
readOnly: true
required:
- name
- controllingAttribute
- difficultyLevel
- rank
- score
formulas:
score:
expression: >-
(() => {
const getAttrValue = () => {
if ($attr === 'ST') {
return $st;
} else if ($attr === 'DX') {
return $dx;
}
return $ht;
}
const getPenalty = () => {
if ($level === 'Average') {
return 2;
} else if ($level === 'Hard') {
return 3;
} else if ($level === 'Very Hard') {
return 4;
}
return 1;
}
return getAttrValue() - getPenalty() + $rank;
})()
replacements:
attr: "#skills:controllingAttribute"
level: "#skills:difficultyLevel"
st: "#basicAttributes:st"
dx: "#basicAttributes:dx"
ht: "#basicAttributes:ht"
characterPoints:
expression: >-
(() => {
if ($rank <= 2) { return $rank; }
return ($rank - 2) * 4;
})()
replacements:
rank: "#skills:rank"
weapons:
$anchor: skills
type: array
items:
type: object
properties:
name:
type: string
damageMovement:
$anchor: weapons:damageMovement
type: string
enum: ["Thrust"]
damageModifier:
$anchor: weapons:damageModifier
type: integer
damageType:
type: string
damage:
$ref: "#$defs/diceRoll"
readOnly: true
st:
type: integer
min: 0
reach:
type: string
parry:
type: string
weight:
type: integer
min: 0
price:
type: number
min: 0
formulas:
damage:
expression: >-
(() => {
if ($movement !== "Thrust") { return; }
[$thrust[0], $thrust[1] + $mod]
})()
replacements:
movement: "#weapons:damageMovement"
thrust: "#secondaryCharacteristics:damage:thrust"
mod: "#weapons:damageModifier"
weight:
$anchor: weight
type: integer
min: 0
encumbrance:
$anchor: encumbrance
type: integer
min: 0
max: 4
formulas:
encumbrance:
expression: >-
(() => {
const x = Math.floor($weight / $basicLift);
return x >= 10 ? 4 : x >= 6 ? 3 : x - 1;
})()
replacements:
weight: "#weight"
basicLift: "#secondaryCharacteristics:basicLift"
$defs:
diceRoll:
type: array
prefixItems:
- type: integer
min: 1
- type: integer
items: false
minItens: 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 class="title">GURPS Character Sheet POC</h1>
<div class="field">
<label for="basicAttributes-st"></label>
<div class="control">
<%= input('basicAttributes.st', { placeholder: 'ST' }) %>
</div>
</div>
<div className="field">
<label for="hp" className="label">
HP
</label>
<%= value('secondaryCharacteristics.hp') %>
</div>
<h3 className="title">Advantages</h3>
<div class="select">
<select onChange="<% (id) => addOptionItem('advantage', 'advantagesOptions', id) %>">
<% eachOption('advantagesOptions', (advantageOption, id, index) => { %>
<option value="<%= id %>"><%= advantageOption.name %></option>
<% }) %>
</select>
</div>
<ul>
<% each('advantages', (item, index) => { %>
<li class="field is-horizontal">
<div className="field-label is-normal">
<label className="label">Advantage <%= index + 1 %></label>
</div>
<div className="field-body">
<div className="field">
<p className="control is-expanded">
<%= input(`advantages.${index}.name`, { placeholder: 'ST' }) %>
</p>
</div>
</div>
<% condition(item.hasLevel, () => { %>
<div className="field">
<p className="control is-expanded">
<input
{...register(`advantages.${index}.level`, {
valueAsNumber: true,
})}
type="number"
className="input"
placeholder="Level"
/>
</p>
</div>
<% }) %>
<button
className="button is-danger"
onClick="<% () => removeItem('advantages', index)} %>"
>
Remove
</button>
</li>
<% }); %>
</ul>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment