dnd5e-character-builder 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ben Strackany
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: dnd5e-character-builder
3
+ Version: 0.1.0
4
+ Summary: Build, level up and check D&D 2024 (SRD 5.2.1) character sheets, on top of the nat20 rules engine.
5
+ Keywords: dnd,5e,2024,srd,character-sheet,ttrpg
6
+ Author: Ben Strackany
7
+ Author-email: Ben Strackany <ben@developmentnow.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ License-File: src/dnd5e_character_builder/data/5e-database/LICENSE.md
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Topic :: Games/Entertainment :: Role-Playing
14
+ Classifier: Typing :: Typed
15
+ Requires-Dist: dnd5e-engine>=0.7,<0.8
16
+ Requires-Dist: dnd5e-srd-data>=0.7,<0.8
17
+ Requires-Dist: pydantic>=2.14.2
18
+ Requires-Dist: pyyaml>=6.0.3
19
+ Requires-Python: >=3.14
20
+ Project-URL: Repository, https://github.com/bstrackany/dnd5e-character-builder
21
+ Description-Content-Type: text/markdown
22
+
23
+ # dnd5e-character-builder
24
+
25
+ Build, level up and check D&D 2024 (SRD 5.2.1) character sheets in Python.
26
+
27
+ A sheet written by a person or a model is free text: "Rogue (Arcane Trickster)", "Shield — level 1, prepared".
28
+ This library reads it against the rules, lists every problem at once, writes in the numbers the rules derive, and says
29
+ what the character may still pick now or at the next level. It sits on [nat20](https://github.com/tapestria/nat20)'s
30
+ rules engine and SRD corpus; nat20 decides whether a build is constructible and derives its HP, AC, saves and slots.
31
+ This library adds the parts of a sheet nat20 takes as given: class spell lists, prepared and cantrip counts, wizard
32
+ spellbooks, feat slots, choice counts, skills and ability-score generation.
33
+
34
+ ```
35
+ pip install dnd5e-character-builder
36
+ ```
37
+
38
+ Python 3.14 or later.
39
+
40
+ ## Use
41
+
42
+ ```python
43
+ from dnd5e_character_builder import Feat, Sheet, check, fill, level_up, options
44
+
45
+ sheet = Sheet.model_validate({
46
+ 'name': 'Ana',
47
+ 'species': 'Human',
48
+ 'class': 'Fighter',
49
+ 'level': 4,
50
+ 'subclass': 'Champion',
51
+ 'background': 'Soldier',
52
+ 'abilities': {'str': 17, 'dex': 15, 'con': 14, 'int': 8, 'wis': 12, 'cha': 10},
53
+ 'skills': ['Athletics', 'Intimidation', 'Perception', 'Survival', 'Acrobatics'],
54
+ 'feats': ['Savage Attacker', 'Alert', Feat(name='Grappler', level=4)],
55
+ 'choices': ['Defense'],
56
+ 'inventory': ['Chain Mail', 'Longsword', 'Shield'],
57
+ })
58
+
59
+ sheet = fill(sheet) # max_hp, ac, saving_throws and spell_slots from nat20
60
+ for issue in check(sheet): # every problem at once
61
+ print(issue.severity, issue.code, issue.message)
62
+
63
+ options(sheet) # every pick at this level (subclass, feature choices, feat slots, skills, spells) and what fills it
64
+ level_up(sheet, 5) # what level 5 gives automatically and which picks it makes due
65
+ ```
66
+
67
+ `level_up` and `options` report; neither changes the sheet. Write the picks, then run `fill` and `check`.
68
+
69
+ `build_spec(sheet)` and `derive(sheet)` give nat20's `CharacterBuildSpec` and `DerivedSheet` for a sheet, for hosts
70
+ that run nat20 combat.
71
+
72
+ ### The sheet
73
+
74
+ | Field | Holds |
75
+ |---|---|
76
+ | `species`, `class`, `subclass`, `background` | Names as written. A subclass in parentheses after the class is read too. |
77
+ | | A background the content does not know is a custom one: +2/+1 or +1/+1/+1 to any abilities, any two skills and any one origin feat. |
78
+ | `level` | 1-20, one class. |
79
+ | `abilities` | Final scores by `str` ... `cha`. |
80
+ | `skills`, `expertise` | Skill names; `"Stealth (expertise)"` in `skills` also counts as Expertise. |
81
+ | `feats` | `Feat(name, level)`. `level` is the class level whose Ability Score Improvement the feat took; leave it `None` for an origin feat. A bare string is a feat with no level. |
82
+ | `choices` | Class, subclass and species options by name: `"Archery"`, `"Agonizing Blast"`, `"Protector"`. |
83
+ | `spells` | Cantrips known and spells prepared. |
84
+ | `spellbook` | A wizard's level 1+ spells. |
85
+ | `magic_initiate` | One record per Magic Initiate: list, ability, two cantrips, one level 1 spell. |
86
+ | `max_hp`, `ac`, `spell_slots`, `saving_throws` | The sheet's numbers; `fill` replaces them. |
87
+
88
+ ### Issues
89
+
90
+ Each `Issue` has a stable `code`, a `severity` (`error` breaks the rules, `warning` is likely wrong, `info` is a
91
+ note), the sheet `field`, a `message` and a `detail` dict.
92
+
93
+ | Code | Severity | Meaning |
94
+ |---|---|---|
95
+ | `build.rejected` | error | nat20 cannot construct the build; its reason is in `detail.reason` |
96
+ | `class.unknown`, `class.multiclass` | error | No class the content knows, or more than one (multiclass is not supported yet) |
97
+ | `species.unknown`, `species.missing` | error | |
98
+ | `subclass.unknown`, `subclass.missing` | error | Not in the content, or none chosen at the subclass level |
99
+ | `feat.unknown` | error | Not in the content |
100
+ | `feat.no_slot` | error | Nothing gives this feat: no Ability Score Improvement at that level, the slot is taken, or an origin feat beyond the background's and species' |
101
+ | `feat.level_missing` | info | A feat in an Ability Score Improvement without the level it was taken at; it is checked against the first free one |
102
+ | `feat.slot_empty` | warning | An Ability Score Improvement level with no feat recorded |
103
+ | `feat.missing` | warning | The background's feat (a custom background's chosen origin feat) is not on the sheet |
104
+ | `abilities.not_buildable` | warning | Not the standard array or a 27-point buy plus the background's increase and the Ability Score Improvements taken |
105
+ | `abilities.over_cap` | error | Above 20 (30 with an Epic Boon) |
106
+ | `saves.mismatch` | error / warning | Not the class's saving throws (warning when the sheet lists none) |
107
+ | `skills.unknown` | error | Not a skill |
108
+ | `skills.too_many`, `skills.too_few` | warning | More or fewer than background, class, species, subclass and feats give |
109
+ | `skills.granted_missing` | warning | A skill given outright is not on the sheet |
110
+ | `expertise.too_many`, `expertise.not_proficient` | error | |
111
+ | `choice.unknown`, `choice.too_many` | error | Not an option this build has, or more picks than its level allows |
112
+ | `choice.missing` | warning | Picks still due |
113
+ | `spell.unknown` | error | Not in the content |
114
+ | `spell.not_on_list` | error | Not on the class list and nothing else grants it |
115
+ | `spell.too_many_cantrips`, `spell.too_many_prepared` | error | Over the class table; granted spells do not count |
116
+ | `spell.above_slot` | error | Prepared above the highest spell slot |
117
+ | `spellbook.missing`, `spellbook.not_wizard_spell` | error | Prepared but not in the book; a book entry that is not a level 1+ wizard spell |
118
+ | `spellbook.too_few` | warning | Fewer than 6 + 2 per level after the first |
119
+ | `magic_initiate.not_on_list`, `magic_initiate.too_many` | error | |
120
+ | `magic_initiate.unrecorded` | info | A Magic Initiate whose picks are not recorded |
121
+ | `max_hp.mismatch`, `ac.mismatch`, `spell_slots.mismatch` | warning | The sheet's number differs from the derived one |
122
+ | `numbers.unchecked` | info | HP, AC and slots not compared because something the derivation needs is unknown |
123
+
124
+ Spells a character has without preparing them do not count against the class table: species and subclass grants,
125
+ spells a feature's own text grants ("You always have the Hunter's Mark spell prepared"), the Circle of the Land table
126
+ the sheet matches best, Mystic Arcanum, Magic Initiate, and the College of Lore's Magical Discoveries.
127
+
128
+ ## Homebrew
129
+
130
+ `load_content(*paths)` layers YAML or JSON files (or folders of them) over the SRD; pass the result as
131
+ `content=` to any call. A file holds nat20-schema entries and this library's tables:
132
+
133
+ ```yaml
134
+ subclasses: [...] # nat20 schema; also classes, species, backgrounds, feats, features, spells
135
+ spell_lists: # class -> spell level -> spells added to its list; a name with no entry is known by name and level
136
+ wizard: {1: [Chromatic Orb], 2: [Dragon's Breath]}
137
+ subclass_spells: # always-prepared spells by class level
138
+ oath-of-glory: {3: [Guiding Bolt, Heroism], 5: [Enhance Ability, Magic Weapon]}
139
+ subclass_spell_choices: # one table per option, as Circle of the Land
140
+ circle-of-the-reef: {shallows: {3: [Fog Cloud, Water Breathing]}}
141
+ class_tables: # cantrips and prepared per level, 20 each; a casting subclass also names progression and spell_list
142
+ eldritch-knight: {cantrips: [...], prepared: [...], progression: third, spell_list: wizard}
143
+ aliases:
144
+ subclasses: {glory: oath-of-glory}
145
+ ```
146
+
147
+ A path may also be a mapping of the same shape. `load_content(loader=...)` builds on your own nat20 `AssetLoader` instead of nat20's bundled corpus.
148
+
149
+ Loading refuses anything the library could not check, with a `ContentError` naming it: a spellcasting class without a
150
+ table or spell list, a subclass with a Spellcasting feature and no table, a subclass whose spells feature grants no
151
+ spells, a spell listed at the wrong level, an alias to nothing. Entries without nat20 provenance get a placeholder one
152
+ so they pass nat20's schema.
153
+
154
+ ## Limits
155
+
156
+ Single class only; see [BACKLOG.md](BACKLOG.md) for the rest.
157
+
158
+ ## Data and attribution
159
+
160
+ - Rules engine and SRD corpus: [nat20](https://github.com/tapestria/nat20) (`dnd5e-engine`, `dnd5e-srd-data`), MIT.
161
+ - Class spell lists and the Cantrips and Prepared Spells columns: `5e-SRD-Spells.json` and `5e-SRD-Levels.json` from
162
+ [5e-database](https://github.com/5e-bits/5e-database) (`src/2024/en`, commit `6f6299e`), MIT, vendored unchanged
163
+ in `src/dnd5e_character_builder/data/5e-database` with its licence. Two columns are corrected from the SRD in
164
+ `data/srd.yaml`.
165
+ - This work includes material from the System Reference Document 5.2.1 ("SRD 5.2.1") by Wizards of the Coast LLC,
166
+ available at <https://www.dndbeyond.com/srd>. The SRD 5.2.1 is licensed under the Creative Commons Attribution 4.0
167
+ International License, available at <https://creativecommons.org/licenses/by/4.0/legalcode>.
168
+
169
+ This library's code is MIT; see [LICENSE](LICENSE).
170
+
171
+ ## Develop
172
+
173
+ ```
174
+ uv sync
175
+ uv run ruff format --check src tests && uv run ruff check src tests && uv run mypy src tests && uv run pytest
176
+ ```
177
+
178
+ ## Release
179
+
180
+ Bump `version` in `pyproject.toml`, commit, then `git tag v<version> && git push origin v<version>`. The
181
+ `release.yml` workflow runs the checks and publishes to PyPI through Trusted Publishing.
@@ -0,0 +1,159 @@
1
+ # dnd5e-character-builder
2
+
3
+ Build, level up and check D&D 2024 (SRD 5.2.1) character sheets in Python.
4
+
5
+ A sheet written by a person or a model is free text: "Rogue (Arcane Trickster)", "Shield — level 1, prepared".
6
+ This library reads it against the rules, lists every problem at once, writes in the numbers the rules derive, and says
7
+ what the character may still pick now or at the next level. It sits on [nat20](https://github.com/tapestria/nat20)'s
8
+ rules engine and SRD corpus; nat20 decides whether a build is constructible and derives its HP, AC, saves and slots.
9
+ This library adds the parts of a sheet nat20 takes as given: class spell lists, prepared and cantrip counts, wizard
10
+ spellbooks, feat slots, choice counts, skills and ability-score generation.
11
+
12
+ ```
13
+ pip install dnd5e-character-builder
14
+ ```
15
+
16
+ Python 3.14 or later.
17
+
18
+ ## Use
19
+
20
+ ```python
21
+ from dnd5e_character_builder import Feat, Sheet, check, fill, level_up, options
22
+
23
+ sheet = Sheet.model_validate({
24
+ 'name': 'Ana',
25
+ 'species': 'Human',
26
+ 'class': 'Fighter',
27
+ 'level': 4,
28
+ 'subclass': 'Champion',
29
+ 'background': 'Soldier',
30
+ 'abilities': {'str': 17, 'dex': 15, 'con': 14, 'int': 8, 'wis': 12, 'cha': 10},
31
+ 'skills': ['Athletics', 'Intimidation', 'Perception', 'Survival', 'Acrobatics'],
32
+ 'feats': ['Savage Attacker', 'Alert', Feat(name='Grappler', level=4)],
33
+ 'choices': ['Defense'],
34
+ 'inventory': ['Chain Mail', 'Longsword', 'Shield'],
35
+ })
36
+
37
+ sheet = fill(sheet) # max_hp, ac, saving_throws and spell_slots from nat20
38
+ for issue in check(sheet): # every problem at once
39
+ print(issue.severity, issue.code, issue.message)
40
+
41
+ options(sheet) # every pick at this level (subclass, feature choices, feat slots, skills, spells) and what fills it
42
+ level_up(sheet, 5) # what level 5 gives automatically and which picks it makes due
43
+ ```
44
+
45
+ `level_up` and `options` report; neither changes the sheet. Write the picks, then run `fill` and `check`.
46
+
47
+ `build_spec(sheet)` and `derive(sheet)` give nat20's `CharacterBuildSpec` and `DerivedSheet` for a sheet, for hosts
48
+ that run nat20 combat.
49
+
50
+ ### The sheet
51
+
52
+ | Field | Holds |
53
+ |---|---|
54
+ | `species`, `class`, `subclass`, `background` | Names as written. A subclass in parentheses after the class is read too. |
55
+ | | A background the content does not know is a custom one: +2/+1 or +1/+1/+1 to any abilities, any two skills and any one origin feat. |
56
+ | `level` | 1-20, one class. |
57
+ | `abilities` | Final scores by `str` ... `cha`. |
58
+ | `skills`, `expertise` | Skill names; `"Stealth (expertise)"` in `skills` also counts as Expertise. |
59
+ | `feats` | `Feat(name, level)`. `level` is the class level whose Ability Score Improvement the feat took; leave it `None` for an origin feat. A bare string is a feat with no level. |
60
+ | `choices` | Class, subclass and species options by name: `"Archery"`, `"Agonizing Blast"`, `"Protector"`. |
61
+ | `spells` | Cantrips known and spells prepared. |
62
+ | `spellbook` | A wizard's level 1+ spells. |
63
+ | `magic_initiate` | One record per Magic Initiate: list, ability, two cantrips, one level 1 spell. |
64
+ | `max_hp`, `ac`, `spell_slots`, `saving_throws` | The sheet's numbers; `fill` replaces them. |
65
+
66
+ ### Issues
67
+
68
+ Each `Issue` has a stable `code`, a `severity` (`error` breaks the rules, `warning` is likely wrong, `info` is a
69
+ note), the sheet `field`, a `message` and a `detail` dict.
70
+
71
+ | Code | Severity | Meaning |
72
+ |---|---|---|
73
+ | `build.rejected` | error | nat20 cannot construct the build; its reason is in `detail.reason` |
74
+ | `class.unknown`, `class.multiclass` | error | No class the content knows, or more than one (multiclass is not supported yet) |
75
+ | `species.unknown`, `species.missing` | error | |
76
+ | `subclass.unknown`, `subclass.missing` | error | Not in the content, or none chosen at the subclass level |
77
+ | `feat.unknown` | error | Not in the content |
78
+ | `feat.no_slot` | error | Nothing gives this feat: no Ability Score Improvement at that level, the slot is taken, or an origin feat beyond the background's and species' |
79
+ | `feat.level_missing` | info | A feat in an Ability Score Improvement without the level it was taken at; it is checked against the first free one |
80
+ | `feat.slot_empty` | warning | An Ability Score Improvement level with no feat recorded |
81
+ | `feat.missing` | warning | The background's feat (a custom background's chosen origin feat) is not on the sheet |
82
+ | `abilities.not_buildable` | warning | Not the standard array or a 27-point buy plus the background's increase and the Ability Score Improvements taken |
83
+ | `abilities.over_cap` | error | Above 20 (30 with an Epic Boon) |
84
+ | `saves.mismatch` | error / warning | Not the class's saving throws (warning when the sheet lists none) |
85
+ | `skills.unknown` | error | Not a skill |
86
+ | `skills.too_many`, `skills.too_few` | warning | More or fewer than background, class, species, subclass and feats give |
87
+ | `skills.granted_missing` | warning | A skill given outright is not on the sheet |
88
+ | `expertise.too_many`, `expertise.not_proficient` | error | |
89
+ | `choice.unknown`, `choice.too_many` | error | Not an option this build has, or more picks than its level allows |
90
+ | `choice.missing` | warning | Picks still due |
91
+ | `spell.unknown` | error | Not in the content |
92
+ | `spell.not_on_list` | error | Not on the class list and nothing else grants it |
93
+ | `spell.too_many_cantrips`, `spell.too_many_prepared` | error | Over the class table; granted spells do not count |
94
+ | `spell.above_slot` | error | Prepared above the highest spell slot |
95
+ | `spellbook.missing`, `spellbook.not_wizard_spell` | error | Prepared but not in the book; a book entry that is not a level 1+ wizard spell |
96
+ | `spellbook.too_few` | warning | Fewer than 6 + 2 per level after the first |
97
+ | `magic_initiate.not_on_list`, `magic_initiate.too_many` | error | |
98
+ | `magic_initiate.unrecorded` | info | A Magic Initiate whose picks are not recorded |
99
+ | `max_hp.mismatch`, `ac.mismatch`, `spell_slots.mismatch` | warning | The sheet's number differs from the derived one |
100
+ | `numbers.unchecked` | info | HP, AC and slots not compared because something the derivation needs is unknown |
101
+
102
+ Spells a character has without preparing them do not count against the class table: species and subclass grants,
103
+ spells a feature's own text grants ("You always have the Hunter's Mark spell prepared"), the Circle of the Land table
104
+ the sheet matches best, Mystic Arcanum, Magic Initiate, and the College of Lore's Magical Discoveries.
105
+
106
+ ## Homebrew
107
+
108
+ `load_content(*paths)` layers YAML or JSON files (or folders of them) over the SRD; pass the result as
109
+ `content=` to any call. A file holds nat20-schema entries and this library's tables:
110
+
111
+ ```yaml
112
+ subclasses: [...] # nat20 schema; also classes, species, backgrounds, feats, features, spells
113
+ spell_lists: # class -> spell level -> spells added to its list; a name with no entry is known by name and level
114
+ wizard: {1: [Chromatic Orb], 2: [Dragon's Breath]}
115
+ subclass_spells: # always-prepared spells by class level
116
+ oath-of-glory: {3: [Guiding Bolt, Heroism], 5: [Enhance Ability, Magic Weapon]}
117
+ subclass_spell_choices: # one table per option, as Circle of the Land
118
+ circle-of-the-reef: {shallows: {3: [Fog Cloud, Water Breathing]}}
119
+ class_tables: # cantrips and prepared per level, 20 each; a casting subclass also names progression and spell_list
120
+ eldritch-knight: {cantrips: [...], prepared: [...], progression: third, spell_list: wizard}
121
+ aliases:
122
+ subclasses: {glory: oath-of-glory}
123
+ ```
124
+
125
+ A path may also be a mapping of the same shape. `load_content(loader=...)` builds on your own nat20 `AssetLoader` instead of nat20's bundled corpus.
126
+
127
+ Loading refuses anything the library could not check, with a `ContentError` naming it: a spellcasting class without a
128
+ table or spell list, a subclass with a Spellcasting feature and no table, a subclass whose spells feature grants no
129
+ spells, a spell listed at the wrong level, an alias to nothing. Entries without nat20 provenance get a placeholder one
130
+ so they pass nat20's schema.
131
+
132
+ ## Limits
133
+
134
+ Single class only; see [BACKLOG.md](BACKLOG.md) for the rest.
135
+
136
+ ## Data and attribution
137
+
138
+ - Rules engine and SRD corpus: [nat20](https://github.com/tapestria/nat20) (`dnd5e-engine`, `dnd5e-srd-data`), MIT.
139
+ - Class spell lists and the Cantrips and Prepared Spells columns: `5e-SRD-Spells.json` and `5e-SRD-Levels.json` from
140
+ [5e-database](https://github.com/5e-bits/5e-database) (`src/2024/en`, commit `6f6299e`), MIT, vendored unchanged
141
+ in `src/dnd5e_character_builder/data/5e-database` with its licence. Two columns are corrected from the SRD in
142
+ `data/srd.yaml`.
143
+ - This work includes material from the System Reference Document 5.2.1 ("SRD 5.2.1") by Wizards of the Coast LLC,
144
+ available at <https://www.dndbeyond.com/srd>. The SRD 5.2.1 is licensed under the Creative Commons Attribution 4.0
145
+ International License, available at <https://creativecommons.org/licenses/by/4.0/legalcode>.
146
+
147
+ This library's code is MIT; see [LICENSE](LICENSE).
148
+
149
+ ## Develop
150
+
151
+ ```
152
+ uv sync
153
+ uv run ruff format --check src tests && uv run ruff check src tests && uv run mypy src tests && uv run pytest
154
+ ```
155
+
156
+ ## Release
157
+
158
+ Bump `version` in `pyproject.toml`, commit, then `git tag v<version> && git push origin v<version>`. The
159
+ `release.yml` workflow runs the checks and publishes to PyPI through Trusted Publishing.
@@ -0,0 +1,84 @@
1
+ [project]
2
+ name = "dnd5e-character-builder"
3
+ version = "0.1.0"
4
+ description = "Build, level up and check D&D 2024 (SRD 5.2.1) character sheets, on top of the nat20 rules engine."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = [
8
+ "LICENSE",
9
+ "src/dnd5e_character_builder/data/5e-database/LICENSE.md",
10
+ ]
11
+ keywords = [
12
+ "dnd",
13
+ "5e",
14
+ "2024",
15
+ "srd",
16
+ "character-sheet",
17
+ "ttrpg",
18
+ ]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Topic :: Games/Entertainment :: Role-Playing",
23
+ "Typing :: Typed",
24
+ ]
25
+ requires-python = ">=3.14"
26
+ dependencies = [
27
+ "dnd5e-engine>=0.7,<0.8",
28
+ "dnd5e-srd-data>=0.7,<0.8",
29
+ "pydantic>=2.14.2",
30
+ "pyyaml>=6.0.3",
31
+ ]
32
+
33
+ [[project.authors]]
34
+ name = "Ben Strackany"
35
+ email = "ben@developmentnow.com"
36
+
37
+ [project.urls]
38
+ Repository = "https://github.com/bstrackany/dnd5e-character-builder"
39
+
40
+ [build-system]
41
+ requires = ["uv_build>=0.11.26,<0.12.0"]
42
+ build-backend = "uv_build"
43
+
44
+ [dependency-groups]
45
+ dev = [
46
+ "mypy>=2.4.1",
47
+ "pytest>=9.2.0",
48
+ "ruff>=0.17.3",
49
+ "types-pyyaml>=6.0.12.20261030",
50
+ ]
51
+
52
+ [tool.ruff]
53
+ line-length = 140
54
+ target-version = "py314"
55
+
56
+ [tool.ruff.lint]
57
+ select = [
58
+ "E",
59
+ "F",
60
+ "W",
61
+ "I",
62
+ "B",
63
+ "UP",
64
+ "SIM",
65
+ "RUF",
66
+ ]
67
+ ignore = [
68
+ "E501",
69
+ "RUF001",
70
+ ]
71
+
72
+ [tool.ruff.format]
73
+ quote-style = "single"
74
+
75
+ [tool.mypy]
76
+ strict = true
77
+ warn_unused_ignores = true
78
+
79
+ [[tool.mypy.overrides]]
80
+ module = ["d20.*"]
81
+ ignore_missing_imports = true
82
+
83
+ [tool.pytest.ini_options]
84
+ testpaths = ["tests"]
@@ -0,0 +1,61 @@
1
+ [project]
2
+ name = "dnd5e-character-builder"
3
+ version = "0.1.0"
4
+ description = "Build, level up and check D&D 2024 (SRD 5.2.1) character sheets, on top of the nat20 rules engine."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE", "src/dnd5e_character_builder/data/5e-database/LICENSE.md"]
8
+ keywords = ["dnd", "5e", "2024", "srd", "character-sheet", "ttrpg"]
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.14",
12
+ "Topic :: Games/Entertainment :: Role-Playing",
13
+ "Typing :: Typed",
14
+ ]
15
+ authors = [
16
+ { name = "Ben Strackany", email = "ben@developmentnow.com" }
17
+ ]
18
+ requires-python = ">=3.14"
19
+ dependencies = [
20
+ "dnd5e-engine>=0.7,<0.8",
21
+ "dnd5e-srd-data>=0.7,<0.8",
22
+ "pydantic>=2.14.2",
23
+ "pyyaml>=6.0.3",
24
+ ]
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/bstrackany/dnd5e-character-builder"
28
+
29
+ [build-system]
30
+ requires = ["uv_build>=0.11.26,<0.12.0"]
31
+ build-backend = "uv_build"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "mypy>=2.4.1",
36
+ "pytest>=9.2.0",
37
+ "ruff>=0.17.3",
38
+ "types-pyyaml>=6.0.12.20261030",
39
+ ]
40
+
41
+ [tool.ruff]
42
+ line-length = 140
43
+ target-version = "py314"
44
+
45
+ [tool.ruff.lint]
46
+ select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF"]
47
+ ignore = ["E501", "RUF001"] # the formatter wraps code; long rule quotes and typographic dashes are deliberate
48
+
49
+ [tool.ruff.format]
50
+ quote-style = "single"
51
+
52
+ [tool.mypy]
53
+ strict = true
54
+ warn_unused_ignores = true
55
+
56
+ [[tool.mypy.overrides]]
57
+ module = ["d20.*"]
58
+ ignore_missing_imports = true
59
+
60
+ [tool.pytest.ini_options]
61
+ testpaths = ["tests"]
@@ -0,0 +1,34 @@
1
+ """Build, level up and check 2024 (SRD 5.2.1) character sheets, on top of the nat20 rules engine.
2
+
3
+ ``check`` lists every problem with a sheet; ``fill`` writes in the numbers nat20 derives; ``options`` says what a sheet
4
+ may still pick; ``level_up`` reports what a new level gives and which picks it makes due. ``load_content`` layers
5
+ homebrew over the SRD.
6
+ """
7
+
8
+ from dnd5e_character_builder.advance import Choice, FeatSlot, Gained, LevelUp, Options, SpellOption, SpellOptions, level_up, options
9
+ from dnd5e_character_builder.check import build_spec, check, derive, fill
10
+ from dnd5e_character_builder.content import Content, ContentError, load_content
11
+ from dnd5e_character_builder.sheet import Feat, Issue, MagicInitiate, Sheet
12
+
13
+ __all__ = [
14
+ 'Choice',
15
+ 'Content',
16
+ 'ContentError',
17
+ 'Feat',
18
+ 'FeatSlot',
19
+ 'Gained',
20
+ 'Issue',
21
+ 'LevelUp',
22
+ 'MagicInitiate',
23
+ 'Options',
24
+ 'Sheet',
25
+ 'SpellOption',
26
+ 'SpellOptions',
27
+ 'build_spec',
28
+ 'check',
29
+ 'derive',
30
+ 'fill',
31
+ 'level_up',
32
+ 'load_content',
33
+ 'options',
34
+ ]