Level | 1 |
Proficiency Bonus | +2 |
replace [Character Name] with your character’s name replace [Race] with your character’s race guess what you do with [Class]
Fill in level (and update it when you level up) Proficiency Bonus will fill in automatically if you put the appropriate values in the function at the bottom of the file
Ability | Value | Mod | Save |
STR | 8 | -1 | -1 |
DEX *** | 14 | +2 | +4 |
CON | 12 | +1 | +1 |
INT | 10 | +0 | +0 |
WIS | 13 | +1 | +1 |
CHA *** | 15 | +2 | +4 |
Only need to fill in Value (and update as any of those values change)
Skills | Check | |
*** | Acrobatics (Dex) | +4 |
Animal Handling (Wis) | +1 | |
Arcana (Int) | +0 | |
Athletics (Str) | -1 | |
*** | Deception (Cha) | +4 |
History (Int) | +0 | |
Insight (Wis) | +1 | |
Intimidation (Cha) | +2 | |
Investigation (Int) | +0 | |
Medicine (Wis) | +1 | |
Nature (Int) | +0 | |
Perception (Wis) | +1 | |
*** | Performance (Cha) | +4 |
Persuasion (Cha) | +2 | |
Religion (Int) | +0 | |
*** | Sleight of Hand (Dex) | +4 |
*** | Stealth (Dex) | +4 |
Survival (Wis) | +1 |
Passive Wisdom (Perception): | 11 |
- Languages:
- Common, …
\pagebreak
Only put ‘***’ in the first column for skills you are proficient in, modifiers should change automatically
Fill in Languages
\FloatBarrier
Armor Class | Initiative Mod | Movement |
14 | +3 | 30 |
Spell Ability | Save DC | Attack Bonus |
CHA | 12 | 4 |
HP (Max 10) | Temp HP | HD (Total 1d8) |
10 | 0 | 1d8 |
Spell Level | Slots | Expended |
1 | 2 | 0 |
2 | 0 | 0 |
3 | 0 | 0 |
4 | 0 | 0 |
5 | 0 | 0 |
6 | 0 | 0 |
7 | 0 | 0 |
8 | 0 | 0 |
9 | 0 | 0 |
\FloatBarrier
\noindent INSPIRATION: Advantage
- DEATH FAILS
- XX
- DEATH SAVES
- X
This section should mostly be managed manually for now. Copy paste bits from ‘other status effects’ above that header as needed.
You can of course add rules for automatically populating hit dice, initiative, and armor class. These vary with class and feats so this is left up to you if you wish.
Feature | Note | Uses Left |
Bardic Inspiration | 1d6 | 4 |
Feature | Note |
By Popular Demand | music for food/lodging |
This section is mostly managed manually, Bard class and Entertainer background features are here as an example.
Cantrips Known | Spells Known | Max level |
2 | 4 | 1 |
Level | Spell |
0 | Prestidigitation |
0 | Mage Hand |
1 | Healing Word |
1 | Sleep |
1 | Speak with Animals |
1 | Faerie Fire |
Fill in links to spells and (if you spelled them carefully) the descriptions will automatically appear in the Spells section. Links in the Spell Column should be org-mode links of the style:
[[Spell Name][Spell Name]]
so that pdf links (and the spell descriptions) work properly .
Proficiencies Light armor, simple weapons, hand crossbows, longswords, rapiers, shortswords, disguise kit, …
Weapon | ATK Bonus | Damage/Type | Range |
Rapier | +5 | 1d8+3 piercing | |
Crossbow (l) | +5 | 1d8+3 piercing | 80/320 |
CP | 0 |
SP | 0 |
EP | 0 |
GP | 0 |
PP | 0 |
Total (in GP) | 0 |
Item | Weight | Notes | # |
*** Rapier | 5 | 1 | |
*** Leather Armor | 10 | AC=11+DEX | 1 |
*** Crossbow (l) | 5 | 1 | |
ball bearings x1000 | 2 | 70 | |
total weight | 160 | /120 max |
\noindent *** indicates that the item is currently equipped (being worn, sheathed, on back or otherwise accessible)
Weapons are managed manually (add your own rules to the table for each weapon if you desire).
Coin totals will be calculated automatically (just fill in how much of each).
Gear Weight are calculated automatically from the individual weights, quantity (under #). Coins are not included in this weight (who does that). Max weight is computed automatically from STR
I rolled 130 GP
item | weight | price (gp) | quantity |
leather armor | 10 | 10 | 1 |
crossbow (l) | 5 | 25 | 1 |
rapier | 2 | 25 | 1 |
ball bearings x1000 | 2 | 1 | 70 |
total | 157 | 130 |
- Alignment:
- Neutral
- Some Guy
- Notes on that dude
- Some Gal
- She seems pretty cool
Fill in and replace as desired none of this section is automated.
Fill in and replace as desired none of this section is automated.
Should automatically execute when exporting, reads from the SPELLS_KNOWN table to populate with each spell. Additional spells you want displayed can be added to this list at the beginning of the source.
List spells by level/class
import json
with open('./srd_spells/spells.json') as f:
spell_list = json.load(f)
for spell in spell_list:
if spell['level'] == '1' and 'bard' in spell['classes']:
print(spell['name'])
print(spell['range'])
print(spell['casting_time'])
print(spell['duration'])
print()
print(spell['description'].replace('*', '-'))
print()
print()
print()
<<Bardic Inspiration>> Bardic Inspiration. You can inspire others through stirring words or music. To do so, you use a bonus action on your turn to choose one creature other than yourself within 60 feet of you who can hear you. That creature gains one Bardic Inspiration die, a d6. Once within the next 10 minutes, the creature can roll the die and add the number rolled to one ability check, attack roll, or saving throw it makes. The creature can wait until after it rolls the d20 before deciding to use the Bardic Inspiration die, but must decide before the GM says whether the roll succeeds or fails. Once the Bardic Inspiration die is rolled, it is lost. A creature can have only one Bardic Inspiration die at a time. You can use this feature a number of times equal to your Charisma modifier (a minimum of once). You regain any expended uses when you finish a long rest. Your Bardic Inspiration die changes when you reach certain levels in this class. The die becomes a d8 at 5th level, a d10 at 10th level, and a d12 at 15th level.
<<By Popular Demand>> By Popular Demand. You can always find a place to perform, usually in an inn or tavern but possibly with a circus, at a theater, or even in a noble’s court. At such a place, you receive free lodging and food of a modest or comfortable standard (depending on the quality of the establishment), as long as you perform each night. In addition, your performance makes you something of a local figure. When strangers recognize you in a town where you have performed, they typically take a liking to you.
This bit isn’t automated yet, copy and paste from a wiki or elsewhere as you get features you’d like to list here. I’ve left two features here as examples, ‘Bardic Inspiration’ (from the Bard class) and ‘By Popular Demand’ (from the Entertainer background).
;;; First few bits are workarounds
;;; to get ox-dnd to work well with our tables.
(defun org-dnd-table (table contents info)
"Transcode a TABLE from Org to a D&D LaTeX table.
CONTENTS holds the contents of the table. INFO is a plist holding
contextual information."
(let ((header (car (org-element-property :header table)))
(align (org-export-read-attribute :attr_dnd table :align))
(color (org-export-read-attribute :attr_dnd table :color))
(size (org-export-read-attribute :attr_dnd table :size))
(separate (org-export-read-attribute :attr_dnd table :separate)))
(format
"%s%s"
(if header (format "\\header{%s}\n" header) "")
(replace-regexp-in-string
"begin{tabular.*"
(format "begin{DndTable}%s%s"
(if color (format "[color=%s]" color) "")
(if align (format "{%s}" align) (format "{%s}" (org-latex--align-string table info))))
(replace-regexp-in-string
"end{tabular}"
"end{DndTable}"
(replace-regexp-in-string
"{table}\\(.*\\)\n"
(format "{table}\\1\n%s"
(format "\\\\DndSetFonts[table-body-style=\\\\%s]\n" (if size size "large")))
;(if size (format "\\\\DndSetFonts[table-body-style=\\\\%s]\n" size) ""))
(replace-regexp-in-string
"\\\\\\(begin\\|end\\){center}\n?"
""
(replace-regexp-in-string
"\\\\centering"
""
(replace-regexp-in-string
"\\\\hline"
(if (not separate) ""
(format "\\\\end{DndTable}\n\\\\begin{DndTable}%s%s"
(if color (format "[color=%s]" color) "")
(if align (format "{%s}" align) (format "{%s}" (org-latex--align-string table info)))))
(org-latex-table table contents info))))))))))
(add-to-list 'org-latex-packages-alist '("" "placeins" `nil))
(setq org-latex-default-figure-position "htbp!")
; update table values on export
(advice-add 'org-dnd-export-to-pdf :before
(lambda (&rest r) (org-table-iterate-buffer-tables)))
; next few are for generating spell list
(defun get-spell-data (spell_names)
"Get alist with a key for each element of SPELL_NAMES.
Each value is the corresponding SRD data, no entry if element is not in SRD."
(let ((spell-data 'nil)
(json-array-type 'list) (json-false 'nil))
(dolist (spell (json-read-file "./srd_spells/spells.json") spell-data)
(let ((this_name (alist-get 'name spell)))
(if (member this_name spell_names)
(push (cons this_name spell) spell-data))))))
(defun link-to-spellname (link-string)
(replace-regexp-in-string
"\\[\\[\\(.*\\)\\]\\[.*\\]\\]" "\\1" link-string))
(defun srd-alist-to-format-alist (spell_data)
"corrections given srd alist return alist to use formatting latex strings"
(let-alist spell_data
(append `((component_string . ,.components.raw)
(level . ,(if (equal .level "cantrip")
0 .level))
(school . ,(if .ritual
(format "%s (ritual)" .school) .school))
(higher_levels_string . ,(if .higher_levels
(format "\n\n*At Higher Levels.* %s"
.higher_levels) ""))
(description . ,(replace-regexp-in-string "*" "-" .description)))
spell_data)))
(defun get-spell-names (spell_table additional_spells)
"parse the spell table for spell names"
(let ((spell_names additional_spells)) ; add aditional spells here
(dolist (spell (cdr spell_table) spell_names)
(push (link-to-spellname (cadr spell)) spell_names))))
(defun get-spell-string (spell_table additional_spells)
(let* ((spell_names (-sort #'string< (get-spell-names spell_table additional_spells)))
(spell_data (get-spell-data spell_names))
(template "\n <<${name}>>\n#+NAME: ${name}
#+ATTR_SPELL: :level ${level} :school ${school} :range ${range} :cast ${casting_time} :duration ${duration} :comp ${component_string}
#+BEGIN_SPELL\n ${description} ${higher_levels_string}\n #+END_SPELL\n")
(result ""))
(dolist (spell_n spell_names result)
(setq result
(concat result
(if (a-has-key spell_data spell_n)
(s-format template 'aget
(srd-alist-to-format-alist (a-get spell_data spell_n)))
(s-format template
(lambda (key map) (a-get map key "n/a"))
`(("name" . ,spell_n)
("description" . "Not available (liekly not in srd json)")))))))))
;;; Below are used in table calcualtions
(defun org-dnd-char-mod-string (mod)
(if (> mod -1)
(concat "+" (number-to-string mod))
(number-to-string mod))
)
(defun org-dnd-char-stat-to-mod (stat)
(let ((mod
(floor (/ (- (string-to-number stat) 10) 2.0))))
(org-dnd-char-mod-string mod))
)
(defun org-dnd-char-skill-to-attribute-index (skill)
(cond
((or
(string-match "Acrobatics" skill)
(string-match "Sleight of Hand" skill)
(string-match "Stealth" skill))
1) ;DEX
((or
(string-match "Animal Handling" skill)
(string-match "Insight" skill)
(string-match "Medicine" skill)
(string-match "Perception" skill)
(string-match "Survival" skill))
4) ;WIS
((or
(string-match "Arcana" skill)
(string-match "History" skill)
(string-match "Investigation" skill)
(string-match "Nature" skill)
(string-match "Religion" skill))
3) ;INT
((string-match "Athletics" skill)
0) ;STR
((or
(string-match "Deception" skill)
(string-match "Intimidation" skill)
(string-match "Performance" skill)
(string-match "Persuasion" skill))
5))); CHA
(defun org-dnd-char-skill-mod (prof-check skill-name prof-mod attr-mods)
(org-dnd-char-mod-string
(+
(string-to-number
(nth
(org-dnd-char-skill-to-attribute-index skill-name)
attr-mods))
(if (string-match "\\*\\*\\*" prof-check)
(string-to-number prof-mod)
0; (floor (/ (string-to-number prof-mod) 2.0)) ; replace 0 with this when we get jack of all trades
)
(if (string-match "\\*\\+\\*" prof-check) ; for bard feature 'Expertise'
(string-to-number prof-mod)
0)
)))
(defun org-dnd-char-level-to-prof (level)
(org-dnd-char-mod-string
(nth (string-to-number level)
'(2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 6))) ;BARD SPECIFIC, FILL IN YOUR CLASS'S VALUES HERE
)
(defun org-dnd-char-level-to-cantrips-known (level)
(nth (- (string-to-number level) 1 )
'(2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4)) ;BARD SPECIFIC, FILL IN YOUR CLASS'S VALUES HERE
)
(defun org-dnd-char-level-to-spells-known (level)
(nth (- (string-to-number level) 1)
'(4 5 6 7 8 9 10 11 12 14 15 15 16 18 19 19 20 22 22 22)) ;BARD SPECIFIC, FILL IN YOUR CLASS'S VALUES HERE
)
(defun org-dnd-char-level-to-max-spell (level) ; replace with a function that calls org-dnd-char-spell-slots
(nth (- (string-to-number level) 1)
'(1 1 2 2 3 3 4 4 5 5 6 6 6 7 7 8 8 9 9 9)) ;BARD SPECIFIC, FILL IN YOUR CLASS'S VALUES HERE
)
(defun org-dnd-char-spell-slots (level)
(let ((slot-matrix
[ ;BARD SPECIFIC, FILL IN YOUR CLASS'S VALUES HERE
[2 0 0 0 0 0 0 0 0]
[3 0 0 0 0 0 0 0 0]
[4 2 0 0 0 0 0 0 0]
[4 3 0 0 0 0 0 0 0]
[4 3 2 0 0 0 0 0 0]
[4 3 3 0 0 0 0 0 0]
[4 3 3 1 0 0 0 0 0]
[4 3 3 2 0 0 0 0 0]
[4 3 3 3 1 0 0 0 0]
[4 3 3 3 2 0 0 0 0]
[4 3 3 3 2 1 0 0 0]
[4 3 3 3 2 1 0 0 0]
[4 3 3 3 2 1 1 0 0]
[4 3 3 3 2 1 1 0 0]
[4 3 3 3 2 1 1 1 0]
[4 3 3 3 2 1 1 1 0]
[4 3 3 3 3 1 1 1 1]
[4 3 3 3 3 2 1 1 1]
[4 3 3 3 3 2 2 1 1]
]))
(aref slot-matrix (- (string-to-number level) 1)))
)
Run the above source block when opening the file before exporting.
For now you must fill in the lists and matrices according to your classes table in the player’s handbook. An example for Bards is provided, there are comments in the sections indicating what bits are Bard specific.
If a particular feature, feat, or other rule modifies your stats you can modify these functions to reflect that. There is a comment with an example for the Bard ‘Jack of All Trades’ feature. This will require a bit of elisp, but allows for straightforward homebrew.