RPTools.net
http://forums.rptools.net/

D&D 5e Statblock Importer.
http://forums.rptools.net/viewtopic.php?f=20&t=25442
Page 1 of 1

Author:  Full Bleed [ Fri Oct 10, 2014 10:32 am ]
Post subject:  D&D 5e Statblock Importer.

Has anyone created a 5e statblock importer yet?

If you have, please share your regex code here. I'd like to compile a generic importer that people will be able to easily adapt to their frameworks.

Author:  StarMan [ Tue Nov 18, 2014 11:14 pm ]
Post subject:  Re: D&D 5e Statblock Importer.

Regex syntax makes my head spin so I recently removed all but the most basic of usage from my own framework (link in sig below). I also don't like those bizarre dialogs strFind pops up if it doesn't find a matched group. Although my framework is written for 4e, you can use my Get Field macro for any stream of text:

Code:
<!--
[
varsFromStrProp("FieldName=; NextFieldList=; FM=0; StopAtColon=1; WordNum=0; StartAtEnd=0; macro.return=;"+macro.args)]
[IF(
indexOf(TextFieldName)+1), CODE:
{
    [
Text=substring(TextindexOf(TextFieldName)+length(FieldName), length(Text))]
    [
ColonPos=indexOf(Text":")]
    [IF(
StopAtColon && ColonPos+1), CODE:
    {
        [
Temp=substring(Text0ColonPos)]
        [IF(
indexOf(Temp" ")+1): Text=substring(Temp0lastIndexOf(Temp" "))]
    }]

    [
EndPos=length(Text)]
    [
SaveText=Text]
    [FOREACH(
NextFieldNextFieldList), CODE:
    {
        [
FirstChar=substring(NextField+" "01)]
        [IF(
startsWith(TextNextField)): Text=replace(TextFirstChar"0"1)]
        [
Pos=indexOf(TextNextField)]
        [IF(
Pos>0): EndPos=min(PosEndPos)]
    }]
    [
Text=SaveText]

    [
macro.return=trim(substring(Text0EndPos))]
}]
[
Words=listCount(macro.return, " ")]
[IF(
WordNum): WordNum=min(WordNumWords)]
[IF(
Words && WordNum): macro.return=if(StartAtEndlistGet(macro.return, Words-WordNum" "), listGet(macro.return, WordNum-1" "))]
[IF(
FM), CODE:
{
    [
Charging=FM]
    [
h,MACRO("Filter Metacharacters@this"): macro.return]
    [
Charging=0]
    [IF(
FM>&& WordNum): macro.return=replace(macro.return, " """)]
    [IF(
macro.return=="" && FM==2): macro.return=0]
}]
--> 


Of course, to use the FM option you will also need my Filter Metacharacters macro which gets rid of any weird non-printable (and just plain unwanted) text. If you are using 4e I suggest, downloading the MPB and looking at the Import macros near the bottom of the Lib token. These show how Get Field is called. However, to address your specific question let's look at the Adult Red Dragon from pg 8 of the 5e DMG.

To get the creature's Skills, you would assign the text to the variable "StatBlock" and use:
Code:
[MACRO("Get Field@this"): "FieldName=Skills; NextFieldList=Damage; Text="+StatBlock]
Found skills {macro.return} 

This returns "Perception +13, Stealth +6" as expected. This assumes the following field will always be "Damage Immunities" … which it might not. To guard against this possibility, list another field that comes after Skills for sure:
Code:
[MACRO("Get Field@this"): "FieldName=Skills; NextFieldList=Damage,Senses; Text="+StatBlock]
Found skills {macro.return} 


This way, even if "Damage Immunities" isn't present, Get Field knows to stop gathering text at "Senses" which all monsters have. Hey, I never said it was elegant … but it does work well enough for those of us with tiny brains incapable of wrapping around regex concepts. Yes, I know enough about them to use the matches function in a rudimentary way but Get Field does everything I need it to without those silly dialogs mentioned earlier.

Again, my Import macros showcase all the different parameters that can be used so it's best to examine those. Hopefully you will be able to see how the StopAtColon, WordNum and StartAtEnd parameters are used. To actually see my importer in action without downloading the MPB, go to my videos page.

Author:  wolph42 [ Wed Nov 19, 2014 2:04 am ]
Post subject:  Re: D&D 5e Statblock Importer.

To prevent that strange regex related pop-up you can use Wiki: getFindCount(), this will return the number of matches, which can also be 0 in which case you should not try to retrieve getGroup (which renders those popups).

Author:  StarMan [ Wed Nov 19, 2014 10:36 am ]
Post subject:  Re: D&D 5e Statblock Importer.

Thanks for the tip, Wolph! However, the regex stuff still confuses me even after going through the basic tutorials. I know "[A-Z]" means "match any capital letter" and you can therefore match capitalized words with "[A-Z][a-z].*" but anything more complicated than that is beyond me.

Full Bleed, I suggest you check out viewtopic.php?f=8&t=13237 if you really want to go this route.

Author:  Sereptus [ Fri Jan 09, 2015 4:31 pm ]
Post subject:  Re: D&D 5e Statblock Importer.

Has anyone gotten any further with a 5E statblock importer? I've been learning a lot just trying to figure out the code of other "Statblock Importers" and its helped immensely in other areas of my coding but I find this is just way over my head. My fingers are starting to bleed coding all these Monsters by hand! :shock:

Any help would be appreciated!

Author:  JamzTheMan [ Sat Jan 10, 2015 4:24 pm ]
Post subject:  Re: D&D 5e Statblock Importer.

It "can" get more complicated than it needs but not always.

I prefer pasting statblock into GM Text for two reason.
1. I preserve the statblock for reference or to rerun later
2. It preserves line breaks (vs pasting into an input box that does not) and makes some regexp easier (ie you can use ^ beginning of line and some regexp become easier)

I also use http://regexr.com/ to build my regexp. I then used http://myregexp.com/signedJar.html to transform it into "java friendly" eg double escape the \\'s

If you can master the "Look ahead" and "Look behind", you can really get specific easier grabbing stuff. The hardest are damage blocks because SO much can be define there (like plus poison or touch or a lot of stuff other than xdx+x)

If you get stuck on a particular block, post the statblock and ask, if I (or others) have time we may be able to post at least the regexp that will work.

Take it in little chunks. Get one skill or stats, or hp, go for what you would need more often and leave complicated ie damage blocks) for last. If you can get AC/HP/Init, that will usually help at least to start.

Author:  wolph42 [ Tue Jan 13, 2015 4:30 am ]
Post subject:  Re: D&D 5e Statblock Importer.

Quote:
If you can master the "Look ahead" and "Look behind", you can really get specific easier grabbing stuff. The hardest are damage blocks because SO much can be define there (like plus poison or touch or a lot of stuff other than xdx+x)


I used to use those a LOT and then I learned to use the Wiki: getGroup ()function properly and I've never used them since.... Its one of the reasons I heavily update that wiki article.

ive attached my smaller (and most recent created) regex parser for space ships in rogue trader with an example text, have a look.

its missing a couple of macros like listCleaner, findSimilar and , correctLearned. The first cleans up lists, the second, findSimilar, is one of the more cooler functions as e.g. if the parser can't find "Besterd Sword" in the Database, it will go through all the weapons in the database and shows the ones that look similar (like 'Bastard Sword'), this you can pick from a resulting list and it will substitute this. Optionally can 'learn' this substitution which than will be used in 'correctLearned', which is executed at the start of the parser. I can provide these functions as well if you want to have a look.

Attachments:
ship regex parser.zip [53.7 KiB]
Downloaded 56 times

Author:  Sereptus [ Thu Jan 15, 2015 9:50 am ]
Post subject:  Re: D&D 5e Statblock Importer.

Thanks a lot Wolph42 and JamztheMan, I'll let you know what I come up with by way of (hopefully) posting a working Statblock parser for D&D 5E.

P.S. Wolph42, you've inspired me to look into that Rogue Trader game as well, looks very interesting!

Author:  wolph42 [ Thu Jan 15, 2015 6:04 pm ]
Post subject:  Re: D&D 5e Statblock Importer.

Sereptus wrote:
Thanks a lot Wolph42 and JamztheMan, I'll let you know what I come up with by way of (hopefully) posting a working Statblock parser for D&D 5E.

P.S. Wolph42, you've inspired me to look into that Rogue Trader game as well, looks very interesting!


You're welcome and thanks

Author:  Stormraven [ Mon Mar 16, 2015 5:29 pm ]
Post subject:  Re: D&D 5e Statblock Importer.

OH! :shock: I just came across this post, I'm curious as to how this project is progressing :D

Author:  StarMan [ Wed Jun 10, 2015 1:55 pm ]
Post subject:  Re: D&D 5e Statblock Importer.

FYI I just made this post in the thread for my 4e framework:

StarMan wrote:
The big one for this release is the inclusion of 5e monster conversion. I did this just because I had some extra time over the past few days and decided to give it a shot. Just copy a monster from http://media.wizards.com/2014/downloads ... sv.0.3.pdf up to but not including the description block(s) at the end and paste into F4 as always. Yes, some concepts aren't compatible between the two versions but you can adjust as needed. This importer branch isn't perfect and just meant for experimental purposes. This has been done for those wishing to use v5 monsters for which there is no v4 equivalent. I have no plans for further 5e development as my group only plays 4e.

Enjoy ...


The imperfection I reference encompasses omissions such as the ignorance of the "Skills" line. I also don't know how resistances and vulnerabilities work in 5e so I ignore that stuff too but damage type immunities should work. Those interested are welcome to go to http://209.90.88.139/~macropolicebox , sign up for an account (bottom left) and download the code. Just follow the "How to import monsters" video (just above the "Running Encounters" section) on this page and you should be fine. Yes, I demo a 4e skeleton there but the process is the same.

Keep in mind this isn't just a 5e monster importer. The creature will be converted to 4e on-the-fly. Yes, I am well aware that isn't what you all want. You want a 5e monster to use in a 5e framework. As I explained earlier, my group has no interest in 5e as we don't see the appeal. I wrote this just in case we see an interesting 5e monster we want to use in our games. By all means adjust the Parse 5e Monster Stats macro you see on my library token for your own purposes. You can even copy/paste the player token into your 5e campaign file as long as your property names are compatible with mine. If not, just use getProperty() to extract mine into yours.

Author:  Sereptus [ Sun Nov 20, 2016 8:47 am ]
Post subject:  Re: D&D 5e Statblock Importer.

Still plugging away at this 5E Stablock Importer for PDF's

Code:
[H: ids = getSelected()]
[H: abort(if(ids == "", 0, 1))]

[H: status=input("junk|Statblock info from Creature Name through Treasure line of Ecology (if it exists). Don't include flavor text/background/descriptions.||LABEL|SPAN=TRUE","junk|If Ecology section comes before Special Abilities, be sure to include Special Abilities section as well. Still no flavor text.||LABEL|SPAN=TRUE","statblock|Insert 5E statblock here|Enter statblock|TEXT|WIDTH=40")]
[H: abort(if(status < 1, 0, 1))]

[H: CRLF = decode("%0D%0A")]

[H: setPropertyType("5E-NPC")]

[H: propnames = getPropertyNames()]
[H, foreach(propname,propnames),CODE: {
   [resetProperty(propname)]
}]

[H: '<!-- Lets clean up those pesky non-ascii characters! -->']
[H: statblock = replace(statblock, "\\xD7", "x")]
[H: statblock = replace(statblock, "\\u2013", "-")]
[H: statblock = replace(statblock, "\\u2014", "-")]
[H: statblock = replace(statblock, "%E2%80%93", "-")]

[H: statblock = replace(statblock, "  ", " ")]

[H: '<!-- change brackets and braces to parens to avoid stat sheet variable input error -->']
[H: statblock = replace(statblock, "[\\[\\{]","(")]
[H: statblock = replace(statblock, "[\\]\\}]",")")]

[H: '<!-- Start formatting imported statblock and set it to GM notes -->']



[H: '<!-- Search for lines and sections of statblock individually -->']

[H: NameSearch = strfind(statblock, "(.*?)(G-size|Tiny|Small|Medium|Large|Huge|Gargantuan)")]
[H: setGMName(getGroup(NameSearch, 1, 1))]

[H: setName("Creature")]
[H: setTokenShape("Top down")]


[H: '<!-- STATS BEGIN -->']
[H: id = strfind(statblock, "(?i)(?<!base )ft.*?STR.?([0-9]*).+?DEX.?([0-9]*).+?CON.?([0-9]*).+?INT.?([0-9]*).+?WIS.?([0-9]*).+?CHA.?([0-9]*)")]
[H, IF(0< getFindCount(id)), CODE: {
   [Strength=getGroup(id, 1, 1)]
   [IF(Strength==""):Strength=10]
   [Dexterity=getGroup(id, 1, 2)]
   [IF(Dexterity==""):Dexterity=10]
   [Constitution=getGroup(id, 1, 3)]
   [IF(Constitution==""):Constitution=10]
   [Intelligence=getGroup(id, 1, 4)]
   [IF(Intelligence==""):Intelligence=10]
   [Wisdom=getGroup(id, 1, 5)]
   [IF(Wisdom==""):Wisdom=10]
   [Charisma=getGroup(id, 1, 6)]
   [IF(Charisma==""):Charisma=10]
   [output="Str: "+Strength+", Dex: "+Dexterity+", Con: "+Constitution+", Int: "+Intelligence+", Wis: "+Wisdom+", Cha: "+Charisma+"<br>"]
}]


[H: '<!-- STATS END -->']

[H: '<!-- AC BEGIN -->']
[H: id = strfind(statblock, "(?i)(?<!Hit )Armor Class.([0-9]*)")]
[H, IF(0< getFindCount(id)), CODE: {
   [AC=getGroup(id, 1, 1)]
   [IF(AC==0):AC=10+Dx]

   [output="AC: "+AC+"<br>"]
}]
[H: '<!-- AC END -->']




[H: '<!-- Speed BEGIN -->']

[H: id = strfind(statblock, "(?i)(?<!STR )(Speed.|burrow.|climb.|fly.|swim.)([0-9]+)")]
[H, IF(0< getFindCount(id)), CODE: {
   [Speed1=getGroup(id, 1, 2)]
   [IF(Speed1==""): setProperty("Speed1","")]
[Speed1="Move: "+Speed1+""]

}]
[H, IF(1< getFindCount(id)), CODE: {
   [Speed2=getGroup(id, 2, 1)]
   [IF(Speed2==""): setProperty("Speed2","")]
   [Speed22=getGroup(id, 2, 2)]
   [IF(Speed22==""): setProperty("Speed22","")]
[Speed2=""+upper(Speed2,1) + ": "+Speed22+""]
}]
[H, IF(2< getFindCount(id)), CODE: {
   [Speed3=getGroup(id, 3, 1)]
   [IF(Speed3==""): setProperty("Speed3","")]
   [Speed33=getGroup(id, 3, 2)]
   [IF(Speed33==""): setProperty("Speed33","")]
   [Speed3=""+upper(Speed3,1) + ": "+Speed33+""]

}]
[H, IF(3< getFindCount(id)), CODE: {
   [Speed4=getGroup(id, 4, 1)]
   [IF(Speed4==""): setProperty("Speed4","")]
   [IF(Speed4!=""): Speed44=getGroup(id, 4, 2)]
   [IF(Speed44==""): setProperty("Speed44","")]
   [Speed4=""+upper(Speed4,1) + ": "+Speed44+""]

}]

[h:Speed=""+Speed1+" "+Speed2+" "+Speed3+" "+Speed4+"" ]





[H: '<!-- Hit Points BEGIN -->']
[H: id = strfind(statblock, "Hit Points (\\d+) [(]([^)]+)[)]")]
[H, IF(0< getFindCount(id)), CODE: {
   [HP=getGroup(id, 1, 1)]
   [MaxHP=getGroup(id, 1, 1)]
   [HitDice=getGroup(id,1,2)]
}]

[H: '<!-- Hit Points END -->']




[H: id = strfind(statblock, "(G-size|Tiny|Small|Medium|Large|Huge|Gargantuan|\w-]+)")]
[H, IF(0< getFindCount(id)),CODE: {

   [Size_txt=getGroup(id, 1, 1)]

[H, IF(Size_txt=="G-size"): Size_txt="Large"]
   [setSize(Size_txt)]

}]


[H: id = strfind(statblock,  "(aberration.*?|beast.*?|celestial.*?|construct.*?|dragon.*?|elemental.*?|fey.*?|fiend.*?|giant.*?|humanoid.*?|monstrosity.*?|ooze.*?|plant.*?|undead.*?|-)\,")]


[H, IF(0< getFindCount(id)),CODE: {
   [Type=getGroup(id, 1, 1)]
   [Type=upper(Type, 1)]

 }]






[H: id = strfind(statblock,  "\,.(lawful.|neutral.|chaotic.|)(good|neutral|unaligned|evil|\w-]+)")]

[H, IF(0< getFindCount(id)),CODE: {
   [Alignment1=getGroup(id, 1, 1)]
   [Alignment2=getGroup(id, 1, 2)]
   [Alignment1=upper(Alignment1,1)]
   [Alignment2=upper(Alignment2,1)]

   [IF(Alignment1==""): Alignment1=""]

[Alignment=" "+Alignment1+" " + Alignment2+""]
 }]




[H: id = strfind(statblock, "passive Perception.([0-9]+)")]
[H, IF(0< getFindCount(id)), CODE: {
   [pass=getGroup(id, 1, 1)]
   
   [IF(pass>=0): setProperty("Passive Perception",pass)]
}]




[H: '<!-- Challenge BEGIN -->']
[H: id = strfind(statblock, "Challenge.([0-9]+)")]
[H, IF(0< getFindCount(id)), CODE: {
   [CR=getGroup(id, 1, 1)]
   [CR=trim(CR)]
   [IF(CR<=4): setProperty("SkillDie",2)]
   [IF(CR<=8&&CR>=5): setProperty("SkillDie",3)]
   [IF(CR<=12&&CR>=9): setProperty("SkillDie",4)]
   [IF(CR<=15&&CR>=13): setProperty("SkillDie",5)]
   [IF(CR<=20&&CR>=16): setProperty("SkillDie",6)]
   [IF(CR<=24&&CR>=21): setProperty("SkillDie",7)]
   [IF(CR<=28&&CR>=25): setProperty("SkillDie",8)]
   [IF(CR<=32&&CR>=29): setProperty("SkillDie",9)]

}]

[H: id = strfind(statblock, "Languages(.*, )(.*)\Challenge")]
[H, IF(0< getFindCount(id)), CODE: {
   [Lang1=getGroup(id, 1, 1)]
   [Lang1 =trim(Lang1)]
   [Lang1 =upper(Lang1,1)]
   [IF(Lang1==""): setProperty("Lang1","")]


   [Lang2=getGroup(id, 1, 2)]
   [Lang2 =trim(Lang2)]
   [Lang2 =upper(Lang2,1)]
   [IF(Lang2==""): setProperty("Lang2","")]



[Languages=""+Lang1+" "+Lang2+""]
}]




[H: id = strfind(statblock, "(?i)\Damage Resistances?(.*?)(Damage Immunities|Condition Immunities|Senses)")]
[H, IF(0< getFindCount(id)), CODE: {
   [Resist1=getGroup(id, 1, 1)]
   [Resist1=trim(Resist1)]
   [Resist1=upper(Resist1,1)]
   [IF(Resist1==""): setProperty("Resistance","")]
   [IF(Resist1!=""): setProperty("Resistance",Resist1)]
}]
[H: id = strfind(statblock, "(?i)\Damage Immunities?(.*?)(Condition Immunities|Senses)")]
[H, IF(0< getFindCount(id)), CODE: {
   [Resist2=getGroup(id, 1, 1)]
   [Resist2=trim(Resist2)]
   [Resist2=upper(Resist2,1)]
   [IF(Resist2==""): setProperty("Immunities","")]
   [IF(Resist2!=""): setProperty("Immunities",Resist2)]
}]

[H: id = strfind(statblock, "(?i)\Condition Immunities?(.*?)(Senses)")]
[H, IF(0< getFindCount(id)), CODE: {
   [Resist3=getGroup(id, 1, 1)]
   [Resist3=trim(Resist3)]
   [Resist3=upper(Resist3,1)]
   [IF(Resist3==""): setProperty("Condition Immunities","")]
   [IF(Resist3!=""): setProperty("Condition Immunities",Resist3)]
}]


[h, IF(0==0), CODE:{
[H: id = strfind(statblock, "(blindsight.|darkvision.|tremorsense.|truesight.)([0-9]+)(.ft.).*?\,")]
[H, IF(0< getFindCount(id)), CODE: {
   [Sense1=getGroup(id, 1, 1)]
   [Sense2=getGroup(id, 1, 2)]
   [Sense3=getGroup(id, 1, 3)]
   [Sense1=trim(Sense1)]
   [Sense2=trim(Sense2)]
   [Sense3=trim(Sense3)]

[Sense1=upper(Sense1,1)]
   [IF(Sense1==""): Sense1=""]
   [IF(Sense2==""): Sense2=""]


[Senses=""+Sense1+" " + Sense2+" " + Sense3+" "]
[Sight1=(Sense1 +Sense2)]
[setSightType(Sight1)]
}]

[H, IF(2== getFindCount(id)), CODE: {
   [Sense4=getGroup(id, 2, 1)]
   [Sense5=getGroup(id, 2, 2)]
   [Sense6=getGroup(id, 2, 3)]
   [Sense4=trim(Sense4)]
   [Sense5=trim(Sense5)]
   [Sense6=trim(Sense6)]


[Sense4=upper(Sense4,1)]

   [IF(Sense4==""): Sense4=""]
   [IF(Sense5==""): Sense5=""]
[Senses=" "+Sense1+" " + Sense2+" " + Sense3+" " +Sense4+" " +Sense5+" " +Sense6+""]
[Sight2=(Sense4 +Sense5)]
 
[IF(Sense2>Sense5): setSightType(Sight1) ; setSightType(Sight2)]
}]

   [IF(Senses==""): setProperty("Senses","")]


   [IF(Senses==""): setHasSight(0)]
   [IF(Senses!=""): setHasSight(1)]
}]

<BR>
[H: '<!-- XP BEGIN -->']
[H: id = strfind(statblock, "Challenge.[0-9]+.(.*XP.)")]
[H, IF(0< getFindCount(id)), CODE: {
   [XP=getGroup(id, 1, 1)]
   
   [setLabel(XP)]
}]




EDIT: Finished for now, feel free to comment.

Author:  Hazeyindahead [ Sat Apr 08, 2017 11:14 am ]
Post subject:  Re: D&D 5e Statblock Importer.

Hi, is there a post on how best to utilize the importer?

I wouldnt mind a tool to convert my statblocks into tokens, though learning how to use it is the obstacle it seems.

Author:  Sereptus [ Thu May 18, 2017 9:27 am ]
Post subject:  Re: D&D 5e Statblock Importer.

Hazeyindahead wrote:
Hi, is there a post on how best to utilize the importer?

I wouldnt mind a tool to convert my statblocks into tokens, though learning how to use it is the obstacle it seems.


Add a new macro in the Campaign Area, copy and paste the contents of the code inside the new macro. Click on the macro and copy and paste the statblock u want and let me know how it goes.

Page 1 of 1 All times are UTC - 6 hours [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/