151 lines
5.0 KiB
Markdown
151 lines
5.0 KiB
Markdown
# D&D 5e Rules Reference — Implementation Spec
|
||
|
||
This is the canonical math the backend must implement. Build it as a single shared
|
||
calculation module (e.g. `backend/src/game/calculations.ts`) that is the **only**
|
||
place these formulas live — every API route and the character builder call into it
|
||
rather than re-deriving numbers. This mirrors the logic already built and verified
|
||
in the "DnD Character Sheet Calculator.xlsx" workbook delivered earlier in this
|
||
project; treat that workbook as the reference implementation if anything below is
|
||
ambiguous.
|
||
|
||
All rules are 5e (2014 core rules).
|
||
|
||
## Ability Modifier
|
||
|
||
```
|
||
modifier = floor((score - 10) / 2)
|
||
```
|
||
|
||
Use `Math.floor`, not integer truncation — they differ for negative results
|
||
(e.g. score 7 → (7-10)/2 = -1.5 → floor = -2, not -1).
|
||
|
||
## Proficiency Bonus
|
||
|
||
```
|
||
proficiencyBonus = 2 + floor((level - 1) / 4)
|
||
```
|
||
|
||
Level 1–4 → +2, 5–8 → +3, 9–12 → +4, 13–16 → +5, 17–20 → +6.
|
||
|
||
## Final Ability Score
|
||
|
||
```
|
||
finalScore = rolledScore + speciesBonus(ability, species)
|
||
```
|
||
|
||
`speciesBonus` comes from the Species Ability Score Increase table below. See
|
||
`seed-data/species.json`.
|
||
|
||
## Saving Throws
|
||
|
||
```
|
||
saveBonus = abilityModifier + (proficient ? proficiencyBonus : 0)
|
||
```
|
||
|
||
Proficiency per ability comes from the Class Quick Facts table (`seed-data/classes.json`).
|
||
|
||
## Skills
|
||
|
||
```
|
||
skillBonus = governingAbilityModifier + (proficient ? proficiencyBonus : 0)
|
||
```
|
||
|
||
Governing ability per skill is fixed — see `seed-data/skills.json`. Proficiency is
|
||
a per-character, per-skill boolean the player/DM sets during character building
|
||
(`CharacterSkillProficiency` in the Prisma schema).
|
||
|
||
Passive Perception:
|
||
|
||
```
|
||
passivePerception = 10 + perceptionSkillBonus
|
||
```
|
||
|
||
## Armor Class
|
||
|
||
```
|
||
armorClass = 10 + dexModifier + armorBonus
|
||
```
|
||
|
||
`armorBonus` is a flat number the player enters representing whatever armor/shield
|
||
they have equipped (this matches the workbook's "Armor Bonus (from armor worn)"
|
||
input cell). Do not try to model armor types as a lookup table unless you want to
|
||
extend scope later — a plain integer field on `Character` is enough for v1.
|
||
|
||
## Initiative
|
||
|
||
```
|
||
initiative = dexModifier
|
||
```
|
||
|
||
## Speed
|
||
|
||
Comes from the species table (30 ft standard; 25 ft for Gnome, Dwarf, Halfling).
|
||
Store as `speed` on the species seed data; characters may override it
|
||
(`speedOverride` on `Character`) for future feature support (e.g. barbarian speed
|
||
boosts) without a schema change.
|
||
|
||
## Hit Points
|
||
|
||
Hit Die size comes from class (`seed-data/classes.json`).
|
||
|
||
```
|
||
hpMax(level) =
|
||
if level === 1:
|
||
hitDieMax + conModifier
|
||
else:
|
||
hitDieMax + conModifier
|
||
+ (level - 1) * (floor(hitDieSize / 2) + 1 + conModifier)
|
||
```
|
||
|
||
Important: **level 1 always uses the maximum possible roll of the Hit Die**, not
|
||
the average. Every level after 1 uses the fixed "average" value
|
||
`floor(hitDieSize / 2) + 1` (e.g. d10 → 6, d6 → 4, d12 → 7) plus the CON modifier
|
||
again. This was a bug caught and fixed while building the Excel calculator —
|
||
do not regress it.
|
||
|
||
`hitDice` available to spend on a short rest = character level (one die of the
|
||
class's Hit Die size per level).
|
||
|
||
## Spellcasting (only for classes with a Spellcasting Ability ≠ "None")
|
||
|
||
```
|
||
spellSaveDC = 8 + proficiencyBonus + spellcastingAbilityModifier
|
||
spellAttackBonus = proficiencyBonus + spellcastingAbilityModifier
|
||
```
|
||
|
||
Spellcasting ability per class is in `seed-data/classes.json` (Intelligence,
|
||
Wisdom, or Charisma). Classes with `"None"` have no spellcasting stats — the UI
|
||
should hide those fields entirely rather than show them as zero/blank.
|
||
|
||
## Species Ability Score Increases & Speed
|
||
|
||
See `seed-data/species.json` for the full table (10 species). Note: Half-Elf gets
|
||
+2 Charisma fixed, plus +1 to **two other** abilities of the player's choice — the
|
||
seed data defaults that flexible pair to STR/INT, but the character builder UI
|
||
must let the player pick which two abilities get the +1 during Half-Elf
|
||
character creation (don't hardcode the default in the UI).
|
||
|
||
## Class Quick Facts
|
||
|
||
See `seed-data/classes.json` for the full table (12 classes): Hit Die, Primary
|
||
Ability, the two Saving Throw proficiencies, and Spellcasting Ability.
|
||
|
||
## Skill → Governing Ability
|
||
|
||
See `seed-data/skills.json` (18 skills).
|
||
|
||
## "Quick Stats" View (combat-context panel)
|
||
|
||
When the frontend shows the contextual quick-stats panel during an attack action,
|
||
surface exactly these derived values (all computed via the module above, never
|
||
stored redundantly):
|
||
|
||
- Attack bonus for the equipped/selected weapon: `proficiencyBonus (if proficient) + relevantAbilityModifier` (STR normally, DEX for finesse/ranged weapons — see `seed-data/weapons.json`'s `attackAbility` field, which is `"STR"`, `"DEX"`, or `"FINESSE"` meaning "use whichever of STR/DEX is higher")
|
||
- Damage: weapon's damage dice + relevant ability modifier
|
||
- Current HP / Max HP / Temp HP
|
||
- AC
|
||
- Any special abilities/features flagged on the character (free-text list is fine for v1 — a `specialAbilities` text/JSON field on `Character` or a simple related table)
|
||
|
||
This is a read view assembled from existing data — it does not need its own
|
||
schema table.
|