{character.species ? `${character.species} ` : ''} {character.characterClass || (inProgress ? 'Not yet built' : '')} · Level {character.level} + {character.currentLocationName && ` · 📍 ${character.currentLocationName}`}
No matches.
} +{campaign.description}
Track where each character currently is.
+ +No locations yet.
} + {locations?.map((loc) => ( +| Name | +Damage | +
|---|---|
| {w.name} | +{w.damageDice} {w.damageType} | +
| Name | +Level | +
|---|---|
| {s.name} | +{s.level === 0 ? 'Cantrip' : s.level} | +
| Name | +AC | +HP | ++ |
|---|---|---|---|
| {m.name} | +{m.ac} | +{m.maxHp} | ++ + | +
No notes yet.
} - {notes?.map((n) => ( + {sharedNotesPage.pageItems.length === 0 &&No notes yet.
} + {sharedNotesPage.pageItems.map((n) => ({n.body}
+{n.body}
— {n.author?.displayName} · {new Date(n.createdAt).toLocaleString()}Only you can see these — plot threads, secrets, NPC tracking.
+ +No DM notes yet.
} + {dmNotesPage.pageItems.map((n) => ( +{n.body}
+ + {new Date(n.createdAt).toLocaleString()} + +Roll the dice at the table — every result is locked in server-side.
+Roll the dice at the table — every result is locked in server-side.
+Pick how this character's dice get rolled. This can't be changed once rolling starts.
++ {bs.speciesChoices?.type === 'wildcard' ? 'Wildcard! Pick any species:' : 'Choose your species:'} +
+Half-Elf gets a fixed +2 CHA, plus +1/+1 to two other abilities of your choice:
++ {bs.classChoices?.type === 'wildcard' ? 'Wildcard! Pick any class:' : 'Choose your class:'} +
++ "{bs.motivationText}" +
+ + > + )} ++ Auto-generated from your class's skill list. Saving throw proficiencies are fixed by class and were + also set automatically. +
+ {!adjustingSkills ? ( + <> +Roll a d6 to see which species you can choose between.
- - > - ) : ( - <> -- {bs.speciesChoices?.type === 'wildcard' ? 'Wildcard! Pick any species:' : 'Choose your species:'} -
-Half-Elf gets a fixed +2 CHA, plus +1/+1 to two other abilities of your choice:
-{bs.classChoices?.type === 'reroll' ? 'You rolled a 7 — reroll once!' : 'Roll a d8 to see which classes you can choose between.'}
- - > - ) : ( - <> -- {bs.classChoices?.type === 'wildcard' ? 'Wildcard! Pick any class:' : 'Choose your class:'} -
-Roll a d6 for your character's driving motivation.
- - > - ) : ( - <> -- "{bs.motivationText}" -
- - > - )} -Saving throw proficiencies are fixed by class and were set automatically.
-{label} Roll it at the table, then type the result in below.
+{label}
+ + > ); } @@ -322,6 +453,7 @@ function BuildProgress({ stage }: { stage: string }) { function AssignScoresStep({ bs, + diceMode, assignments, setAssignments, onRoll, @@ -329,18 +461,22 @@ function AssignScoresStep({ rolling, }: { bs: BuildState; + diceMode: DiceMode; assignments: PartialRoll 4d6 (drop the lowest) six times — one set per ability.
-Roll 4d6 six times at the table, then enter each set's four results below (drop-the-lowest is applied automatically).
+ {sets.map((set, i) => ( +Pick a species and class to see your projected stats build up here.
+ )} + + {species && ( ++ {SPECIES_LABELS[species]} + {Object.entries({ STR: 0, DEX: 0, CON: 0, INT: 0, WIS: 0, CHA: 0 }) + .map(([a]) => [a as Ability, speciesBonusFor(species, a as Ability, halfElfBonusAbilities)] as const) + .filter(([, bonus]) => bonus !== 0) + .map(([a, bonus]) => ( + + {fmtMod(bonus)} {a} + + ))} +
+ )} + + {characterClass && ( ++ {CLASS_LABELS[characterClass]} + · d{hitDie} hit die + {saveProfs && saveProfs.length > 0 && ( + · {saveProfs.map((a) => ABILITY_LABELS[a]).join('/')} saves + )} +
+ )} + + {Object.keys(abilityFinals).length > 0 && ( ++ Save DC {spellSaveDC(profBonus, abilityModifier(abilityFinals[spellAbility]!))} · Attack{' '} + {fmtMod(spellAttackBonus(profBonus, abilityModifier(abilityFinals[spellAbility]!)))} +
+ > + )} + + {bs.skillProficiencies && bs.skillProficiencies.length > 0 && ( + <> + +{character.species && SPECIES_LABELS[character.species]} {character.characterClass && CLASS_LABELS[character.characterClass]} · Level{' '} {character.level} {character.motivation && ` · "${character.motivation}"`} + {character.currentLocation && ` · 📍 ${character.currentLocation.name}`}