D&D 5e Statblock Importer.

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

User avatar
Full Bleed
Demigod
Posts: 4736
Joined: Sun Feb 25, 2007 11:53 am
Location: FL

D&D 5e Statblock Importer.

Post by Full Bleed »

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.
Maptool is the Millennium Falcon of VTT's -- "She may not look like much, but she's got it where it counts."

User avatar
StarMan
Dragon
Posts: 939
Joined: Mon Jul 18, 2011 1:10 pm
Location: Toronto

Re: D&D 5e Statblock Importer.

Post by StarMan »

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: Select all

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

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

    [macro.return=trim(substring(Text, 0, EndPos))]
}]
[Words=listCount(macro.return, " ")]
[IF(WordNum): WordNum=min(WordNum, Words)]
[IF(Words && WordNum): macro.return=if(StartAtEnd, listGet(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>1 && 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: Select all

[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: Select all

[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.
StarMan - The MacroPolicebox D&D 4E Framework: Import ANYTHING!

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: D&D 5e Statblock Importer.

Post by wolph42 »

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).

User avatar
StarMan
Dragon
Posts: 939
Joined: Mon Jul 18, 2011 1:10 pm
Location: Toronto

Re: D&D 5e Statblock Importer.

Post by StarMan »

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 http://forums.rptools.net/viewtopic.php?f=8&t=13237 if you really want to go this route.
StarMan - The MacroPolicebox D&D 4E Framework: Import ANYTHING!

User avatar
Sereptus
Giant
Posts: 123
Joined: Tue Jun 07, 2011 12:08 pm
Location: Canada

Re: D&D 5e Statblock Importer.

Post by Sereptus »

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!
OOOHH RegEx....YOU BITTER-SWEET BEAST!!!

User avatar
JamzTheMan
Great Wyrm
Posts: 1872
Joined: Mon May 10, 2010 12:59 pm
Location: Chicagoland
Contact:

Re: D&D 5e Statblock Importer.

Post by JamzTheMan »

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.
-Jamz
____________________
Custom MapTool 1.4.x.x Fork: maptool.nerps.net
Custom TokenTool 2.0 Fork: tokentool.nerps.net
More information here: MapTool Nerps! Fork

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: D&D 5e Statblock Importer.

Post by wolph42 »

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 153 times

User avatar
Sereptus
Giant
Posts: 123
Joined: Tue Jun 07, 2011 12:08 pm
Location: Canada

Re: D&D 5e Statblock Importer.

Post by Sereptus »

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!
OOOHH RegEx....YOU BITTER-SWEET BEAST!!!

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: D&D 5e Statblock Importer.

Post by wolph42 »

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

Stormraven
Kobold
Posts: 3
Joined: Fri Sep 26, 2014 1:23 am

Re: D&D 5e Statblock Importer.

Post by Stormraven »

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

User avatar
StarMan
Dragon
Posts: 939
Joined: Mon Jul 18, 2011 1:10 pm
Location: Toronto

Re: D&D 5e Statblock Importer.

Post by StarMan »

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.
Last edited by StarMan on Sat Apr 08, 2017 12:25 pm, edited 1 time in total.
StarMan - The MacroPolicebox D&D 4E Framework: Import ANYTHING!

User avatar
Sereptus
Giant
Posts: 123
Joined: Tue Jun 07, 2011 12:08 pm
Location: Canada

Re: D&D 5e Statblock Importer.

Post by Sereptus »

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

Code: Select all

[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: GP=getProperty("dnd.weapons")]
[H: propnames = getPropertyNames()]
[H, foreach(propname,propnames),CODE: {
   [resetProperty(propname)]
}]

[H: setProperty("dnd.weapons", GP)]


[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, "(.*?)(CoS|OotA|SKT|PotA|ToD|VGM|MM|Tiny|Small|Medium|Large|Huge|Gargantuan)")]
[H: THEName= getGroup(NameSearch, 1, 1)]
[H: NameSearch=trim(THEName)]

[H: setName(trim(THEName))]


[H: setTokenShape("Top down")]



<!-- get stats -->
[H: regex1 = "((\\d+) *[(][\\d +-]+[)]\\t*){6}"]
[H: regex2 = "(\\d+) *[(][\\d +-]+[)]\\t*"]
[H: id = strfind(statblock,regex1)]

[H, if(getFindCount(id)), code: {
   [H: stats = json.append("[]","Strength","Dexterity","Constitution","Intelligence","Wisdom","Charisma")]
   [H: match = getGroup(id,1,0)]
   [H: id = strfind(match,regex2)]
   [H, foreach(stat,stats), code: {
      [H: set(stat,getGroup(id,roll.count+1,1))]
   }]
};{
   [H: assert(0,"Stats in statblock not found.",0)]
}]


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

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

   [AC=""+AC+" "+ACC+""]
}]


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

[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","")]
   [IF(Speed1!=""): Speed11=getGroup(id, 1, 2)]
   [IF(Speed11==""): setProperty("Speed11","")]

   
[Speed1="Move: "+Speed1+""]
[Spd1=""+Speed11+""]
} ;{
	[Speed1=""]
	[Spd1=0]

}]
[H, IF(1< getFindCount(id)), CODE: {
   [Speed2=getGroup(id, 2, 1)]
   [IF(Speed2==""): setProperty("Speed2","")]
   [IF(Speed2!=""): Speed22=getGroup(id, 2, 2)]
   [IF(Speed22==""): setProperty("Speed22","")]
[Speed2=""+upper(Speed2,1) + ": "+Speed22+""]
[Spd2=""+Speed22+""]
};{
	[Spd2=0]

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

}]
[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+""]
   [Spd4=""+Speed44+""]
};{
	[Spd4=0]

}]
[H, IF(4< getFindCount(id)), CODE: {
   [Speed5=getGroup(id, 5, 1)]
   [IF(Speed5==""): setProperty("Speed5","")]
   [IF(Speed5!=""): Speed55=getGroup(id, 5, 2)]
   [IF(Speed55==""): setProperty("Speed55","")]
   [Speed5=""+upper(Speed5,1) + ": "+Speed55+""]
   [Spd5=""+Speed55+""]
};{
	[Spd5=0]

}]

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

[h: Spd= Max(eval("Spd1"),eval("Spd2"),eval("Spd3"),eval("Spd4"),eval("Spd5"))]



[H: '<!-- SET Hit Points -->']
[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: '<!-- SET SIZE -->']
[H: id = strfind(statblock, "(Tiny|Small|Medium|Large|Huge|Gargantuan|\w-]+)")]
[H, IF(0< getFindCount(id)),CODE: {

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

   [setSize(Size)]

}]

[H: '<!-- SET TYPE -->']
[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: '<!-- SET ALIGNMENT -->']
[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)]
   [Alignment1=trim(Alignment1,1)]
   [Alignment2=trim(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<=16&&CR>=13): setProperty("SkillDie",5)]
   [IF(CR<=20&&CR>=17): 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","")]




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




[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: '<!-- SET SENSES AND SIGHT -->']
[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)]
}]

[H: '<!-- SET XP TO GM NAME -->']
[H: id = strfind(statblock, "Challenge.[0-9]+.(.*XP.)")]
[H, IF(0< getFindCount(id)), CODE: {
   [XP=getGroup(id, 1, 1)]
	[setGMName(XP)]
  
}]
[H: '<!-- HATE BARS -->']
[h: setBarVisible("Health", 0)]


[h: ids=getTokenNames()]
[h:gTok=getTokenImage()] 
[h:iTok=getTokenHandout()] 
[h:setProperty("PIC1",gTok)]
[h:setProperty("PIC2",iTok)]


EDIT: Finished for now, feel free to comment.
UPDATED: December-23-2017, I now use this website to copy and paste the stats;



NOTE: This will only work if you have the following properties under edit-Campaign Properties-Name: 5E-NPC
Spoiler
@Strength (Str):8
@Dexterity (Dex):8
@Constitution (Con):8
@Intelligence (Int):8
@Wisdom (Wis):8
@Charisma (Cha):8
St:{floor((Strength - 10)/2)}
Dx:{floor((Dexterity - 10)/2)}
Cn:{floor((Constitution - 10)/2)}
In:{floor((Intelligence - 10)/2)}
Ws:{floor((Wisdom - 10)/2)}
Ch:{floor((Charisma - 10)/2)}
Modifier:[]
RechargePowers:{}
RequiresRecharge
HitDice:1d6
@SkillDie:0
@InitMod:0
CurrentHitDice:{CR}
*Type:Grue
*@#Alignment
*@Class
*@CR:{Level}
#Level
HP:0
*@HPDisplay (HP):{HP+TempHP} ({MaxHP})
MaxHP:0
@TempHP:0
*@AC:0
*@Speed
Description (Des)
#DailySpells:[]
#DailyPower1
#DailyPower2
#DailyPower3
#DailyPower4
#DailyPower5
Resistance
*@sheet.Resistance (Resistance):[r, if(! json.isEmpty(Resistance)): substring(Resistance,0,min(length(Resistance),50)); ""]
*@sheet.specialR2 (Resist cont):[r, if(Resistance == "NA" || Resistance == "" || length(Resistance) <= 50): ""; substring(Resistance,50,min(length(Resistance),100))]
*@sheet.specialR3 (R --):[r, if(Resistance == "NA" || Resistance == "" || length(Resistance) <= 100): ""; substring(Resistance,100,min(length(Resistance),150))]
*@Vulnerability
*@Immunities
Condition Immunities
ConI:{getProperty("Condition Immunities")}
*@sheet.ConI (Condition Immunities):[r, if(! json.isEmpty(ConI)): substring(ConI,0,min(length(ConI),51)); ""]
*@sheet.specialC2 (Immune -):[r, if(ConI == "NA" || ConI == "" || length(ConI) <= 51): ""; substring(ConI,51,min(length(ConI),100))]
*@sheet.specialC3 (Immune --):[r, if(ConI == "NA" || ConI == "" || length(ConI) <= 100): ""; substring(ConI,100,min(length(ConI),150))]
*@Languages
Skills:[]
@CritRange:20
*@Passive Perception:{10+Ws+SkillDie}
PassivePerception
*@Note
Charges
Spd:30
Speed1:""
Speed2:""
Speed3:""
Speed4:""
Speed5:""
First:0
Second:0
Third:0
Fourth:0
Fifth:0
Sixth:0
Seventh:0
Eighth:0
Ninth:0
XP
Rage
*@Luck
*@#Stealth
*@Ki
*@#ActionSurge
*@#WildShapes
HealingPotion
WSRetainedHP
CurShape
ChannelDivinity
RageDmg
Exhaustion:0
SecondWind
SpellSave:{8+SkillDie+ Max (Ws,In,Ch)}
SpellAttack:{SkillDie+ Max (Ws,In,Ch)}
*@SpellSlots (Spell Slots)
Concentration
*sheet.appearance (Appearance)
*sheet.distance (Distance):[R: distanceStatSheet(currentToken())]
*sheet.IsBloodied (Is Bloodied):[r:if(HP/MaxHP < 0.5, "YES!", "")]
*@sheet.physical (Str Dex Con):[R: strformat("%s(%+d) %s(%+d) %s(%+d)",Strength,St,Dexterity,Dx,Constitution,Cn)]
*@sheet.mental (Int Wis Cha):[R: strformat("%s(%+d) %s(%+d) %s(%+d)",Intelligence,In,Wisdom,Ws,Charisma,Ch)]
Coinpurse
Weightload:{Strength*15}
DeathFail:0
DeathSucc:0
dnd.weapons
Inventory:[]
*Conditions:[h:theList = getTokenStates()][r, foreach(item, theList, ""): if(getState(item), item + ", ", "")]
*size
Senses:""
PIC1:[]
PIC2:asset://cf90363732c7d24e997eefee527bbeb5
InitAmmo:0
Also you would have to have these set for your sight properties;
Spoiler
Truesight 120: circle r122
Darkvision60: circle r62
Blind: circle distance=2.5 r2
Darkvision30: circle r32
Blindsight90: circle r92
Darkvision120: circle r122
Tremorsense90: circle r92
Tremorsense60: circle r62
Normal: circle r5
Darkvision90: circle r92
Dazzled: circle x0.5 r2
Tremorsense120: circle r122
Obsuring Mist: circle distance=12
Truesight90: circle r92
Tremorsense30: circle r32
Blindsight60: circle r62
Truesight120: circle r122
Truesight60: circle r62
Truesight30: circle r32
Blindsight30: circle r32
Blindsight120: circle r122
It works seamlessly for me, let me know if there is any glitches. It should also work with the D&D Beyond website monster statblocks.

I'd also like to thanks AliasMask for the original statblock importer to which I heavily "borrowed" most of his code and his seemingly endless generosity in helping me and others with our coding techniques. I'd also like to thank Wolph42 for all his input, his awesome BoT, and patience. JamztheMan and paulstrait for their contribution as well. :lol:
Last edited by Sereptus on Mon Aug 12, 2019 11:15 am, edited 5 times in total.
OOOHH RegEx....YOU BITTER-SWEET BEAST!!!

Hazeyindahead
Kobold
Posts: 5
Joined: Fri Feb 05, 2016 12:18 pm

Re: D&D 5e Statblock Importer.

Post by Hazeyindahead »

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.

User avatar
Sereptus
Giant
Posts: 123
Joined: Tue Jun 07, 2011 12:08 pm
Location: Canada

Re: D&D 5e Statblock Importer.

Post by Sereptus »

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.
OOOHH RegEx....YOU BITTER-SWEET BEAST!!!

User avatar
PinkRose
Dragon
Posts: 732
Joined: Sat Nov 15, 2008 2:28 pm
Location: The City of Roses, Oregon

Re: D&D 5e Statblock Importer.

Post by PinkRose »

It doesn't seem to be working with D&D Beyond.
Should it?
I get, "Stats in statblock not found."
I am a special snowflake!

Post Reply

Return to “Macros”