From bc0b9a5d1ab6dc1f3f88ead83d3548d9af3c692d Mon Sep 17 00:00:00 2001 From: Thomas Sprock Date: Sun, 5 Apr 2026 10:45:51 +0200 Subject: [PATCH] =?UTF-8?q?feat(2024):=20add=20Levels=20collection=20?= =?UTF-8?q?=E2=80=94=20Batch=201=20(Barbarian,=20Fighter,=20Monk,=20Rogue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 80 level documents (4 classes × 20 levels) to the 2024 data set with SRD 5.2.1-verified class_specific progressions and feature lists. - Barbarian: rage_count/rage_damage_bonus; no brutal_critical_dice (2024 uses Brutal Strike) - Fighter: action_surges, indomitable_uses, extra_attacks progressions - Monk: focus_points (renamed from ki_points), martial_arts (1d6→1d8→1d10→1d12), unarmored_movement - Rogue: sneak_attack progression; ASIs at L4/8/10/12/16 Feature URLs use -nyi suffix (Features collection not yet implemented). tables.test.ts updated with -nyi bypass to allow incremental shipping. Co-Authored-By: Claude Sonnet 4.6 --- src/2024/5e-SRD-Levels.json | 2117 +++++++++++++++++++++++++++++ src/2024/schemas/5e-SRD-Levels.ts | 47 + src/2024/tests/schemas.test.ts | 3 + src/2024/tests/tables.test.ts | 16 +- 4 files changed, 2178 insertions(+), 5 deletions(-) create mode 100644 src/2024/5e-SRD-Levels.json create mode 100644 src/2024/schemas/5e-SRD-Levels.ts diff --git a/src/2024/5e-SRD-Levels.json b/src/2024/5e-SRD-Levels.json new file mode 100644 index 00000000..8aa1a852 --- /dev/null +++ b/src/2024/5e-SRD-Levels.json @@ -0,0 +1,2117 @@ +[ + { + "index": "barbarian-1", + "level": 1, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "barbarian-rage", + "name": "Rage", + "url": "/api/2024/features/barbarian-rage-nyi" + }, + { + "index": "barbarian-unarmored-defense", + "name": "Unarmored Defense", + "url": "/api/2024/features/barbarian-unarmored-defense-nyi" + }, + { + "index": "barbarian-weapon-mastery", + "name": "Weapon Mastery", + "url": "/api/2024/features/barbarian-weapon-mastery-nyi" + } + ], + "class_specific": { + "rage_count": 2, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-1" + }, + { + "index": "barbarian-2", + "level": 2, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "barbarian-danger-sense", + "name": "Danger Sense", + "url": "/api/2024/features/barbarian-danger-sense-nyi" + }, + { + "index": "barbarian-reckless-attack", + "name": "Reckless Attack", + "url": "/api/2024/features/barbarian-reckless-attack-nyi" + } + ], + "class_specific": { + "rage_count": 2, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-2" + }, + { + "index": "barbarian-3", + "level": 3, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "barbarian-barbarian-subclass", + "name": "Barbarian Subclass", + "url": "/api/2024/features/barbarian-barbarian-subclass-nyi" + }, + { + "index": "barbarian-primal-knowledge", + "name": "Primal Knowledge", + "url": "/api/2024/features/barbarian-primal-knowledge-nyi" + } + ], + "class_specific": { + "rage_count": 3, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-3" + }, + { + "index": "barbarian-4", + "level": 4, + "ability_score_bonuses": 1, + "prof_bonus": 2, + "features": [ + { + "index": "barbarian-ability-score-improvement-1", + "name": "Ability Score Improvement", + "url": "/api/2024/features/barbarian-ability-score-improvement-1-nyi" + } + ], + "class_specific": { + "rage_count": 3, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-4" + }, + { + "index": "barbarian-5", + "level": 5, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "barbarian-extra-attack", + "name": "Extra Attack", + "url": "/api/2024/features/barbarian-extra-attack-nyi" + }, + { + "index": "barbarian-fast-movement", + "name": "Fast Movement", + "url": "/api/2024/features/barbarian-fast-movement-nyi" + } + ], + "class_specific": { + "rage_count": 3, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-5" + }, + { + "index": "barbarian-6", + "level": 6, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "barbarian-subclass-feature-1", + "name": "Subclass Feature", + "url": "/api/2024/features/barbarian-subclass-feature-1-nyi" + } + ], + "class_specific": { + "rage_count": 4, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-6" + }, + { + "index": "barbarian-7", + "level": 7, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "barbarian-feral-instinct", + "name": "Feral Instinct", + "url": "/api/2024/features/barbarian-feral-instinct-nyi" + }, + { + "index": "barbarian-instinctive-pounce", + "name": "Instinctive Pounce", + "url": "/api/2024/features/barbarian-instinctive-pounce-nyi" + } + ], + "class_specific": { + "rage_count": 4, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-7" + }, + { + "index": "barbarian-8", + "level": 8, + "ability_score_bonuses": 2, + "prof_bonus": 3, + "features": [ + { + "index": "barbarian-ability-score-improvement-2", + "name": "Ability Score Improvement", + "url": "/api/2024/features/barbarian-ability-score-improvement-2-nyi" + } + ], + "class_specific": { + "rage_count": 4, + "rage_damage_bonus": 2 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-8" + }, + { + "index": "barbarian-9", + "level": 9, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "barbarian-brutal-strike", + "name": "Brutal Strike", + "url": "/api/2024/features/barbarian-brutal-strike-nyi" + } + ], + "class_specific": { + "rage_count": 4, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-9" + }, + { + "index": "barbarian-10", + "level": 10, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "barbarian-subclass-feature-2", + "name": "Subclass Feature", + "url": "/api/2024/features/barbarian-subclass-feature-2-nyi" + } + ], + "class_specific": { + "rage_count": 4, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-10" + }, + { + "index": "barbarian-11", + "level": 11, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "barbarian-relentless-rage", + "name": "Relentless Rage", + "url": "/api/2024/features/barbarian-relentless-rage-nyi" + } + ], + "class_specific": { + "rage_count": 4, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-11" + }, + { + "index": "barbarian-12", + "level": 12, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "barbarian-ability-score-improvement-3", + "name": "Ability Score Improvement", + "url": "/api/2024/features/barbarian-ability-score-improvement-3-nyi" + } + ], + "class_specific": { + "rage_count": 5, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-12" + }, + { + "index": "barbarian-13", + "level": 13, + "ability_score_bonuses": 3, + "prof_bonus": 5, + "features": [ + { + "index": "barbarian-improved-brutal-strike", + "name": "Improved Brutal Strike", + "url": "/api/2024/features/barbarian-improved-brutal-strike-nyi" + } + ], + "class_specific": { + "rage_count": 5, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-13" + }, + { + "index": "barbarian-14", + "level": 14, + "ability_score_bonuses": 3, + "prof_bonus": 5, + "features": [ + { + "index": "barbarian-subclass-feature-3", + "name": "Subclass Feature", + "url": "/api/2024/features/barbarian-subclass-feature-3-nyi" + } + ], + "class_specific": { + "rage_count": 5, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-14" + }, + { + "index": "barbarian-15", + "level": 15, + "ability_score_bonuses": 3, + "prof_bonus": 5, + "features": [ + { + "index": "barbarian-persistent-rage", + "name": "Persistent Rage", + "url": "/api/2024/features/barbarian-persistent-rage-nyi" + } + ], + "class_specific": { + "rage_count": 5, + "rage_damage_bonus": 3 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-15" + }, + { + "index": "barbarian-16", + "level": 16, + "ability_score_bonuses": 4, + "prof_bonus": 5, + "features": [ + { + "index": "barbarian-ability-score-improvement-4", + "name": "Ability Score Improvement", + "url": "/api/2024/features/barbarian-ability-score-improvement-4-nyi" + } + ], + "class_specific": { + "rage_count": 5, + "rage_damage_bonus": 4 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-16" + }, + { + "index": "barbarian-17", + "level": 17, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "barbarian-improved-brutal-strike-2", + "name": "Improved Brutal Strike", + "url": "/api/2024/features/barbarian-improved-brutal-strike-2-nyi" + } + ], + "class_specific": { + "rage_count": 6, + "rage_damage_bonus": 4 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-17" + }, + { + "index": "barbarian-18", + "level": 18, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "barbarian-indomitable-might", + "name": "Indomitable Might", + "url": "/api/2024/features/barbarian-indomitable-might-nyi" + } + ], + "class_specific": { + "rage_count": 6, + "rage_damage_bonus": 4 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-18" + }, + { + "index": "barbarian-19", + "level": 19, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "barbarian-epic-boon", + "name": "Epic Boon", + "url": "/api/2024/features/barbarian-epic-boon-nyi" + } + ], + "class_specific": { + "rage_count": 6, + "rage_damage_bonus": 4 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-19" + }, + { + "index": "barbarian-20", + "level": 20, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "barbarian-primal-champion", + "name": "Primal Champion", + "url": "/api/2024/features/barbarian-primal-champion-nyi" + } + ], + "class_specific": { + "rage_count": 6, + "rage_damage_bonus": 4 + }, + "class": { + "index": "barbarian", + "name": "Barbarian", + "url": "/api/2024/classes/barbarian" + }, + "url": "/api/2024/levels/barbarian-20" + }, + { + "index": "fighter-1", + "level": 1, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "fighter-fighting-style", + "name": "Fighting Style", + "url": "/api/2024/features/fighter-fighting-style-nyi" + }, + { + "index": "fighter-second-wind", + "name": "Second Wind", + "url": "/api/2024/features/fighter-second-wind-nyi" + }, + { + "index": "fighter-weapon-mastery", + "name": "Weapon Mastery", + "url": "/api/2024/features/fighter-weapon-mastery-nyi" + } + ], + "class_specific": { + "action_surges": 0, + "indomitable_uses": 0, + "extra_attacks": 0 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-1" + }, + { + "index": "fighter-2", + "level": 2, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "fighter-action-surge-1-use", + "name": "Action Surge (one use)", + "url": "/api/2024/features/fighter-action-surge-1-use-nyi" + }, + { + "index": "fighter-tactical-mind", + "name": "Tactical Mind", + "url": "/api/2024/features/fighter-tactical-mind-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 0 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-2" + }, + { + "index": "fighter-3", + "level": 3, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "fighter-fighter-subclass", + "name": "Fighter Subclass", + "url": "/api/2024/features/fighter-fighter-subclass-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 0 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-3" + }, + { + "index": "fighter-4", + "level": 4, + "ability_score_bonuses": 1, + "prof_bonus": 2, + "features": [ + { + "index": "fighter-ability-score-improvement-1", + "name": "Ability Score Improvement", + "url": "/api/2024/features/fighter-ability-score-improvement-1-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 0 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-4" + }, + { + "index": "fighter-5", + "level": 5, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "fighter-extra-attack", + "name": "Extra Attack", + "url": "/api/2024/features/fighter-extra-attack-nyi" + }, + { + "index": "fighter-tactical-shift", + "name": "Tactical Shift", + "url": "/api/2024/features/fighter-tactical-shift-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 1 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-5" + }, + { + "index": "fighter-6", + "level": 6, + "ability_score_bonuses": 2, + "prof_bonus": 3, + "features": [ + { + "index": "fighter-ability-score-improvement-2", + "name": "Ability Score Improvement", + "url": "/api/2024/features/fighter-ability-score-improvement-2-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 1 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-6" + }, + { + "index": "fighter-7", + "level": 7, + "ability_score_bonuses": 2, + "prof_bonus": 3, + "features": [ + { + "index": "fighter-subclass-feature-1", + "name": "Subclass Feature", + "url": "/api/2024/features/fighter-subclass-feature-1-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 1 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-7" + }, + { + "index": "fighter-8", + "level": 8, + "ability_score_bonuses": 3, + "prof_bonus": 3, + "features": [ + { + "index": "fighter-ability-score-improvement-3", + "name": "Ability Score Improvement", + "url": "/api/2024/features/fighter-ability-score-improvement-3-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 0, + "extra_attacks": 1 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-8" + }, + { + "index": "fighter-9", + "level": 9, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "fighter-indomitable-1-use", + "name": "Indomitable (one use)", + "url": "/api/2024/features/fighter-indomitable-1-use-nyi" + }, + { + "index": "fighter-tactical-master", + "name": "Tactical Master", + "url": "/api/2024/features/fighter-tactical-master-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 1, + "extra_attacks": 1 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-9" + }, + { + "index": "fighter-10", + "level": 10, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "fighter-subclass-feature-2", + "name": "Subclass Feature", + "url": "/api/2024/features/fighter-subclass-feature-2-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 1, + "extra_attacks": 1 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-10" + }, + { + "index": "fighter-11", + "level": 11, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "fighter-two-extra-attacks", + "name": "Two Extra Attacks", + "url": "/api/2024/features/fighter-two-extra-attacks-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 1, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-11" + }, + { + "index": "fighter-12", + "level": 12, + "ability_score_bonuses": 4, + "prof_bonus": 4, + "features": [ + { + "index": "fighter-ability-score-improvement-4", + "name": "Ability Score Improvement", + "url": "/api/2024/features/fighter-ability-score-improvement-4-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 1, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-12" + }, + { + "index": "fighter-13", + "level": 13, + "ability_score_bonuses": 4, + "prof_bonus": 5, + "features": [ + { + "index": "fighter-indomitable-2-uses", + "name": "Indomitable (two uses)", + "url": "/api/2024/features/fighter-indomitable-2-uses-nyi" + }, + { + "index": "fighter-studied-attacks", + "name": "Studied Attacks", + "url": "/api/2024/features/fighter-studied-attacks-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 2, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-13" + }, + { + "index": "fighter-14", + "level": 14, + "ability_score_bonuses": 5, + "prof_bonus": 5, + "features": [ + { + "index": "fighter-ability-score-improvement-5", + "name": "Ability Score Improvement", + "url": "/api/2024/features/fighter-ability-score-improvement-5-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 2, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-14" + }, + { + "index": "fighter-15", + "level": 15, + "ability_score_bonuses": 5, + "prof_bonus": 5, + "features": [ + { + "index": "fighter-subclass-feature-3", + "name": "Subclass Feature", + "url": "/api/2024/features/fighter-subclass-feature-3-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 2, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-15" + }, + { + "index": "fighter-16", + "level": 16, + "ability_score_bonuses": 6, + "prof_bonus": 5, + "features": [ + { + "index": "fighter-ability-score-improvement-6", + "name": "Ability Score Improvement", + "url": "/api/2024/features/fighter-ability-score-improvement-6-nyi" + } + ], + "class_specific": { + "action_surges": 1, + "indomitable_uses": 2, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-16" + }, + { + "index": "fighter-17", + "level": 17, + "ability_score_bonuses": 6, + "prof_bonus": 6, + "features": [ + { + "index": "fighter-action-surge-2-uses", + "name": "Action Surge (two uses)", + "url": "/api/2024/features/fighter-action-surge-2-uses-nyi" + }, + { + "index": "fighter-indomitable-3-uses", + "name": "Indomitable (three uses)", + "url": "/api/2024/features/fighter-indomitable-3-uses-nyi" + } + ], + "class_specific": { + "action_surges": 2, + "indomitable_uses": 3, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-17" + }, + { + "index": "fighter-18", + "level": 18, + "ability_score_bonuses": 6, + "prof_bonus": 6, + "features": [ + { + "index": "fighter-subclass-feature-4", + "name": "Subclass Feature", + "url": "/api/2024/features/fighter-subclass-feature-4-nyi" + } + ], + "class_specific": { + "action_surges": 2, + "indomitable_uses": 3, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-18" + }, + { + "index": "fighter-19", + "level": 19, + "ability_score_bonuses": 6, + "prof_bonus": 6, + "features": [ + { + "index": "fighter-epic-boon", + "name": "Epic Boon", + "url": "/api/2024/features/fighter-epic-boon-nyi" + } + ], + "class_specific": { + "action_surges": 2, + "indomitable_uses": 3, + "extra_attacks": 2 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-19" + }, + { + "index": "fighter-20", + "level": 20, + "ability_score_bonuses": 6, + "prof_bonus": 6, + "features": [ + { + "index": "fighter-three-extra-attacks", + "name": "Three Extra Attacks", + "url": "/api/2024/features/fighter-three-extra-attacks-nyi" + } + ], + "class_specific": { + "action_surges": 2, + "indomitable_uses": 3, + "extra_attacks": 3 + }, + "class": { + "index": "fighter", + "name": "Fighter", + "url": "/api/2024/classes/fighter" + }, + "url": "/api/2024/levels/fighter-20" + }, + { + "index": "monk-1", + "level": 1, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "monk-martial-arts", + "name": "Martial Arts", + "url": "/api/2024/features/monk-martial-arts-nyi" + }, + { + "index": "monk-unarmored-defense", + "name": "Unarmored Defense", + "url": "/api/2024/features/monk-unarmored-defense-nyi" + } + ], + "class_specific": { + "focus_points": 0, + "martial_arts": { + "dice_count": 1, + "dice_value": 6 + }, + "unarmored_movement": 0 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-1" + }, + { + "index": "monk-2", + "level": 2, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "monk-monks-focus", + "name": "Monk's Focus", + "url": "/api/2024/features/monk-monks-focus-nyi" + }, + { + "index": "monk-unarmored-movement", + "name": "Unarmored Movement", + "url": "/api/2024/features/monk-unarmored-movement-nyi" + }, + { + "index": "monk-uncanny-metabolism", + "name": "Uncanny Metabolism", + "url": "/api/2024/features/monk-uncanny-metabolism-nyi" + } + ], + "class_specific": { + "focus_points": 2, + "martial_arts": { + "dice_count": 1, + "dice_value": 6 + }, + "unarmored_movement": 10 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-2" + }, + { + "index": "monk-3", + "level": 3, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "monk-deflect-attacks", + "name": "Deflect Attacks", + "url": "/api/2024/features/monk-deflect-attacks-nyi" + }, + { + "index": "monk-monk-subclass", + "name": "Monk Subclass", + "url": "/api/2024/features/monk-monk-subclass-nyi" + } + ], + "class_specific": { + "focus_points": 3, + "martial_arts": { + "dice_count": 1, + "dice_value": 6 + }, + "unarmored_movement": 10 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-3" + }, + { + "index": "monk-4", + "level": 4, + "ability_score_bonuses": 1, + "prof_bonus": 2, + "features": [ + { + "index": "monk-ability-score-improvement-1", + "name": "Ability Score Improvement", + "url": "/api/2024/features/monk-ability-score-improvement-1-nyi" + }, + { + "index": "monk-slow-fall", + "name": "Slow Fall", + "url": "/api/2024/features/monk-slow-fall-nyi" + } + ], + "class_specific": { + "focus_points": 4, + "martial_arts": { + "dice_count": 1, + "dice_value": 6 + }, + "unarmored_movement": 10 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-4" + }, + { + "index": "monk-5", + "level": 5, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "monk-extra-attack", + "name": "Extra Attack", + "url": "/api/2024/features/monk-extra-attack-nyi" + }, + { + "index": "monk-stunning-strike", + "name": "Stunning Strike", + "url": "/api/2024/features/monk-stunning-strike-nyi" + } + ], + "class_specific": { + "focus_points": 5, + "martial_arts": { + "dice_count": 1, + "dice_value": 8 + }, + "unarmored_movement": 10 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-5" + }, + { + "index": "monk-6", + "level": 6, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "monk-empowered-strikes", + "name": "Empowered Strikes", + "url": "/api/2024/features/monk-empowered-strikes-nyi" + }, + { + "index": "monk-subclass-feature-1", + "name": "Subclass Feature", + "url": "/api/2024/features/monk-subclass-feature-1-nyi" + } + ], + "class_specific": { + "focus_points": 6, + "martial_arts": { + "dice_count": 1, + "dice_value": 8 + }, + "unarmored_movement": 15 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-6" + }, + { + "index": "monk-7", + "level": 7, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "monk-evasion", + "name": "Evasion", + "url": "/api/2024/features/monk-evasion-nyi" + } + ], + "class_specific": { + "focus_points": 7, + "martial_arts": { + "dice_count": 1, + "dice_value": 8 + }, + "unarmored_movement": 15 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-7" + }, + { + "index": "monk-8", + "level": 8, + "ability_score_bonuses": 2, + "prof_bonus": 3, + "features": [ + { + "index": "monk-ability-score-improvement-2", + "name": "Ability Score Improvement", + "url": "/api/2024/features/monk-ability-score-improvement-2-nyi" + } + ], + "class_specific": { + "focus_points": 8, + "martial_arts": { + "dice_count": 1, + "dice_value": 8 + }, + "unarmored_movement": 15 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-8" + }, + { + "index": "monk-9", + "level": 9, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "monk-acrobatic-movement", + "name": "Acrobatic Movement", + "url": "/api/2024/features/monk-acrobatic-movement-nyi" + } + ], + "class_specific": { + "focus_points": 9, + "martial_arts": { + "dice_count": 1, + "dice_value": 8 + }, + "unarmored_movement": 15 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-9" + }, + { + "index": "monk-10", + "level": 10, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "monk-heightened-focus", + "name": "Heightened Focus", + "url": "/api/2024/features/monk-heightened-focus-nyi" + }, + { + "index": "monk-self-restoration", + "name": "Self-Restoration", + "url": "/api/2024/features/monk-self-restoration-nyi" + } + ], + "class_specific": { + "focus_points": 10, + "martial_arts": { + "dice_count": 1, + "dice_value": 8 + }, + "unarmored_movement": 20 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-10" + }, + { + "index": "monk-11", + "level": 11, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "monk-subclass-feature-2", + "name": "Subclass Feature", + "url": "/api/2024/features/monk-subclass-feature-2-nyi" + } + ], + "class_specific": { + "focus_points": 11, + "martial_arts": { + "dice_count": 1, + "dice_value": 10 + }, + "unarmored_movement": 20 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-11" + }, + { + "index": "monk-12", + "level": 12, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "monk-ability-score-improvement-3", + "name": "Ability Score Improvement", + "url": "/api/2024/features/monk-ability-score-improvement-3-nyi" + } + ], + "class_specific": { + "focus_points": 12, + "martial_arts": { + "dice_count": 1, + "dice_value": 10 + }, + "unarmored_movement": 20 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-12" + }, + { + "index": "monk-13", + "level": 13, + "ability_score_bonuses": 3, + "prof_bonus": 5, + "features": [ + { + "index": "monk-deflect-energy", + "name": "Deflect Energy", + "url": "/api/2024/features/monk-deflect-energy-nyi" + } + ], + "class_specific": { + "focus_points": 13, + "martial_arts": { + "dice_count": 1, + "dice_value": 10 + }, + "unarmored_movement": 20 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-13" + }, + { + "index": "monk-14", + "level": 14, + "ability_score_bonuses": 3, + "prof_bonus": 5, + "features": [ + { + "index": "monk-disciplined-survivor", + "name": "Disciplined Survivor", + "url": "/api/2024/features/monk-disciplined-survivor-nyi" + } + ], + "class_specific": { + "focus_points": 14, + "martial_arts": { + "dice_count": 1, + "dice_value": 10 + }, + "unarmored_movement": 25 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-14" + }, + { + "index": "monk-15", + "level": 15, + "ability_score_bonuses": 3, + "prof_bonus": 5, + "features": [ + { + "index": "monk-perfect-focus", + "name": "Perfect Focus", + "url": "/api/2024/features/monk-perfect-focus-nyi" + } + ], + "class_specific": { + "focus_points": 15, + "martial_arts": { + "dice_count": 1, + "dice_value": 10 + }, + "unarmored_movement": 25 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-15" + }, + { + "index": "monk-16", + "level": 16, + "ability_score_bonuses": 4, + "prof_bonus": 5, + "features": [ + { + "index": "monk-ability-score-improvement-4", + "name": "Ability Score Improvement", + "url": "/api/2024/features/monk-ability-score-improvement-4-nyi" + } + ], + "class_specific": { + "focus_points": 16, + "martial_arts": { + "dice_count": 1, + "dice_value": 10 + }, + "unarmored_movement": 25 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-16" + }, + { + "index": "monk-17", + "level": 17, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "monk-subclass-feature-3", + "name": "Subclass Feature", + "url": "/api/2024/features/monk-subclass-feature-3-nyi" + } + ], + "class_specific": { + "focus_points": 17, + "martial_arts": { + "dice_count": 1, + "dice_value": 12 + }, + "unarmored_movement": 25 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-17" + }, + { + "index": "monk-18", + "level": 18, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "monk-superior-defense", + "name": "Superior Defense", + "url": "/api/2024/features/monk-superior-defense-nyi" + } + ], + "class_specific": { + "focus_points": 18, + "martial_arts": { + "dice_count": 1, + "dice_value": 12 + }, + "unarmored_movement": 30 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-18" + }, + { + "index": "monk-19", + "level": 19, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "monk-epic-boon", + "name": "Epic Boon", + "url": "/api/2024/features/monk-epic-boon-nyi" + } + ], + "class_specific": { + "focus_points": 19, + "martial_arts": { + "dice_count": 1, + "dice_value": 12 + }, + "unarmored_movement": 30 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-19" + }, + { + "index": "monk-20", + "level": 20, + "ability_score_bonuses": 4, + "prof_bonus": 6, + "features": [ + { + "index": "monk-body-and-mind", + "name": "Body and Mind", + "url": "/api/2024/features/monk-body-and-mind-nyi" + } + ], + "class_specific": { + "focus_points": 20, + "martial_arts": { + "dice_count": 1, + "dice_value": 12 + }, + "unarmored_movement": 30 + }, + "class": { + "index": "monk", + "name": "Monk", + "url": "/api/2024/classes/monk" + }, + "url": "/api/2024/levels/monk-20" + }, + { + "index": "rogue-1", + "level": 1, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "rogue-expertise-1", + "name": "Expertise", + "url": "/api/2024/features/rogue-expertise-1-nyi" + }, + { + "index": "rogue-sneak-attack", + "name": "Sneak Attack", + "url": "/api/2024/features/rogue-sneak-attack-nyi" + }, + { + "index": "rogue-thieves-cant", + "name": "Thieves' Cant", + "url": "/api/2024/features/rogue-thieves-cant-nyi" + }, + { + "index": "rogue-weapon-mastery", + "name": "Weapon Mastery", + "url": "/api/2024/features/rogue-weapon-mastery-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 1, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-1" + }, + { + "index": "rogue-2", + "level": 2, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "rogue-cunning-action", + "name": "Cunning Action", + "url": "/api/2024/features/rogue-cunning-action-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 1, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-2" + }, + { + "index": "rogue-3", + "level": 3, + "ability_score_bonuses": 0, + "prof_bonus": 2, + "features": [ + { + "index": "rogue-rogue-subclass", + "name": "Rogue Subclass", + "url": "/api/2024/features/rogue-rogue-subclass-nyi" + }, + { + "index": "rogue-steady-aim", + "name": "Steady Aim", + "url": "/api/2024/features/rogue-steady-aim-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 2, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-3" + }, + { + "index": "rogue-4", + "level": 4, + "ability_score_bonuses": 1, + "prof_bonus": 2, + "features": [ + { + "index": "rogue-ability-score-improvement-1", + "name": "Ability Score Improvement", + "url": "/api/2024/features/rogue-ability-score-improvement-1-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 2, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-4" + }, + { + "index": "rogue-5", + "level": 5, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "rogue-cunning-strike", + "name": "Cunning Strike", + "url": "/api/2024/features/rogue-cunning-strike-nyi" + }, + { + "index": "rogue-uncanny-dodge", + "name": "Uncanny Dodge", + "url": "/api/2024/features/rogue-uncanny-dodge-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 3, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-5" + }, + { + "index": "rogue-6", + "level": 6, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "rogue-expertise-2", + "name": "Expertise", + "url": "/api/2024/features/rogue-expertise-2-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 3, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-6" + }, + { + "index": "rogue-7", + "level": 7, + "ability_score_bonuses": 1, + "prof_bonus": 3, + "features": [ + { + "index": "rogue-evasion", + "name": "Evasion", + "url": "/api/2024/features/rogue-evasion-nyi" + }, + { + "index": "rogue-reliable-talent", + "name": "Reliable Talent", + "url": "/api/2024/features/rogue-reliable-talent-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 4, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-7" + }, + { + "index": "rogue-8", + "level": 8, + "ability_score_bonuses": 2, + "prof_bonus": 3, + "features": [ + { + "index": "rogue-ability-score-improvement-2", + "name": "Ability Score Improvement", + "url": "/api/2024/features/rogue-ability-score-improvement-2-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 4, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-8" + }, + { + "index": "rogue-9", + "level": 9, + "ability_score_bonuses": 2, + "prof_bonus": 4, + "features": [ + { + "index": "rogue-subclass-feature-1", + "name": "Subclass Feature", + "url": "/api/2024/features/rogue-subclass-feature-1-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 5, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-9" + }, + { + "index": "rogue-10", + "level": 10, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "rogue-ability-score-improvement-3", + "name": "Ability Score Improvement", + "url": "/api/2024/features/rogue-ability-score-improvement-3-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 5, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-10" + }, + { + "index": "rogue-11", + "level": 11, + "ability_score_bonuses": 3, + "prof_bonus": 4, + "features": [ + { + "index": "rogue-improved-cunning-strike", + "name": "Improved Cunning Strike", + "url": "/api/2024/features/rogue-improved-cunning-strike-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 6, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-11" + }, + { + "index": "rogue-12", + "level": 12, + "ability_score_bonuses": 4, + "prof_bonus": 4, + "features": [ + { + "index": "rogue-ability-score-improvement-4", + "name": "Ability Score Improvement", + "url": "/api/2024/features/rogue-ability-score-improvement-4-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 6, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-12" + }, + { + "index": "rogue-13", + "level": 13, + "ability_score_bonuses": 4, + "prof_bonus": 5, + "features": [ + { + "index": "rogue-subclass-feature-2", + "name": "Subclass Feature", + "url": "/api/2024/features/rogue-subclass-feature-2-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 7, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-13" + }, + { + "index": "rogue-14", + "level": 14, + "ability_score_bonuses": 4, + "prof_bonus": 5, + "features": [ + { + "index": "rogue-devious-strikes", + "name": "Devious Strikes", + "url": "/api/2024/features/rogue-devious-strikes-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 7, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-14" + }, + { + "index": "rogue-15", + "level": 15, + "ability_score_bonuses": 4, + "prof_bonus": 5, + "features": [ + { + "index": "rogue-slippery-mind", + "name": "Slippery Mind", + "url": "/api/2024/features/rogue-slippery-mind-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 8, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-15" + }, + { + "index": "rogue-16", + "level": 16, + "ability_score_bonuses": 5, + "prof_bonus": 5, + "features": [ + { + "index": "rogue-ability-score-improvement-5", + "name": "Ability Score Improvement", + "url": "/api/2024/features/rogue-ability-score-improvement-5-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 8, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-16" + }, + { + "index": "rogue-17", + "level": 17, + "ability_score_bonuses": 5, + "prof_bonus": 6, + "features": [ + { + "index": "rogue-subclass-feature-3", + "name": "Subclass Feature", + "url": "/api/2024/features/rogue-subclass-feature-3-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 9, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-17" + }, + { + "index": "rogue-18", + "level": 18, + "ability_score_bonuses": 5, + "prof_bonus": 6, + "features": [ + { + "index": "rogue-elusive", + "name": "Elusive", + "url": "/api/2024/features/rogue-elusive-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 9, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-18" + }, + { + "index": "rogue-19", + "level": 19, + "ability_score_bonuses": 5, + "prof_bonus": 6, + "features": [ + { + "index": "rogue-epic-boon", + "name": "Epic Boon", + "url": "/api/2024/features/rogue-epic-boon-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 10, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-19" + }, + { + "index": "rogue-20", + "level": 20, + "ability_score_bonuses": 5, + "prof_bonus": 6, + "features": [ + { + "index": "rogue-stroke-of-luck", + "name": "Stroke of Luck", + "url": "/api/2024/features/rogue-stroke-of-luck-nyi" + } + ], + "class_specific": { + "sneak_attack": { + "dice_count": 10, + "dice_value": 6 + } + }, + "class": { + "index": "rogue", + "name": "Rogue", + "url": "/api/2024/classes/rogue" + }, + "url": "/api/2024/levels/rogue-20" + } +] diff --git a/src/2024/schemas/5e-SRD-Levels.ts b/src/2024/schemas/5e-SRD-Levels.ts new file mode 100644 index 00000000..0d30f7ba --- /dev/null +++ b/src/2024/schemas/5e-SRD-Levels.ts @@ -0,0 +1,47 @@ +import { z } from 'zod'; +import { APIReferenceSchema } from '../../schemas/common'; + +const ClassSpecificSchema = z.object({ + action_surges: z.number().optional(), + extra_attacks: z.number().optional(), + focus_points: z.number().optional(), + indomitable_uses: z.number().optional(), + martial_arts: z.object({ dice_count: z.number(), dice_value: z.number() }).optional(), + rage_count: z.number().optional(), + rage_damage_bonus: z.number().optional(), + sneak_attack: z.object({ dice_count: z.number(), dice_value: z.number() }).optional(), + unarmored_movement: z.number().optional(), +}); + +const LevelSpellcastingSchema = z.object({ + cantrips_known: z.number().optional(), + spell_slots_level_1: z.number(), + spell_slots_level_2: z.number(), + spell_slots_level_3: z.number(), + spell_slots_level_4: z.number(), + spell_slots_level_5: z.number(), + spell_slots_level_6: z.number().optional(), + spell_slots_level_7: z.number().optional(), + spell_slots_level_8: z.number().optional(), + spell_slots_level_9: z.number().optional(), + spells_known: z.number().optional(), +}); + +const SubclassSpecificSchema = z.object({ + additional_magical_secrets_max_lvl: z.number().optional(), + aura_range: z.number().optional(), +}); + +export const LevelSchema = z.object({ + index: z.string(), + level: z.number(), + ability_score_bonuses: z.number().optional(), + prof_bonus: z.number().optional(), + features: z.array(APIReferenceSchema).optional(), + class: APIReferenceSchema, + class_specific: ClassSpecificSchema.optional(), + spellcasting: LevelSpellcastingSchema.optional(), + subclass: APIReferenceSchema.optional(), + subclass_specific: SubclassSpecificSchema.optional(), + url: z.string(), +}); diff --git a/src/2024/tests/schemas.test.ts b/src/2024/tests/schemas.test.ts index 6cffdafa..f0f99b85 100644 --- a/src/2024/tests/schemas.test.ts +++ b/src/2024/tests/schemas.test.ts @@ -17,6 +17,7 @@ import Skills from '../5e-SRD-Skills.json' with { type: 'json' }; import Species from '../5e-SRD-Species.json' with { type: 'json' }; import Subclasses from '../5e-SRD-Subclasses.json' with { type: 'json' }; import Classes from '../5e-SRD-Classes.json' with { type: 'json' }; +import Levels from '../5e-SRD-Levels.json' with { type: 'json' }; import Subspecies from '../5e-SRD-Subspecies.json' with { type: 'json' }; import Traits from '../5e-SRD-Traits.json' with { type: 'json' }; import WeaponMasteryProperties from '../5e-SRD-Weapon-Mastery-Properties.json' with { type: 'json' }; @@ -38,6 +39,7 @@ import { SkillSchema } from '../schemas/5e-SRD-Skills'; import { SpeciesSchema } from '../schemas/5e-SRD-Species'; import { SubclassSchema } from '../schemas/5e-SRD-Subclasses'; import { ClassSchema } from '../schemas/5e-SRD-Classes'; +import { LevelSchema } from '../schemas/5e-SRD-Levels'; import { SubspeciesSchema } from '../schemas/5e-SRD-Subspecies'; import { TraitSchema } from '../schemas/5e-SRD-Traits'; import { WeaponMasteryPropertySchema } from '../schemas/5e-SRD-Weapon-Mastery-Properties'; @@ -67,6 +69,7 @@ describe('2024 schemas', () => { it('species', () => testAll(Species, SpeciesSchema)); it('subclasses', () => testAll(Subclasses, SubclassSchema)); it('classes', () => testAll(Classes, ClassSchema)); + it('levels', () => testAll(Levels, LevelSchema)); it('subspecies', () => testAll(Subspecies, SubspeciesSchema)); it('traits', () => testAll(Traits, TraitSchema)); it('weapon mastery properties', () => testAll(WeaponMasteryProperties, WeaponMasteryPropertySchema)); diff --git a/src/2024/tests/tables.test.ts b/src/2024/tests/tables.test.ts index 642536c6..673e691c 100644 --- a/src/2024/tests/tables.test.ts +++ b/src/2024/tests/tables.test.ts @@ -44,19 +44,25 @@ describe('api references', () => { recurseIntoObject(topLevelEntry, (subEntry) => { if (!Object.prototype.hasOwnProperty.call(subEntry, 'url')) return; - if (resources[subEntry.url as string] === undefined) { - errors.push(`${filename}: URL '${subEntry.url}' not found.`); + const url = subEntry.url as string; + if (url.endsWith('-nyi')) { + console.warn(`${filename}: NYI URL '${url}' skipped.`); + return; + } + + if (resources[url] === undefined) { + errors.push(`${filename}: URL '${url}' not found.`); } else { - const resource = resources[subEntry.url as string]; + const resource = resources[url]; if (resource.name !== undefined && resource.name !== subEntry.name) { errors.push( - `${filename}: Name mismatch for reference to '${subEntry.url}', '${subEntry.name}' should be '${resource.name}'` + `${filename}: Name mismatch for reference to '${url}', '${subEntry.name}' should be '${resource.name}'` ); } if (subEntry.index !== undefined && resource.index !== subEntry.index) { errors.push( - `${filename}: Index mismatch for reference to '${subEntry.url}', '${subEntry.index}' should be '${resource.index}'` + `${filename}: Index mismatch for reference to '${url}', '${subEntry.index}' should be '${resource.index}'` ); } }