Class Creation Script

The homebrew forum

Moderator: Moderators

User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Class Creation Script

Post by Vebyast »

Earlier this morning, I realized that I would eventually get really annoyed with having to shuffle around abilities, having to make sure that unusual class names were spelled correctly, having to make sure that abilities' blurbs said they were granted at the right level, and so on. So I wrote a program to do all the boring stuff for me.

classbuilder.py isused to be a straightforward python script that parses a configuration file and a bunch of txt files and spits out a class. The configuration file specifies which txt files to use for which parts of the class, and the script takes the designated files and pastes them all together to make a nicely formatted class.

Currently, the script can be found on pasetebin here: http://pastebin.com/Z27AtNaX . If someone here has an FTP server that would be willing to host a zip file with some demos in it, that would be great. My ISP doesn't let me do FTP . :(

This was written by a computer guy for a computer guy, so it's probably not going to be as user-friendly as you might hope. It has some pretty nifty features (for example, you'll note that the entire spell list links to the d20 SRD), but it can't recover from even minor syntax errors, and it probably just plain won't make sense to someone that doesn't deal with computers on a regular basis. Also note that (as it turns out) it's really, really hard to use on windows, because I forgot that windows effectively has no command line. I will attempt to fix this at some point.

There's now a manual posted later in this thread.

As a demonstration, the following config file, when fed through the python script with all of the correct source files nearby, generates the following outputs:

Class Definition:

Code: Select all

Name: Test Class
Names: Test Classes
AAn: a
Rants: rant1, rant2
HD: d6
Skill Points: 4
BAB: Moderate
Fort: Poor
Ref: Good
Will: Poor
Columns: acbonus, dexbonus
!Spellcasting: spellcasting
Spells/Day: spellsperday
Spell List: spelllist
1: ability1, ability2
2: 
3: ability3
4: ability1
5: 
6: ability4
7: ability1
8: 
post: notes, note2

# custom parameters for DNDwiki formatting
Author: Vebyast
Creation Status: Testing, Testing, one two three point one four one five nine two six
Editing: This should not be on the wiki. KILL IT WITH FIRE.
Balance: Other
!Summary: summary
Primary Ability: Other
Ability Progression: Full
Minimum Level: 1
!Alignment Rant: align
Starting Gold: 4d4x10
Starting Age: Complex
The code for ability1. Note that this definition is used to generate both the bbcode and the dndwiki code.

Code: Select all

Ability 1|Ability 1 Upgrade
An &#91;i&#93;extremely&#91;/i&#93; motivated $cn gains $abname at $1levth level and again at $2levth. This ability links to <ifdef bbcode>&#91;url=www.google.com&#93;this text&#91;/url&#93;</ifdef><ifdef dndwiki>&#91;http&#58;//www.google.com this text&#93; instead</ifdef>.

BBCode output:
Test Class
"0001010110111010100010110101111010110010101110101000010101011111010100011101010001?"
"0"
":("

This is a test class. It is designed to test the classbuilder.py script.

Playing the Test Class: The Test Class is a test. It is used by humans to verify that their programs are correct. Test Classes aren't meant to be played by humans. Playing a Test Class is a recipe for disaster, mostly because they have no usable abilities.

Alignment, Races and People: Test Classes mostly turn up in computers, usually alongside or as part of more important or interesting projects. It is strongly encouraged that all projects have them.

On the subject of RANT RANT RANT: THIS IS A RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT RANT

It is after midnight: This is why you don't write programs at two in the morning.

Class Skills: the Test Class's class skills are: Alchemy (Int), Balance (Dex), Disable Device (Int), Hide (Dex), Climb (Str), Concentration (Con), Intimidate (Cha), Jump (Str), Craft (Int), Knowledge [dungeoneering] (Int), Sleight of Hand (Dex), Tumble (Dex)
Skill Points per Level: 4

Hit Dice: d6
BAB: Moderate
Fortitude save: Poor
Reflex save: Good
Will save: Poor

LevelDexterity BonusAC BonusAbilities
1210Ability 1, The Second Ability31----
222420000
3232His Highness Ability the Third421---
444Ability 1 Upgrade43201-
54fffff43211-
645ASDFHYAUIYCTAHG433210
7661Ability 1 Upgrade44321-
867443320

Spellcasting: Test Classes cast spells because they have to. No, seriously, that's why. Otherwise they wouldn't be very good Test Classes, now would they?

Abilities:

Ability 1: An extremely motivated Test Class gains Ability 1 at 1st level and again at 4th. This ability links to this text.

The Second Ability (Ex): This is the second ability. Duh. Its features:
  • Gain some stuff.
  • Gain some more stuff.
  • And finally some more.
His Highness Ability the Third: It's the Test Class's third ability. What did you expect?

ASDFHYAUIYCTAHG: 444444444444444444444444444444444. It is gained at level 6.


Test Class Spells:
0th level: Detect Poison (Spell Compendium), Disguise Self, Feather Fall, Ghost Sound, Jump, Obscuring Mist, Sleep, True Strike
1st level: Alter Self, Cat'S Grace, Darkness, Fox'S Cunning, Illusory Script, Invisibility, Pass Without Trace, Spider Climb (Spell Compendium), Undetectable Alignment
2nd level: Deep Slumber, Deeper Darkness (Spell Compendium), False Life, Magic Circle Against Good, Misdirection, Nondetection
3rd level: Clairaudience/Clairvoyance, Dimension Door, Freedom Of Movement, Glibness, Greater Invisibility (Frostburn), Locate Creature, Modify Memory, Poison
4th level: Fireball
5th level: Lesser Geas (New Spell)
6th level: Major Creation (New Spell)
7th level: Disintegrate
8th level: Limited Wish (Frostburn)
9th level: Binding




Notes: Hopefully this will all work as planned

ASDFASDFASDF: asdfaslkdfghaslirghncnakghclsdghmlxaehmglkhagm
DNDwiki output can be viewed on one of my user pages on the wiki.
Last edited by Vebyast on Sat Oct 23, 2010 8:03 pm, edited 20 times in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Lokathor
Duke
Posts: 2185
Joined: Sun Nov 01, 2009 2:10 am
Location: ID
Contact:

Post by Lokathor »

Now make a version 2 that spits out LaTeX source instead of BBcode source.
[*]The Ends Of The Matrix: Github and Rendered
[*]After Sundown: Github and Rendered
User avatar
Murtak
Duke
Posts: 1577
Joined: Fri Mar 07, 2008 7:54 pm

Post by Murtak »

1. Shouldn't you be able to derive plural and possessive from the base class name?
2. You should be able to get rid of most links to external files. Just check for existence.
3. In the same vein, blank lines in the ability list can be generated by the script.
4. File extensions can be left out.
5. You use a file format quite similar to YAML. This, or a similar format could be used. That lets you get rid of your parser (140 lines of code). You probably also get some nifty features and lots of bug testing for free.

That should turn your file into this:

Code: Select all

Name&#58; Test Class
HD&#58; d6
Skill Points&#58; 4
BAB&#58; medium
Fort&#58; low
Ref&#58; high
Will&#58; low
Abilities&#58;
    1&#58; ability1, ability2, spellcasting
    3&#58; ability3
    6&#58; ability4
Murtak
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Version 0.2 is up. http://pastebin.com/0cZiRD6H . I'm not going to advance out to a major version until a few people have used it to actually build classes.
Lokathor wrote:Now make a version 2 that spits out LaTeX source instead of BBcode source.
Go right ahead. Write up a formatted-output block based on the code in pastebin and I'd be happy to put it in. Shouldn't be that hard. All you need is a regex that goes through and replaces all the bbcode formatting tags with the correct LaTeX ones and a new table builder.
Murtak wrote:1. Shouldn't you be able to derive plural and possessive from the base class name?
2. You should be able to get rid of most links to external files. Just check for existence.
3. In the same vein, blank lines in the ability list can be generated by the script.
4. File extensions can be left out.
5. You use a file format quite similar to YAML. This, or a similar format could be used. That lets you get rid of your parser (140 lines of code). You probably also get some nifty features and lots of bug testing for free.
1) Possessive, yes, but plural, almost certainly not. English is really, really bad about nonstandard plurals (mouse->mice but house->houses, for example). Version 0.2 adds simple support for generating singular and plural possessives, but plurals must still be specified by hand, and specifying a possessive automatically overwrites the autogenerated one.

2,3,4) Yes. Those features have been added to version 0.2. quote, descript, playing, races, prereqs, skills, and profs are all added from default locations unless you specifically blank them out. Spellcasting, rants, special columns, and notes are assumed to be absent unless you specifically add them.

5) The vast majority of the parser is actually for parsing source files, which aren't in YAML. For example, spellsperday.txt looks like this:

Code: Select all

3 1 
4 2 0 0 0 0
4 2 1
4 3 2 0 1
4 3 2 1 1
4 3 3 2 1 0
4 4 3 2 1
4 4 3 3 2 0
And the spells list looks something like this:

Code: Select all

disguise self; detect poison; feather fall; ghost sound
alter self; cat's grace; darkness; fox's cunning; illusory script
deep slumber; deeper darkness; false life; magic circle against good
clairaudience/clairvoyance; dimension door; freedom of movement
fireball; geas, lesser; Major creation
disintegrate
limited wish
binding
hold monster, mass; meteor swarm
Switching to YAML would only replace about 10 lines of code (the line parser around L165). Since doing it with YAML would also require installing a YAML package, it wasn't worth the effort.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Version 0.3 is up. I added support for abilities that appear on multiple levels but should only be written once, like the Fighter's bonus feats.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Lokathor
Duke
Posts: 2185
Joined: Sun Nov 01, 2009 2:10 am
Location: ID
Contact:

Post by Lokathor »

1) Obviously this can be made to run a lot faster, at least in the section where you're putting together the spell lists.

2) Here's the stuff that should generate the proper LaTeX source, if I interpreted your stuff properly, and I also got all my \ correct and such. LaTeX uses "\command{args}" for most commands, for example bold is "\textbf{bold text here}". Also, forced newlines are \\. I think I put \\ and \\\\ in all the right places...

Code: Select all

######################################################################
######################################################################
######################################################################
#
# Formatting and Writing
#
######################################################################

output = file&#40;sys.argv&#91;2&#93;, "w"&#41;


# Class name/Title
output.write&#40;"\\section&#123;" + cn + "&#125;\n"&#41;
if quote != "" and quote != -1&#58;
    output.write&#40;quote + "\n"&#41;

# Write description block
if descript != "" and descript != -1&#58;
    output.write&#40;"\n" + descript + "\n\n"&#41;

# Write "playing the..." block
if playing != "" and playing != -1&#58;
    output.write&#40;"\\paragraph&#123;Playing the " + cn + "&#58;&#125; " + playing + "\n\n"&#41;

# Write "alignment, races..." block
if races != "" and races != -1&#58;
    output.write&#40;"\\paragraph&#123;Alignment, Races and People&#58;&#125; " + races + "\n\n"&#41;

# Write rants
for rant in rants&#58;
    output.write&#40;rant + "\n\n"&#41;

# Write prereqs block
if prereqs != "" and prereqs != -1&#58;
    output.write&#40;"\n\\paragraph&#123;Prerequisites&#58;&#125;\n" + prereqs + "\n\n\n"&#41;

# Write skills block
temp = ""
temp = temp + "\\paragraph&#123;Class Skills&#58;&#125; the " + cnpos + " class skills are&#58; "
for skill in skills&#58;
    temp = temp + skill&#91;0&#93; + " &#40;" + skill&#91;1&#93; + "&#41;, "
temp = temp.rstrip&#40;", "&#41;
temp = temp + "\\\\\n"
output.write&#40;temp&#41;
output.write&#40;"\\textbf&#123;Skill Points per Level&#58;&#125; " + skillpts + "\n\n"&#41;

# Write progression block
output.write&#40;"\\textbf&#123;Hit Dice&#58;&#125; " + hdsize + "\\\\\n"&#41;
output.write&#40;"\\textbf&#123;BAB&#58;&#125; " + bab + "\\\\\n"&#41;
output.write&#40;"\\textbf&#123;Fortitude save&#58;&#125; " + fort + "\\\\\n"&#41;
output.write&#40;"\\textbf&#123;Reflex save&#58;&#125; " + ref + "\\\\\n"&#41;
output.write&#40;"\\textbf&#123;Will save&#58;&#125; " + will + "\\\\\n\n\n"&#41;


# Write table
#open table
output.write&#40;"\\begin&#123;tabular&#125;&#123;c "
for key in keys&#58;
    output.write&#40;"c "&#41;
output.write&#40;"l&#125;\n"&#41;
#first row is bolded
row = "\\textbf&#123;Level&#125;&\\textbf&#123;"
keys = cols.keys&#40;&#41;
for key in keys&#58;
    row = row + key + "&#125;&\\textbf&#123;"
row = row + "Abilities&#125;\\\\\n"
output.write&#40;row&#41;
#other rows
for i in range&#40;1, len&#40;abilities&#41;&#41;&#58;
    row = str&#40;i&#41; + " & "
    for key in keys&#58;
        row = row + cols&#91;key&#93;&#91;i-1&#93; + " & "
    for ability in abilities&#91;i&#93;&#58;
        row = row + ability&#91;0&#93; + ", "
    row = row.strip&#40;"\n, "&#41;
    if len&#40;spells&#41; > 0&#58;
        for sl in range&#40;0,9&#41;&#58;
            row = row + " & " + spells&#91;i-1&#93;&#91;sl&#93;
    output.write&#40;row + "\\\\\n"&#41;
#close table
output.write&#40;"\\end&#123;tabular&#125;\n\n"&#41;


# Write abilities blocks
output.write&#40;"\\subsection&#123;Abilities&#125;\n\n"&#41;
for level in range&#40;1, len&#40;abilities&#41;&#41;&#58;
    for ability in abilities&#91;level&#93;&#58;
        output.write&#40;"\\paragraph&#123;" + ability&#91;0&#93; + "&#58;&#125; " + ability&#91;1&#93; + "\n\n"&#41;
output.write&#40;"\n"&#41;

# Write spellbook and links
if len&#40;spellbk&#41; > 0&#58;
    output.write&#40;"\\subsection&#123;" + cn + " Spells&#125;\n"&#41;
    for level in range&#40;1, len&#40;spellbk&#41;&#41;&#58;
        row = "\\paragraph&#123;" + ordinal&#40;level - 1&#41; + " level&#58;&#125; "\
\href&#123;my_url&#125;&#123;description&#125;
        for spell in spellbk&#91;level&#93;&#58;
            row = row + "\\emph&#123;\\href&#123;http&#58;//www.d20srd.org/srd/spells/"
            temp = spell
            temp = temp.replace&#40;" ", ""&#41;
            temp = temp.replace&#40;",", ""&#41;
            temp = temp.replace&#40;"'", ""&#41;
            temp = temp.replace&#40;"/", ""&#41;
            row = row + temp
            row = row + ".htm&#125;&#123;"
            row = row + spell.title&#40;&#41;
            row = row + "&#125;, "
        row = row.rstrip&#40;", "&#41;
        row = row + "\n"
        output.write&#40;row&#41;

# Write postscripts
if len&#40;posts&#41; > 0&#58;
    output.write&#40;"\n\n"&#41;
for post in posts&#58;
    output.write&#40;post + "\n\n"&#41;


Last edited by Lokathor on Thu Apr 15, 2010 11:26 pm, edited 1 time in total.
[*]The Ends Of The Matrix: Github and Rendered
[*]After Sundown: Github and Rendered
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Lokathor wrote:1) Obviously this can be made to run a lot faster, at least in the section where you're putting together the spell lists.
No kidding. If you think that's bad, take a look at the new abilities system. Not some of my best work.

Of course, if I really thought that this was going to be anything other than disk-bound, I wouldn't have used python.
Lokathor wrote:2) Here's the stuff that should generate the proper LaTeX source, if I interpreted your stuff properly, and I also got all my \ correct and such. LaTeX uses "\command{args}" for most commands, for example bold is "\textbf{bold text here}". Also, forced newlines are \\. I think I put \\ and \\\\ in all the right places...
I'll look through it, update the abilities section to the latest version if I need to, and add it. Thanks.
Last edited by Vebyast on Thu Apr 15, 2010 11:50 pm, edited 2 times in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Lokathor
Duke
Posts: 2185
Joined: Sun Nov 01, 2009 2:10 am
Location: ID
Contact:

Post by Lokathor »

Oh, right, and tables. The basic idea is:

\begin{tabular}{COLUMN_ALIGNMENTS}
cell1.1 & cell1.2 & cell1.3 \\
cell2.1 & cell2.2 & cell2.3 \\
cell3.1 & cell3.2 & cell3.3 \\
\end{tabular}

COLUMN_ALIGNMENTS is a series of letters, l, c, and r, one for each column, depending on how you want it aligned. There's more stuff you can do like having a column be paragraphs of a specific width and such, but you shouldn't really need that, and specific adjustments like that really depend on the contents of the table and so on. In this case, you mostly want your columns either l or c. If any of your rows are more cells wide than your opening specified for columns, the file won't compile. You can choose to specify more columns than you need, and nothing bad happens.

Thinking back, I'm not sure if the table will work properly when there's a spell book, because there isn't a check ahead of time for if spell columns will be included or not. I'm sure you can add that "c c c c c c c c c" at the proper time with an if statement.
[*]The Ends Of The Matrix: Github and Rendered
[*]After Sundown: Github and Rendered
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Lokathor wrote:Oh, right, and tables. The basic idea is:

\begin{tabular}{COLUMN_ALIGNMENTS}
cell1.1 & cell1.2 & cell1.3 \\
cell2.1 & cell2.2 & cell2.3 \\
cell3.1 & cell3.2 & cell3.3 \\
\end{tabular}

COLUMN_ALIGNMENTS is a series of letters, l, c, and r, one for each column, depending on how you want it aligned. There's more stuff you can do like having a column be paragraphs of a specific width and such, but you shouldn't really need that, and specific adjustments like that really depend on the contents of the table and so on. In this case, you mostly want your columns either l or c. If any of your rows are more cells wide than your opening specified for columns, the file won't compile. You can choose to specify more columns than you need, and nothing bad happens.
Makes sense. Been a while since I did any TeX. Thanks for the quick refresher.
Lokathor wrote:Thinking back, I'm not sure if the table will work properly when there's a spell book, because there isn't a check ahead of time for if spell columns will be included or not. I'm sure you can add that "c c c c c c c c c" at the proper time with an if statement.
Yep, fixed. It looks like there are some architectural issues (missing \documentclass, looks like a possible missing package), so it might be a few minutes before I have another version up.


Edit: New version is up. Includes a switch for creating either bbcode or LaTeX code. It's actually rather pretty.
Last edited by Vebyast on Fri Apr 16, 2010 1:36 am, edited 3 times in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
CatharzGodfoot
King
Posts: 5668
Joined: Fri Mar 07, 2008 7:54 pm
Location: North Carolina

Post by CatharzGodfoot »

This rocks my world.
The law in its majestic equality forbids the rich as well as the poor from stealing bread, begging and sleeping under bridges.
-Anatole France

Mount Flamethrower on rear
Drive in reverse
Win Game.

-Josh Kablack

User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

CatharzGodfoot wrote:This rocks my world.
Programming is awesome like that. Why do you think I got into computer science? ^_^
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Lokathor
Duke
Posts: 2185
Joined: Sun Nov 01, 2009 2:10 am
Location: ID
Contact:

Post by Lokathor »

Vebyast wrote:Yep, fixed. It looks like there are some architectural issues (missing \documentclass, looks like a possible missing package), so it might be a few minutes before I have another version up.
...oooohhhh... yeah.... Uh, I just had it output the stuff as a single section. You'll have to insert it into the frame for a book or article document.

Minimal outline:

Code: Select all

\documentclass&#91;onecolumn,oneside,12pt,a4paper&#93;&#123;book&#125;

\usepackage&#91;pdfborder=&#123;0,0,0&#125;,colorlinks=true,linkcolor=blue,pdfstartview=FitH&#93;&#123;hyperref&#125;
\usepackage&#91;all&#93;&#123;hypcap&#125;
%%%%%%%%%%%%%%%%%%%%%%%%%

\begin&#123;document&#125;

<LaTeX output from python goes here. If desired, you can include more than one class and they'll all be in a single large PDF when compiled &#40;but you probably already knew that&#41;.>

\end&#123;document&#125;
Last edited by Lokathor on Fri Apr 16, 2010 2:05 am, edited 1 time in total.
[*]The Ends Of The Matrix: Github and Rendered
[*]After Sundown: Github and Rendered
User avatar
Lokathor
Duke
Posts: 2185
Joined: Sun Nov 01, 2009 2:10 am
Location: ID
Contact:

Post by Lokathor »

The final step is of course to make a program that converts LaTeX into BBcode (limited by what BBcode can output, obviously).
[*]The Ends Of The Matrix: Github and Rendered
[*]After Sundown: Github and Rendered
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Lokathor wrote: ...oooohhhh... yeah.... Uh, I just had it output the stuff as a single section. You'll have to insert it into the frame for a book or article document.
It's been a long time since I actually did any TeX, and I didn't do that much of it (I'm more of a C and LISP guy), so I'm going to guess that yours is better. Since you're also the one that's going to be using this, I've gone ahead and put in your documentclass snippet there (and uploaded it).
Last edited by Vebyast on Fri Apr 16, 2010 2:35 am, edited 1 time in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Lokathor wrote:The final step is of course to make a program that converts LaTeX into BBcode (limited by what BBcode can output, obviously).
For right now, I have a command-line switch on the script that tells it whether to compile to LaTeX or to BBcode. That would be a really cool ultimate goal, though I'm pretty sure I don't know enough about TeX to actually pull it off.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Lokathor
Duke
Posts: 2185
Joined: Sun Nov 01, 2009 2:10 am
Location: ID
Contact:

Post by Lokathor »

Basics:

Code: Select all

\section&#123;name&#125;
\subsection&#123;name&#125;
\subsubsection&#123;name&#125;
\paragraph&#123;first words&#125;

\textbf&#123;text&#125;
\emph&#123;text&#125;

\begin&#123;itemize&#125;
\item thing
\item thing
\item thing
\end&#123;itemize&#125;

\href&#123;link target&#125;&#123;link text&#125;

table stuff as mentioned earlier
In my own experiences with LaTeX and BB code interactions, When you're working on the LaTeX side of stuff you usually end up wanting to use features that aren't available in BB code, so you have to either just not use those or dumb it down and reformat when you switch (and then amp it back up when you switch back).
[*]The Ends Of The Matrix: Github and Rendered
[*]After Sundown: Github and Rendered
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

New version is up. Fixes a few bugs, adds a few new features, and adds an output mode for DNDwiki. It's kind of rudimentary right now (no support for custom columns, and it has some problems with knowledge skills), but it's more or less usable. Link at http://pastebin.com/K4DD3tiF, sample output at http://pastebin.com/hUdzJR8B.

Note that the whole thing is still very much in alpha, and it looks like it's going to going through a heavy development cycle this week.
Last edited by Vebyast on Sat Jul 31, 2010 5:41 am, edited 2 times in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
Surgo
Duke
Posts: 1924
Joined: Fri Mar 07, 2008 7:54 pm

Post by Surgo »

Thanks much for the wiki output. I was thinking of doing something just like that, only in Emacs Lisp and not Python.
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Another update, this one mostly to fill feature requests. New features:
  • Ability definitions have a separate line for their type (Ex, Su, etc). In Wiki mode, this generates an internal link to the proper page.
  • Spell List and Skills get sorted alphabetically
  • When in wiki mode, automatically changes bbcode formatting for italics, bold, and lists to the corresponding wiki format ('', ''', ''''', and *).
  • Finished adding special parameters for wiki mode (author, creation date, balance point, short summary, etc)
  • Fixed the problems it has with skill links. Knowledge (Dungeoneering), for example, now links to Knowledge.
  • Included support for specifically not linking skills (!Alchemy, for example, doesn't turn into {{Property Link|Skill|SRD:Alchemy Skill|Alchemy}}, which would be an unfixable redlink).
  • Added support for making custom properties load from file (!summary: summary, for example, loads the summary text from summary.txt instead of setting it to "summary").
  • Added a few more tokens to the substitution function (for example, $a turns into "a" or "an", and $abname turns into the name of the current ability).
  • Custom columns work in Wiki mode.
For a sample of what the classbuilder is currently capable of, compare these two versions of the Speedened: on the wiki and on the den. They were generated by identical source files. All I had to do was tell the program which type of output I wanted.
Last edited by Vebyast on Sun Aug 01, 2010 12:59 am, edited 2 times in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Yet another update. More feature additions:
  • Full support for recurring abilities. For example:
    • Ability definitions can specify a unique name for each occurrence of the ability
    • The ability to insert the levels at which the ability is gained, either as numerals or ordinal strings. For example, in an ability which is gained first at 4th level and again at 8th, the string "This ability is gained first at $0levth level and again at $1levth" is replaced with something very similar to the beginning of this sentence.
  • Features for handling points where the program is incapable of automatically translating from bbcode to wiki format:
    • When told to load file $f and in output mode $mode, the program will attempt to load from a file named $f.$mode.txt before trying just plain $f.txt. This allows you to specify multiple versions of a file for different modes: abilityname.bbcode.txt will be loaded when in bbcode mode, for example, while abilityname.dndwiki.txt will be loaded when in dndwiki mode.
    • Added support for tags that allow similar features inside files. <ifdef bbcode>stuff</ifdef> will be deleted from the file when not in bbcode mode, and will be reduced to just "stuff" when in bbcode mode.
  • Finally, the coolest addition: support for multiple, custom spell list citation formats. When combined with the feature above for separate definition files for different modes, you can easily generate spell lists as complex as the one for the Summoner without compromising simplicity.
I've also been keeping the sample outputs and pastebin links in the OP up-to-date.

Also, for those of you wondering exactly what features the thing has by now and how it works, and especially for those of you that might want to be guinea pigs, here's a manual! It's still incomplete, but I should be able to finish it tomorrow afternoon.
classbuilder.py
classbuilder.py works by reading a definition file to determine where its data is stored, then putting it together in the properly boilerplated format. The definition file is a series of entries, one per line, of the form "field: value", where field and value are each strings. Some fields are literal: the value gets used directly during formatting. Others are indirect: the value specifies one or more files to load, and the values inside those files are used instead of the value itself. When loading files, the filename used is the value, followed by the name of the mode currently in use, followed by ".txt"; if no file exists, then the program uses just the filename and ".txt". If that doesn't exist, the program crashes. For example, when in bbcode mode, the line "Rants: rant1" would first try to load a rant from rant1.bbcode.txt; if that failed, the program would try rant1.txt, and then it would crash if that didn't exist.

When possible, examples are taken from the Test Class.

Class Descriptor File
Meaningful fields in all formats are as follows.
  • Name: The name of the class: "Test Class". This will be used exactly as typed, with leading and trailing whitespace trimmed.
  • Names: As above, but plural: "Test Classes"
  • Name's: As above, but possessive: "Test Class's"
  • Names': As above, but plural possessive: "Test Classes'"
  • AAn: Whether to use a or an: a Test Class versus an Enchanter. Should be "a" or "an", must be lowercase (unless you want all instances of it to be uppercase).
  • Quote: The filename of a quote that gets displayed right under the class name up top.
  • Description: The filename of a longer description for the class, giving an overall view of what the class does and some of its strengths and weaknesses.
  • How to Play: The filename of a blurb on how to build and play the class, possibly including a few useful hints and some information about how hard the class is to play and how the class behaves in general.
  • Races/People: The filename of a blurb about the kinds of people one would expect to take this class in-world. For example, elves are classically rangers, and stereotypical rogues grow up as orphans in dark alleys.
  • Rants: Each word on this line specifies a filename for a rant.
  • Prereqs: A filename for a file specifying the prerequisites for taking levels in the class, if any.
  • HD: How large the class's hit dice are. Include the d in this: "d6".
  • Skills: The filename of the file containing the list of the class's skills
  • Skill Points: How many skill points per level the class gets.
  • BAB: The class's base attack bonus progression. Can be one of "Good", "Moderate", or "Poor". Other values will work, but will cause major problems when in wiki mode.
  • Fort:
  • Ref: The class's save progressions. Can be either "Good" or "Poor". Same warning about over values as with the BAB.
  • Will:
  • Columns: The filenames of any special columns the class has on its table, such as the rogue's sneak attack damage or the monk's AC bonus.
  • Profs: The filename of the file describing the class's weapon and armor proficiencies.
  • Spellcasting: The first word on the line specifies the filename for the spellcasting blurb.
  • Spells/Day: Specifies the filename of the file containing the spells/day chart.
  • Spell List: Specifies the filename of the file containing the class's spell list.
  • Spell Sources: Specifies the filename of the file containing the formats for citations for spells on the spell list.
  • post: Specifies any postscripts. These are like rants, but they're placed at the very end of the class.
  • A number: Specifies which abilities the class gets at that level. These are each filenames specifying that ability's text. To steal a line from the Tome Summoner, "1: rapidsummoning, summonedcohort, armoredcasting, aura" would mean that the class gets the abilities held in rapidsummoning.txt, summonedcohort.txt, armoredcasting.txt, and aura.txt all at level 1. Note that the same ability can be put on the list at different levels under different names, which makes it easier to do things like the SRD Fighter's bonus feats or the Rogue's Evasion and Improved Evasion.
These fields are used for extra formatting for the wiki's extra metadata, such as authorname and creation date. All of these are technically optional, but DNDwiki will complain at you if you leave any of them off.
  • Author: The class's author.
  • Creation Status: How far along the class is. For example, "Complete".
  • Editing: What kind of edits you'd like. For example, "Please edit Constructively!", or "Spelling and Grammar only".
  • Balance: The class's balance point. See http://dungeons.wikia.com/wiki/Dungeons ... nce_Points
  • !Summary: A filename for a short summary of the class, to be displayed in the "all classes" list.
  • Primary Ability: The class's primary ability type. See http://dungeons.wikia.com/wiki/Property:Class_Ability
  • Ability Progression: The progression that the class has with its primary ability
  • Minimum Level: Not entirely sure. The minimum level you have to be to take this class, maybe? Ask the wiki.
  • !Alignment Rant: A filename for a rant about alignments and so on.
  • Starting Gold: Starting gold for the class.
  • Starting Age: Starting age for the class.
Some of these fields are optional, others are required. Of the required ones, some are automatically filled if they're not included (though if you include them but leave them blank they'll be specifically excluded).
  • Absolutely required:
    • Name
    • Names
    • HD
    • Skill Points
    • BAB
    • Fort
    • Ref
    • Will
  • Automatically filled if not included, and how they get filled:
    • Aan: defaults to "a"
    • Name's: The program will attempt to construct it by adding "'s". This succeeds once in a while.
    • Names': Same as above, by adding "'". Again, this succeeds once in a while.
    • Quote: loaded from a file (quote)
    • Description: loaded from a file (descript)
    • How to Play: loaded from a file (playing)
    • Races/People: loaded from a file (races)
    • Skills: loaded from a file (skills)
    • Profs: loaded from a file (profs)
  • Completely optional:
    • Rants
    • Prereqs
    • Columns
    • post
    • Spellcasting
    • Spells/Day
    • Spell List
    • Spell Sources

File formats
Naturally, this is all useless if you don't know how to organize the files in question. Therefore, the various filetypes, and how they're parsed.


Quote:
Nothing special here. The entire contents of the file is pasted straight into the output with no processing whatsoever.


Description:
As with the quote, the entire contents of the file is pasted into the output. However, it's processed for token replacement and to for bbcode-->wiki format conversion.


How to Play:
The same as description. A "Playing the $class:" header is added before the contents of the file, so you don't need to add that yourself. It's processed for token replacement and to for bbcode-->wiki format conversion.


Races/People:
Exactly the same as both of the above. It's processed for token replacement and to for bbcode-->wiki format conversion.


Rants:
The first line of the file is the name of the rant, and everything after that is the body. The body is processed for token replacement and to for bbcode-->wiki format conversion.


Prereqs:
Same as before.


Skills:
Here we start getting into things that are slightly more complex. Each line of the skills file corresponds to a single skill. The last word on the line is the three-letter contraction of the skill's key ability (Str, Dex, Con, Int, Wis, Cha), and everything else is the skill name. To prevent the classbuilder from attempting to automatically link to the wiki's page for a skill when in wiki mode, prepend a "!" to the line, so that it looks like this: "!This skill doesn't exist Int". The skills will be automatically alphabetized before being output. In wiki mode, any knowledge skills will be pointed to the Knowledge Skill page to prevent redlinks. For example, the first few lines of a skill list might look like this:

Code: Select all

!Autohypnosis Wis
Concentration Con
Craft Int
Decipher Script Int
Knowledge &#91;dungeoneering&#93; Int

Columns:
These are fun. The first line in the file is the column header. Every line after that is what goes on the level corresponding to that line. For example, a column devoted to the Rogue's sneak attack bonus would look like this:

Code: Select all

Sneak Attack
+1d6
+1d6
+2d6
+2d6
...
Profs:
This is another simple one. It behaves like the simple description blurbs above. The body is processed for token replacement and to for bbcode-->wiki format conversion.


Spellcasting:
As the description and everything.


Spells/Day:
This is simply a table of spells per day, in the standard format. Rows are levels, columns are separated by spaces and - means "no spells of this spell level available at this level". The spells/day file for a Cleric would start off looking like this:

Code: Select all

3 1+1 -   -   -   -   -   -   -   -
4 2+1 -   -   -   -   -   -   -   -
4 2+1 1+1 -   -   -   -   -   -   -
5 3+1 2+1 -   -   -   -   -   -   -
5 3+1 2+1 1+1 -   -   -   -   -   -
5 3+1 3+1 2+1 -   -   -   -   -   -
6 4+1 3+1 2+1 1+1 -   -   -   -   -
6 4+1 3+1 3+1 2+1 -   -   -   -   -

Spell List:
Another tricky one. This file defines the class's custom spell list if it has one. Each line of the file is a spell level, starting with zero (there currently isn't a way to tell the classbuilder to start with SL1). Each spell level is composed of spells, which are separated by semicolons. Each spell has two parts: a source number and the spell name. These two are separated by a !, and the source number goes in front of the name: "0!Fireball". Leaving off the source number and the ! ("Fireball" alone) causes the program to assume that the spell comes from source 0. WARNING: the wiki's links to internal spells are insanely fragile. They are case-sensitive and the capitalization is somewhat messed up. If your spellbook is entirely redlinks, you need to go through your spell list and fix it.


Spell Sources:
This is by far the most complex part of the classbuilder. In fact, it requires you to know a very, very small amount of python (at least until I get around to adding shortcuts or macros for you). Every line of the sources file is a single source, and can be linked to from the spell list using its line number.

For those of you that aren't programmers, here's a heavily simplified explanation. Each source is actually a tiny chunk of python code that gets run inside the classbuilder. This chunk of python is given access to the spell's name (held in a variable called "value"), and its task is to encase that name in stuff until it's a cited and formatted properly. You can do this by adding strings together. When you type stuff in between quotation marks ("like this" or 'like this' or '''like this''' or """like this"""), that becomes an element that you can add to other elements (like value) with the + sign.

For those of you that are programmers, a more detailed explanation. Each line is read into a buffer and then passed to python's eval() function. This causes it to execute exactly as if it were part of the source code, down to variables and working directory. What I do is run the proper line of code from the sources file, then stick the output directly into the spell list. The variable "value" is a string containing the name of the class, exactly as it's typed after the ! in the spell list file.

Now for some examples.

To start, we'll try something simple: simply italicizing the spell name in bbcode. Doing this is pretty simple:

Code: Select all

&#91;i&#93;Spell Name&#91;/i&#93;
There are three basic elements to this: the open tag, the spell name, and the close tag. All we have to do is add them all together, remembering that our spell name is "value".

Code: Select all

"&#91;i&#93;" + value + "&#91;/i&#93;"
Let's try something a bit more complicated. The standard citation format for the wiki is as follows: "[[SRD:Fireball|Fireball]]" or "[[SRD:Detect Magic|Detect Magic]]. To do this, note that this is constructed of a few basic parts: "[[SRD:", then the name of the spell, the a "|", then the name of the spell again, then "]]". So that's exactly what we put together:

Code: Select all

"&#91;&#91;SRD&#58;" + value + "|" + value + "&#93;&#93;"
For those of you that know a lot of python, we can get really crazy. I'm only including this because a bunch of you will want it and as a demonstration of the kinds of things you can do with this system. This snippet of code turns a spell name into a link from a bbcode page to the hypertext SRD's spell pages.

Code: Select all

"&#91;i&#93;&#91;url=http&#58;//www.d20srd.org/srd/spells/" + re.sub&#40;r"&#91; ,'/*&#93;", "", value&#41; + ".htm&#93;" + spell.title&#40;&#41; + "&#91;/url&#93;&#91;/i&#93;"
The basic order in which this went together:

Code: Select all

&#91;*&#93; re.sub&#40;r"&#91; ,'/*&#93;", "", value&#41; takes value and runs it through a regex substitution. This replaces all instances of space, comma, apostrophe, slash, and asterisk characters with "", in effect deleting them. This turns "Spell Name, Lesser" into "spellnamelesser", which is what the hypertext SRD likes.
&#91;*&#93; The url for the SRD goes like "http&#58;//www.d20srd.org/srd/spells/spellname.htm", so we take the first part of the url tag "&#91;url=", append the value that we got out of the regex &#40;spellname&#41;, and then cap it off with with a ".htm&#93;".
&#91;*&#93; spell.title&#40;&#41; makes the spell name Title Case. It then gets appended to our &#91;url=&#93; tag.
&#91;*&#93; We then close both of our tags with the "&#91;/url&#93;&#91;/i&#93;", and our formatting is complete.

Post:
Posts behave exactly like rants; they're just placed differently. First line is title, body is processed and posted.


Abilities:
Abilities are the other complex format. An ability definition has three parts: names, type, and body. The names take up the first line, the type takes up the second if it's present, and the body is everything else.

The first line of an ability file defines its names. The name provided in the class definition file is simply a pointer to the file; it has no effect on the actual output. This line, on the other hand, does. In its most basic form, this line is just the name of the ability: "Ability Name". In complexly recurring abilities (like the Rogue's Uncanny Dodge line), this line actually defines several names, which are used in order as the ability appears multiple times in the class's table. These names all go on the same line, and are separated by "|"s. For example, given "Uncanny Dodge|Improved Uncanny Dodge", the first time the ability is given in the class it will show up as "Uncanny Dodge", but on the second (and further) appearances it'll be "Improved Uncanny Dodge". The first name is the one used to head the ability block.

If the second line of the ability file is a single word (no spaces at all), it will be interpreted as an ability type (such as Ex, Su, or Ps), and the classbuilder will take this into account and provide the proper links.

If the second line has any spaces on it, then the body of the ability starts on the second line; otherwise, it starts on the third. The body is processed heavily for replaceable tokens and for bbcode-->wiki formatting, and is then pasted into the output in the appropriate location.


Summary:
The full text of the file is processed and put directly into the output.


Text Processing

Before being output, most of the larger chunks of text in the program get fed through some code that processes them to make building classes easier. It also provides tools for building source code that works for multiple output formats without modification.
  • $#lev, where # is a number, is replaced with the level at which the ability is gained for the #th time. The first time the ability is gained is at $1levth. The output of this replacement is a numeral digit; it isn't written out. Using the Test Class as an example, the ability "Ability 1" is gained at levels 1, 4, and 7. To generate that string ("the ability... 7."), you would write "the ability "Ability 1" is gained at levels $1lev, $2lev, and $3lev".
  • $lev is equivalent to $1lev
  • $levth and $#levth work like $lev and $#lev, but instead of a basic number, the output is the corresponding ordinal number. For example, on Ability 1, $2levth becomes "4th", and $levth becomes "1st".
  • $abname is replaced with the name of the ability that's being defined. For example, in an ability named "Ability 1", $abname will be replaced with "Ability 1".
  • $cn is replaced with the value of the Name field in the definition file (the name of the class).
  • $cnplu is replaced with the value of the Names field in the definition file (the plural version of the class's name)
  • $cnpos is replaced with the value of the Name's field (the possessive)
  • $cnplupos is replace with the value of the Names' field (the plural possessive)
  • $a and $A are replaced with the correspondingly capitalized value of the AAn field. This is used for the indefinite article corresponding to the class's name. For example, in a class named "Test Class", to create "A Test Class.", you would type "$A $cn.". If you decided to change the name of the class to, say, "Enchanter", then you would change the AAn value to "an", and your ability source would still work without modification: "$A $cn" would generate "An Enchanter".
  • <ifdef $mode>Text</ifdef> is deleted unless you're in output mode $mode, in which case the output is simply the text between the tags. To draw an example from the Test Class's ability1, this can be used to define differences between wiki and bbcode source without replacing the entire ability. This is useful with links, because the wiki's link format and bbcode's link formats are incompatible. When in bbcode mode, this resolves to [ url=www.google.com]a link in bbcode mode[/url], but in dndwiki mode this resolves to [http://www.google.com a link in wiki mode].

    Code: Select all

    <ifdef bbcode>&#91;url=www.google.com&#93;a link in bbcode mode&#91;/url&#93;</ifdef><ifdef dndwiki>&#91;http&#58;//www.google.com a link in wiki mode&#93;</ifdef>
  • When in dndwiki mode, all instances of and [/i ] are converted to '' (but not the other way around!). This enables you to write your class with bbcode italics formatting and be able to use it in wiki mode without being changed.
  • The same is done with the tags for bold: and [/b ] are converted to '''.
  • All instances of
    • and [/list ] are deleted.
    • All instances of
    • at the beginning of lines are replaced with *. If you're careful about how you format your lists, these two substitutions means that lists that are written in bbcode format are automatically converted to wiki formatting.
Last edited by Vebyast on Sat Oct 23, 2010 8:05 pm, edited 10 times in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
MfA
Knight-Baron
Posts: 578
Joined: Sat Jan 17, 2009 4:53 am

Post by MfA »

Pretty cool ... got started on doing the same thing with RelaxNG and XSLT once, but got lazy.
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

Neat. I considered doing it with a transformation language, but eventually decided on the programmatic approach for the extra power. It also gives me the ability to put every ability under source control individually, which is nice.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
MfA
Knight-Baron
Posts: 578
Joined: Sat Jan 17, 2009 4:53 am

Post by MfA »

Well Python vs XSLT is pretty much a wash I agree, it's just that I'm trying to learn Docbook so it seemed a good fit.

Schema languages however seem to still have a place even if you use Python for the transforms. It's an elegant way to express the structure input should take (to an extent it's a design document for your transformer too) and makes entering new instances easier with the help of schema aware editors.
Last edited by MfA on Mon Aug 02, 2010 8:44 am, edited 1 time in total.
User avatar
Vebyast
Knight-Baron
Posts: 801
Joined: Tue Mar 23, 2010 5:44 am

Post by Vebyast »

True. I could pass quite a bit of the parsing off to XML. Turn ability definitions into <ability><name>Ability 1</name><type>Ex</type></ability> or something, make the class definition file a big chunk of XML, etc. Do the parsing and transformations using python, but organize the data initially using XML. Might be worth exploring for the next major version.
Last edited by Vebyast on Mon Aug 02, 2010 10:03 pm, edited 1 time in total.
DSMatticus wrote:There are two things you can learn from the Gaming Den:
1) Good design practices.
2) How to be a zookeeper for hyper-intelligent shit-flinging apes.
Surgo
Duke
Posts: 1924
Joined: Fri Mar 07, 2008 7:54 pm

Post by Surgo »

I don't really see why that's necessary personally, markup languages are best if things are supposed to be unordered / nullable / have multiples or whatever. I'm of the opinion that if it works not to fix it.
Post Reply