| name | dnd-mechanics |
| description | Implement 2D&D's D&D 5E-inspired stats, combat, elements, and statuses correctly |
| license | MIT |
D&D-Inspired Mechanics
2D&D adapts D&D 5E concepts to a single-player JRPG. Follow the implemented
rules in src/systems/ when they intentionally differ from tabletop rules.
Ability scores
The six scores are Strength, Dexterity, Constitution, Intelligence, Wisdom,
and Charisma.
abilityModifier(score) === Math.floor((score - 10) / 2);
Character creation supports:
- 27-point buy
- Base score range 8-15
- Costs: 8/0, 9/1, 10/2, 11/3, 12/4, 13/5, 14/7, 15/9
- Optional 4d6-drop-lowest generation
- Class boosts applied after base scores
Classes
| Class | Boosts | Primary stat |
|---|
| Knight | STR +2, CON +1 | STR |
| Ranger | DEX +2, WIS +1 | DEX |
| Wizard | INT +2, WIS +1 | INT |
| Sorcerer | CHA +2, CON +1 | CHA |
| Rogue | DEX +2, CHA +1 | DEX |
| Paladin | STR +1, CHA +2 | CHA |
| Warlock | CHA +2, INT +1 | CHA |
| Cleric | WIS +2, CON +1 | WIS |
| Druid | WIS +2, CON +1 | WIS |
| Barbarian | STR +2, CON +1 | STR |
| Monk | DEX +2, WIS +1 | DEX |
| Bard | CHA +2, DEX +1 | CHA |
The class primaryStat is used for attack/to-hit calculations where the
specific action does not override the stat.
Non-combat ability checks
Use src/systems/skillChecks.ts for exploration and dialogue checks:
total = naturalD20 + abilityModifier(player.stats[ability]);
success = total >= dc;
- Dexterity: lockpicking, trap disarming, and escaping hazards
- Wisdom: hidden loot, paths, passages, and exploration events
- Charisma: Persuade/Bluff NPC outcomes and shop negotiation
- Natural 1 and 20 do not automatically fail or succeed on these checks
- Fixed outcomes are one-time and persist in
player.progression.skillChecks
- Exploration damage is nonlethal and cannot reduce the player below 1 HP
Gathering minigames do not apply unrelated ability modifiers silently. Related
future talents or tools may modify explicit data-driven difficulty, but fishing,
mining, and foraging use readable input patterns with identical reduced-motion
thresholds and rewards.
Core combat
- Attack rolls use d20 + ability modifier + proficiency/bonuses.
- Natural 20 automatically hits and is critical.
- Natural 1 automatically misses.
- Initiative uses d20 + Dexterity modifier.
- Spells consume MP rather than spell slots.
- Armor Class comes from base AC, Dexterity, equipment, talents, active
statuses, and temporary defense bonuses.
- Group initiative rolls once for the player and once per monster, then
interleaves all actors by total using stable combatant IDs. Companion actors
use the same initiative contract.
- Melee must clear the front row before attacking the back row. Once exposed,
back-row melee targets impose -2 to the attack roll; ranged attacks and
spells ignore this formation penalty.
Use the combat functions in src/systems/combat.ts; do not reimplement formulas
inside scenes.
Advantage and disadvantage
Roll two d20s and select the higher/lower natural roll before adding modifiers.
Natural 1 and natural 20 behavior must be based on the selected die, not the
first die rolled.
Poisoned, Frightened, and Prone currently impose attack disadvantage. Magic
Missile is auto-hit and bypasses attack-roll disadvantage.
AoE spells pay MP once and share one attack/damage roll, while elemental
resistance, weakness, and immunity resolve independently per target.
Healing target scopes are explicit: self, one ally, all allies, or the whole
party. A single-ally action falls back to self when no ally is present.
Elements
src/data/elements.ts defines:
- Fire
- Ice
- Lightning
- Poison
- Necrotic
- Radiant
- Thunder
- Force
- Psychic
Elemental profile order is:
- Immunity: damage becomes 0.
- Weakness: damage is doubled.
- Resistance: damage is floored after halving.
- Otherwise unchanged.
Status damage modifiers are applied before the elemental modifier. Record
observed non-neutral interactions in the Codex.
Monster affinity is presentation and roster metadata using the same canonical
nine-element enum. Damage still resolves only through ability/spell elements and
the target's elementalProfile; family data must not create a parallel damage
rule.
Status effects
src/systems/statusEffects.ts is the single source of truth.
Debuffs:
- Poisoned: turn damage and attack disadvantage
- Burning: fire damage each turn
- Frozen: reduced accuracy and AC
- Paralyzed: skipped turns and reduced AC
- Stunned: one skipped turn
- Frightened: attack disadvantage and reduced damage
- Slowed: reduced accuracy and damage
- Prone: attack disadvantage and reduced AC
- Asleep: skipped turns until save/expiration
- Confused: reduced accuracy
Buffs:
- Enraged: increased damage and reduced AC
- Hasted: increased accuracy and AC
- Inspired: increased accuracy and damage
- Raging: increased damage
- Sneak Stance: increased AC
Lifecycle for each actor:
- Start turn: resolve tick damage, allowed saving throws, and skip-turn state.
- Perform or skip the action.
- End turn: decrement duration and expire effects.
A one-turn stun therefore skips exactly one actor turn. Cure items remove only
matching effects. Combat effects are cleared when leaving Battle.
Dungeon trap checks
- Detection and disarming use d20 + the configured Dexterity or Intelligence
modifier through
resolveSkillCheck() / rollSkillCheck() with a validated
situational modifier.
- Trap Kits, Natural Explorer, Cunning Action, and persistent Adventurer
guidance add their defined bonuses; Danger Sense detects automatically.
- Failed detection persists in authoritative
trapStates, so checks cannot be
farmed by stepping away or reloading.
- Successful disarming awards XP through
awardXP().
- Trigger damage and MP loss apply immediately and cannot reduce HP below 1.
Trap-applied statuses seed the next battle and then follow the existing
combat-turn lifecycle.
- Alarm traps replace the normal encounter roll with a forced dungeon
encounter. Hidden floors use the next level's validated spawn.
Action economy
- Attack, spell, defend, flee, and normal abilities consume the action.
- Bonus-action abilities do not schedule the monster turn.
- The first item use in a turn is a bonus action.
- Invalid spell/ability/item choices must not consume MP, inventory, or turn
state.
- Every party actor owns an immutable one-action/one-bonus-action economy.
Manual and gambit-controlled companion actions use the same validator and
executor as the hero-compatible battle contracts.
- Animation is presentation-only: resolve and consume the validated action
exactly once before emitting actor/target feedback. Tween completion must
never apply damage, spend resources, or advance action economy.
Companion progression
- Recruits match the hero level deterministically, then gain XP and levels
independently.
- Living party members receive battle XP. A KO member receives no victory XP
and resets to the current-level XP floor (
0 at level 1).
- Full defeat occurs only when every active party actor is KO. Apply the
current-level XP floors and 30% carried-gold loss once, restore defeated
actors to half HP/MP, clear battle effects, and recover at the last town.
Return the exact before/after receipt for
DefeatScene; boss and random
encounters use identical mechanics. Inn rest still fully restores and
processes pending levels for all recruited companions.
- Party-wide heal/buff spells consume MP once and resolve each valid target.
- Gambit and UI actions pass through
validateBattleAction() before execution;
validated plans bind stable actor/target IDs and declare action or bonus
action cost.
BattleActionEconomyState tracks one action and one bonus action per actor;
consuming a bonus action leaves the main action available for lower-ranked
gambits.
- Generic outbound actors execute validated plans through the same d20, AC,
element, status, healing, MP, inventory, and defend paths as the hero.
- Battle consumables use item-declared target scopes. Ally items fall back to
self when solo, self-only items remain self, and inventory ownership stays
separate from the effect target.
- Inventory sorting and filtering are presentation-only. Preserve canonical
ownership order, exact equipment links, and equipped/key/mount transfer
restrictions while resolving actions through original inventory indexes.
- Crafting is deterministic rather than a d20 check. Validate every ingredient,
batch, gold/station requirement, and output before mutation; equipment upgrades
intentionally replace exact equipped links while unrelated equipped, key,
mount, and quest items remain protected.
- Flee DC is 10 for one monster and increases by 2 for each additional living
monster. Boss encounters cannot be fled.
Leveling
- Proficiency increases at levels 5, 9, 13, and 17.
- Ability score improvements occur at levels 4, 8, 12, 16, and 19.
- Pending level gains are applied by the existing rest/level processing logic.
- Class hit dice and unlock tables are defined in
src/systems/classes.ts.
Alignment and reputation
- Alignment uses two bounded axes: Law/Chaos and Good/Evil, both from -100 to
100. Scores at +25 or higher use the positive label; scores at -25 or lower
use the negative label; the middle band is Neutral.
- New heroes begin at
lawChaos: -50, goodEvil: 0, exactly Chaotic Neutral.
- Reputation is independent per canonical town and faction. Tiers are Hostile,
Wary, Neutral, Friendly, Trusted, and Exalted.
- Apply shifts only through
applySocialMutation() with a stable source ID.
Routine combat and movement never change alignment or reputation.
- Reputation shop adjustments are additive with Charisma negotiation and clamp
to a 25% surcharge or 35% discount.
Achievement-specific mechanics
- A one-hit defeat requires one damaging action to take an enemy from its full
maximum HP to zero; prior chip or status damage disqualifies it.
- The no-defeat campaign achievement uses explicit schema-v13 party-defeat
history. Older saves have unknown history and cannot receive it by inference.
- Achievements and equipped cosmetic titles never modify ability scores, AC,
attacks, damage, rewards, or action economy.
Testing
Use deterministic dice mocks for:
- Natural 1/20 and two-d20 selection
- Status saving throws, ticks, skips, and expiration
- Elemental immunity/weakness/resistance
- Status plus elemental damage ordering
- Bonus-action scheduling
- MP and inventory consumption on invalid actions
- Seeded trap placement, one-shot checks, modifiers, nonlethal consequences,
alarm suppression, and hidden-floor destinations
- Ability modifiers, DC boundaries, persistent negotiation choices, and
nonlethal exploration damage
- Alignment boundaries, reputation tiers, clamping, stable-source idempotency,
social hooks, and shop-adjustment composition
Relevant suites include combat.test.ts, dice.test.ts, elements.test.ts,
statusEffects.test.ts, skillChecks.test.ts, reputation.test.ts, player.test.ts, and
traps.test.ts.