Files
the-sunken-reach/docs/GAME_RULES_REFERENCE.md
T
2026-09-22 16:27:53 +02:00

151 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.