Page 2 of 3

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Wed Apr 18, 2012 10:43 pm
by DMFTodd
I'm using the Statblock2token, works pretty well, but on the occasions it doesn't work I'm stumped. Is there any tips & trips for getting the 1.8 import to work smoothly?

Or, if you want specifics, how come this stat block fails to create the attack for the Shortsword?

Code: Select all

Zyphus Cultist CR 1
Male or Female human fortune-spurned warrior 1
NE Medium humanoid (human)
Init +1; Senses Perception –2
Aura aura of misfortune (10 ft.)
Defense
AC 17, touch 11, flat-footed 16 (+4 armor, +1 Dex, +2 shield)
hp 9 (1d10+4)
Fort +1, Ref –1, Will –3
Offense
Speed 30 ft.
Melee short sword +2 (1d6+1/19–20)
Tactics
Before Combat The cultists rest in the light of a single torch in
the northeast corner of the tunnels. If they know the PCs are
coming, they move back out of sight and wait to ambush the
PCs either in the northeast corner or in the south where the
two tunnels reconnect.
During Combat The cultists are not strategic fighters—they pick
the nearest target, get close, and attack furiously.
Morale With no other options left to them, the cultists fight to
the death.
Statistics
Str 13, Dex 12, Con 13, Int 10, Wis 9, Cha 8
Base Atk +1; CMB +2; CMD 13
Feats Toughness, Weapon Focus (short sword)
Skills Climb –1, Intimidate +1, Knowledge (religion) –1, Perception
–2; Racial Modifiers A fortune-spurned creature takes a –
2
penalty on all skill checks—this penalty stacks with the one it
takes on opposed checks
Languages Common
SQ cursed, diminished criticals, twist of fate, unlucky strike (DC 9)
Combat Gear potion of cure light wounds; Other Gear chain shirt,
heavy wooden shield, short sword

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Thu Apr 19, 2012 7:04 pm
by neofax
Yes, your em-dashes kill the regular expression matching. I bet you were getting a "java.lang.String, expecting java.math.BigDecimal" error when running the macro. It was dying on the first em-dash in Ref -1 and anything after that did not get added to the properties. Use notepad++ to remove those em-dashes with actual dashes and it will work fine.

P.S. I attached the token to my post.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Thu Apr 19, 2012 7:55 pm
by DMFTodd
That did it. Thanks a bunch.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Sat Apr 28, 2012 4:13 pm
by DMFTodd
Anybody see a problem with this one:

Code: Select all

Greater Reefclaws (3) CR 2
Advanced reefclaw (Pathfinder RPG Bestiary 2 234, 292)
CN Small aberration (aquatic)
Init +7; Senses darkvision 60 ft., low-light vision; Perception +8
DEFENSE
AC 18, touch 14, flat-footed 15 (+3 Dex, +4 natural, +1 size)
hp 17 each (2d8+8)
Fort +4, Ref +3, Will +6
Defensive Abilities ferocity; Resist cold 5
OFFENSE
Speed 5 ft., swim 40 ft.
Melee 2 claws +4 (1d4+2 plus grab and poison)
Special Attacks death frenzy, constrict (1d4+2)
TACTICS
Before Combat The reefclaws wait until the entire party
has entered the chamber or one of their foes has its back
turned before they attack, surrounding their enemies to
block the exit.
During Combat Silently drifting from the watery recesses of
the southernmost chamber, they pounce from the water
and grab their prey, attempting to drag creatures back
toward the water to drown them.
Morale The reefclaws fight to the death.
STATISTICS
Str 14, Dex 17, Con 18, Int 9, Wis 16, Cha 17
Base Atk +1; CMB +2 (+10 grapple); CMD 15 (can’t be tripped)
Feats Improved Initiative
Skills Perception +8, Swim +10
Languages Common (can’t speak)
SQ amphibious
SPECIAL ABILITIES
Death Frenzy (Su) See Subtier 1–2.
Grab (Ex) See Subtier 1–2.
Poison (Ex) Claw—injury; save Fort DC 15; frequency 1/round
for 4 rounds; effect 1d2 Str; cure 1 save. The save DC is
Constitution-based.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Mon Apr 30, 2012 9:38 pm
by neofax
I just made the token both with the 1.8 version and 1.9. Can you post your error?

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Mon Apr 30, 2012 10:07 pm
by DMFTodd
There's no error but when you hit "F2" for an attack, it asks "Value for Feats". Checking the token, the attack information is not there.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Tue May 01, 2012 7:50 am
by neofax
I'll try it on the PC at work and see if I can get it working.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Tue May 01, 2012 9:56 am
by neofax
I have no clue. The regular expression catches the claws attack, but for some reason it doesn't add it as a weapon. I am going to have to place debugging pauses in to see where it doesn't work. I won't be able to do this until tomorrow evening.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Tue May 01, 2012 10:18 am
by DMFTodd
It appears to be the word "and" in the attack's "plus grab and poison" that is confusing things. Remove the "and" and the 1.8 import works fine. Parser must be seeing "and" and thinking another attack is starting.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Tue May 01, 2012 6:46 pm
by Azhrei
I don't recall that RE off-hand, but the word "and" might be looking like a dice roll where the number of dice is "an" (since the letter "d" is how dice are rolled).

It seems unlikely though unless the RE is playing it fast-and-loose; I would typically use "\\dd" which would mean a digit followed by a "d"... Don't know if that's my RE creation or not...

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Tue May 15, 2012 11:13 pm
by JamzTheMan
Am I doing something wrong or do the statBlock importers not handle CR properly?

Tried creating a rat, kobold, and orc and it set them all to level/cr 1. It doesn't seem to like CR < 1?

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Wed May 16, 2012 12:22 am
by DMFTodd
I don't think it does it. My imports have CR less than 1 wrong as well.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Wed May 16, 2012 7:04 pm
by JamzTheMan
I did some more importing last night and it looks like it just ignores CR completely, it looks like it grabs the HD, puts that in correctly, and then just leaves CR == 'Level'.

Maybe there is an option somewhere I'm missing? Just started to play with the framework this week and spent 2 hours messing with pits/traps before I figured out you had to use the Init Pads macro (which I thought was for TP pads only). There's SO much lol

May have to dive into that macro and take a peek...

BTW, does anyone know what the difference is between 1.6 and 1.8? I've tried both with PDF and d20pfrd site and tokens seem to come out the same so far.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Thu May 17, 2012 7:11 am
by neofax
Skills with spaces in the name like Sleight of Hand. That is the difference between 1.6 and 1.8. Yes, the CR is ignored as the original author I assume didn't care to use the XP macro in the campaign.

Re: Statblock2Token v1.8 ? I only see v1.6

Posted: Fri May 18, 2012 1:41 am
by JamzTheMan
OK, so where IS the latest version of this drop-in? I see a post of a .txt above and a copy/paste of code but not sure which one is the latest and some don't work quite right?

I've taken the 1.8 that was int the framework (latest!), which btw, also seems out of date with most of the drop-ins :(

I'm not complaining and love all the work, it's just taking me forever to cobble together the latest code everywhere for my new PF campaign...

If anyone needs the the correct parsing of CR, here's what I added:

Code: Select all

<!-- Setting ECL to CR from statblock. For CR 1/3 & 1/6 we will set it to .338 & .1625 so XP calcs correctly.-->
[H: id = strfind(statblock, "(?<=[ |\\t]CR )([1-9]+)(/[1-9]+)?")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [ECL=trim(getGroup(id, 1, 0))]
  [output=output+"CR: "+ECL+", "]
  [IF(ECL == "1/3"),CODE:{
    [ECL=".338"]
  }]
  [IF(ECL == "1/6"),CODE:{
    [ECL=".1625"]
  }]
  [Levels = setStrProp(Levels, "ECL", ECL)]
  }]
Complete macro code based on 1.8 in latest FW

Code: Select all

<!-- Statblock2Token v1.9
Changes: HD fixed, Resets token, Skills with space in the name except SleightOfHand
Macro takes a Pathfinder statblock as input and updates selected token with various stats.
Macro assumes pathfinder properties in the selected token.
Statblock format should mirror PF bestiary format.
Not handled: SQ, Spells
-->
[H: ids = getSelected()]
[H: abort(if(ids == "", 0, 1))]

[H: status=input("statblock|Insert statblock here|Enter statblock|TEXT|WIDTH=40")]
[H: abort(if(status < 1, 0, 1))]

[H: setPropertyType("Pathfinder")]

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

[H: setGMNotes(statblock)]
[H: output="Token Updated:<br>"]
[H: id = strfind(statblock, "^(.+).*CR ")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [name=trim(getGroup(id, 1, 1))]
  [output=output+"Name: "+name+", "]
  [Race =  json.set(Race, "name", name)]
  [setName( name)]
   }]

<!-- Setting ECL to CR from statblock. For CR 1/3 & 1/6 we will set it to .338 & .1625 so XP calcs correctly.-->
[H: id = strfind(statblock, "(?<=[ |\\t]CR )([1-9]+)(/[1-9]+)?")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [ECL=trim(getGroup(id, 1, 0))]
  [output=output+"CR: "+ECL+", "]
  [IF(ECL == "1/3"),CODE:{
    [ECL=".338"]
  }]
  [IF(ECL == "1/6"),CODE:{
    [ECL=".1625"]
  }]
  [Levels = setStrProp(Levels, "ECL", ECL)]
  }]

[H: id = strfind(statblock, "(Fine|Diminutive|Tiny|Small|Medium|Large|Huge|Gargantuan|Colossal).([a-zA-Z]+\\s?[a-zA-Z]*?)\\s?(\\((.+)\\))?.?Init")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [Size_txt=getGroup(id, 1, 1)]
  [setSize(Size_txt)]
  [sizeList = table( "SysVars", json.get( table( "SysVars", 0 ), "sizeList" ) ) ]
  [sizeModList = "8,4,2,1,0,-1,-2,-4,-8"] 
  [Size=listFind(sizeList, Size_txt)]
  [SizeM = listGet(sizeModList, Size)]
  [type=upper(substring(getGroup(id, 1, 2),0,1))+substring(getGroup(id, 1, 2),1)]
  [Race =  json.set(Race, "type", type)]
  [subtypes=getGroup(id, 1, 4)]
  [output=output+"Size: "+Size_txt+", type: "+type+", subtypes: "+subtypes+"<br>"]
  [subtypes="['"+replace(subtypes,", *","','")+"']"]
  [Race =  json.set(Race, "subtype",subtypes)]
  }]

[H: id = strfind(statblock, "Senses ([lL]ow-?light|[dD]arkvision)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [sighttype=upper(substring(getGroup(id, 1, 1),0,1))+substring(getGroup(id, 1, 1),1)]
  [sighttype=replace(sighttype, "-", "")]
  [setSightType(sighttype)]
  [setHasSight(1)]
  [output=output+"Sighttype: "+sighttype+", "]
  }]

[H: id = strfind(statblock, "Reach ([0-9]+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [Reach=getGroup(id, 1, 1)]
  [output=output+"Reach: "+Reach+", "]
  }]

[H: id = strfind(statblock, "DR(.?[0-9]+)/(.+?)[;O]")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [DR=getGroup(id, 1, 1)+"/"+getGroup(id, 1, 2))]
  [output=output+"DR: "+DR+", "]
  }]

[H: id = strfind(statblock, "Resist (\\w+) (\\d+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [resist = getGroup(id, 1, 1)+" "+getGroup(id, 1, 2))]
  [DR=DR+" Resist "+resist]
  [output=output+"Resist: "+resist+", "]
  }]

[H: id = strfind(statblock, "SR ([0-9]+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [SR=getGroup(id, 1, 1)]
  [SpecialQual="SR "+SR]
  [output=output+"SR: "+SR+", "]
  }]

<!-- STATS BEGIN -->
[H: id = strfind(statblock, "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=output+"Str: "+Strength+", Dex: "+Dexterity+", Con: "+Constitution+", Int: "+Intelligence+", Wis: "+Wisdom+", Cha: "+Charisma+"<br>"]
  }]
<!-- STATS END -->

<!-- SAVES BEGIN -->
[H: id = strfind(statblock, "Fort (.?[0-9]+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [Fort=getGroup(id, 1, 1)-ConB]
  [output=output+"Fort: "+fort+", "]
  }]
[H: id = strfind(statblock, "Ref (.?[0-9]+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [Reflex=getGroup(id, 1, 1)-DexB]
  [output=output+"Ref: "+reflex+", "]
  }]

[H: id = strfind(statblock, "Will (.?[0-9]+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [Will=getGroup(id, 1, 1)-WisB]
  [output=output+"Will: "+Will+"<br>"]
  }]
<!-- SAVES END -->

[H: id = strfind(statblock, "hp ([0-9]+).+?([0-9]+)")]
[H, IF(0<getFindCount(id)),CODE:
  {
  [HP=getGroup(id, 1, 1)]
  [HPmax="[R: max(Level, ( "+(HP-ConB*Level)+" + (ConB * Level) ) )]"]
  [HD=getGroup(id, 1, 2)]
  [Level = max(1,HD)]
  [output=output+"HP: "+HP+", HD: "+HD+"<br>"]
  }]

[H: id = strfind(statblock, "Base Atk.?(.?[0-9]+);")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [BAB=getGroup(id, 1, 1)]
  [output=output+"Base Atk: "+BAB+", "]
  }]

<!-- AC BEGIN -->
[H: id = strfind(statblock, "(.?[0-9]+) natural")]
[H, IF(0<getFindCount(id)): natural=getGroup(id, 1, 1); natural=0]
[H: ArmorClass =  setStrProp(ArmorClass, "Natural", natural)]

[H: id = strfind(statblock, "(.?[0-9]+) armor")]
[H, IF(0<getFindCount(id)): armor=getGroup(id, 1, 1);armor=0]
[H: ArmorClass =  setStrProp(ArmorClass, "Armor", armor)]

[H: id = strfind(statblock, "(.?[0-9]+) deflection")]
[H, IF(0<getFindCount(id)): deflect=getGroup(id, 1, 1); deflect=0]
[H: ArmorClass =  setStrProp(ArmorClass, "Deflection", deflect)]

[H: id = strfind(statblock, "(.?[0-9]+) dodge")]
[H, IF(0<getFindCount(id)): dodge=getGroup(id, 1, 1); dodge=0]
[H: ArmorClass =  setStrProp(ArmorClass, "Dodge", dodge)]

[H: id = strfind(statblock, "(.?[0-9]+) shield")]
[H, IF(0<getFindCount(id)): shield=getGroup(id, 1, 1); shield=0]
[H: ArmorClass =  setStrProp(ArmorClass, "Shield", shield)]

[H: id = strfind(statblock, "AC ([0-9]+),")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [AC=getGroup(id, 1, 1)]
  [tch=AC-armor-shield]
  [FF=AC-dodge-DexB]
  [CMD=10+BAB+StrB+DexB-SizeM+dodge+deflect]
  [CMDFF=CMD-DexB-dodge]
  [AC=concat(AC,"/",tch,"/",FF,"/",CMD,"/",CMDFF)]
  [output=output+"AC/tch/FF/CMD/CMDFF: "+AC+"<br>"]
  }]
<!-- AC END -->


[H: id = strfind(statblock, "(Speed|Spd) ([0-9]+) ft")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [spd=getGroup(id, 1, 2)]
  [output=output+"Spd: "+spd]
  [Speed = json.set(Speed, "base",spd)]
  }]
[H: id = strfind(statblock, "fly ([0-9]+) ft")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [spd=getGroup(id, 1, 1)]
  [output=output+", Fly: "+spd]
  [Speed = json.set(Speed, "fly",spd)]
  }]
[H: id = strfind(statblock, "swim ([0-9]+) ft")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [spd=getGroup(id, 1, 1)]
  [output=output+", Swim: "+spd]
  [Speed = json.set(Speed, "swim",spd)]
  }]
[H: id = strfind(statblock, "burrow ([0-9]+) ft")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [spd=getGroup(id, 1, 1)]
  [output=output+", Burrow: "+spd]
  [Speed = json.set(Speed, "burrow",spd)]
  }]
[H: id = strfind(statblock, "climb ([0-9]+) ft")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [spd=getGroup(id, 1, 1)]
  [output=output+", Climb: "+spd]
  [Speed = json.set(Speed, "climb",spd)]
  }]
[H: output=output+"<br>"]

<!-- SKILLS BEGIN -->
[H: tSkillsJ ="[]"]
[H: output=output+"Skills: "]
[H, FOREACH(s, SkillsJ), CODE:
  {
  [skN = json.get(s, "name")]
  [id=0]
  [id = strfind(statblock, skN+"\\s(.?[0-9]+)")]
  [H, IF(0<getFindCount(id)), CODE:
    {
    [skN = replace(skN, " ", "")] <!-- Remove spaces from skill name -->
    [skStat = getStrProp(SkillStat, skN)]   
    [skVal = getGroup(id, 1, 1)]
    [output=output+skN+" "+skVal+", "]
    [s =  json.set( s, "rank", skVal-eval(skStat))]   
    }]
  [tSkillsJ = json.append(tSkillsJ,s)]
  }]
[H: output=output+"<br>"]
[H: SkillsJ=tSkillsJ]
<!-- SKILLS END -->

<!-- FEATS BEGIN -->
[H: output=output+"Feats: "]
[H: id = strfind(statblock, "(Improved Initiative)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedInitiative",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedInitiative",0)]
  }]
[H: id = strfind(statblock, "(Agile Maneuvers)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "AgileManeuvers",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "AgileManeuvers",0)]
  }]
[H: id = strfind(statblock, "(Improved Bull Rush)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedBullRush",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedBullRush",0)]
  }]
[H: id = strfind(statblock, "(Improved Disarm)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedDisarm",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedDisarm",0)]
  }]
[H: id = strfind(statblock, "(Improved Grapple)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedGrapple",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedGrapple",0)]
  }]
[H: id = strfind(statblock, "(Improved Overrun)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedOverrun",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedOverrun",0)]
  }]
[H: id = strfind(statblock, "(Improved Sunder)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedSunder",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedSunder",0)]
  }]
[H: id = strfind(statblock, "(Improved Trip)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedTrip",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedTrip",0)]
  }]
[H: id = strfind(statblock, "(Improved Channeling)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "ImprovedChanneling",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "ImprovedChanneling",0)]
  }]
[H: id = strfind(statblock, "(Two.?Weapon Fighting)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "TwoWeaponFighting",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "TwoWeaponFighting",0)]
  }]
[H: id = strfind(statblock, "(Multi.?[aA]ttack)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "MultiAttack",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "MultiAttack",0)]
  }]
[H: id = strfind(statblock, "(Weapon Finesse)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "WeaponFinesse",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "WeaponFinesse",0)]
  }]
[H: id = strfind(statblock, "(Uncanny Dodge)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "UncannyDodge",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "UncannyDodge",0)]
  }]
[H: id = strfind(statblock, "(Die.?[hH]ard)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [Feats=setStrProp(Feats, "DieHard",1)]
  [H: output=output+getGroup(id, 1, 1)+", "]
  };{
  [Feats=setStrProp(Feats, "DieHard",0)]
  }]
[H: output=output+"<br>"]
<!-- FEATS END -->

[H: id = strfind(statblock, "Special Attacks (.+)(Statis|STATIS)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [SpecialATK=getGroup(id, 1, 1)]
  [SpecialATK=trim(SpecialATK)]
  [SpecialATK=substring(SpecialATK,0,min(length(SpecialATK),70))]
  [output=output+"SA: "+SpecialATK+"<br>"]
  };{
  [SpecialATK=""]
  }]

<!-- ATTACKS BEGIN -->
<!-- Insert empty weapon strings -->
[H: wpnstr=getPropertyDefault( "Weapon0" )]
[H, c(7,""),CODE:
  {
  [wpn="Weapon"+roll.count]
  [eval(wpn + " = '" + wpnstr+ "'")]
  }]

[H: atkno = 0]
<!-- MELEE -->
<!-- The '(?i)' makes the expression case-insensitive -->
[H: id = strfind(statblock, "Melee (.+?)(?i)(Tactic|Face|Space|Reach|Special|Ranged|Stat)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [allmelee=getGroup(id, 1, 1)]
  <!-- separate each attack option -->
  [allmelee=stringToList(allmelee, "( or (Melee)?)|(Melee)",":")]
  [atkno = listCount(allmelee, ":")]
  }]
<!-- Loop through each attack option and divide into weapons -->
[H: wpnarray=""]
[H: wpnno=0]
[H, FOR(i,0,atkno,1), CODE:
  {
  [atkstr=listget(allmelee, i, ":")]
  [atkstr=stringToList(atkstr, "( and )|(, )",":")]
  [wpns = listCount(atkstr, ":")]
  [FOR(j,0,wpns,1), CODE:
  {
    [WpnName = "Weapon" + wpnno]
    [wpnarray=json.append(wpnarray, trim(listGet(atkstr,j,":")))]
    [ x = eval(WpnName) ]
    [IF(j>0):eval(WpnName + "= '" + replace(x,"Primary=\\d+", "Primary=2")+ "'")]
    <!-- Set attack options after first to secondary attacks -->
    [eval(WpnName + "= '" + replace(x,"OHLight=\\d+", "OHLight=2")+ "'")]
    <!-- Default to not multiattack -->
    [wpnno = wpnno +1]
    }]
  }]
<!-- RANGED -->
[H: atkno = 0]
[H: id = strfind(statblock, "Ranged (.+?)(Tac|Face|Space|Reach|Special|Ranged|STAT|Stat)")]
[H, IF(0<getFindCount(id)), CODE:
  {
  [allranged=getGroup(id, 1, 1)]
  <!-- separate each attack option -->
  [allranged=stringToList(allranged, "(\\s?or (Ranged)?)|(Ranged)",":")]
  [atkno = listCount(allranged, ":")]
  }]
<!-- Loop through each attack option and divide into weapons -->
[H, FOR(i,0,atkno,1), CODE:
  {
  [atkstr=listget(allranged, i, ":")]
  [atkstr=stringToList(atkstr, "( and )|(, )",":")]
  [wpns = listCount(atkstr, ":")]
  [FOR(j,0,wpns,1), CODE:
    {
    [WpnName = "Weapon" + wpnno]
    [wpnarray=json.append(wpnarray, trim(listGet(atkstr,j,":")))]
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"Ranged", 1)+ "'")]
    [wpnno = wpnno +1]
    }]
  }]
<!-- extract info from each attack  -->
[H: wpnno=0]
[H, FOREACH(atkstr, wpnarray), CODE:
  {
  [atkstr=trim(atkstr)]
  [id = strfind(atkstr, "([+-]\\d+)?(\\d+)?(\\d?\\s?[a-zA-Z ]+?) ([+-]\\d+)[/+\\-0-9\\s]*?\\((.+)\\)")]
  [IF(0<getFindCount(id)), CODE:
    {
    [WpnName = "Weapon" + wpnno]
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"Name", getGroup(id, 1, 3))+ "'")]
    <!-- Quantity -->
    [wquant = 1]
    [wbonus = ""]
    [IF(getGroup(id,1,1)==""): wquant = getGroup(id, 1, 2); wbonus="+ "+getGroup(id,1,1)]
    [IF(getGroup(id,1,2)==""): wquant = 1]
    [IF(1<wquant):eval(WpnName + "= '" + setStrProp(eval(WpnName),"Quantity", wquant)+ "'")]
    <!-- Manufactored ? -->
    [id2 = strfind(atkstr, "(slam|bite|claw|gore|hoof|tentacle|wing|pincer|tail|sting|talon)")]
    [IF(0==getFindCount(id2)):eval(WpnName + "= '" + setStrProp(eval(WpnName),"Manufactured", 1)+ "'")]
    <!-- Not a natural weapon -->
    <!-- Attack bonus (assumes StrB for melee and DexB for ranged) -->
    [watkbonus=getGroup(id, 1, 4)]
    [Ranged=getStrProp(eval(WpnName),"Ranged")]
    [IF(Ranged>0):watkbonus=watkbonus-BAB-DexB;watkbonus=watkbonus-BAB-StrB-SizeM]
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"AtkBonus", watkbonus)+ "'")]
    <!-- CritMult-->
    [wdam=getGroup(id, 1, 5)]
    [CritMult=2]
    [id2 = strfind(wdam, "/[xX](\\d)")]
    [IF(0<getFindCount(id2)):CritMult=getGroup(id2, 1, 1)]
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"CritMult", CritMult)+ "'")]
    <!-- CritRange-->
    [CritRange=20]
    [id2 = strfind(wdam, "/(1[0-9])")]
    [IF(0<getFindCount(id2)):CritRange=getGroup(id2, 1, 1)]
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"CritRange", CritRange)+ "'")]
    <!-- Damage -->
    [Damage="0d4 plus" + wdam]
    [id2 = strfind(wdam, "(\\dd\\d+)[^p]*(plus .+)?")]
    [IF(0<getFindCount(id2)):Damage=getGroup(id2, 1, 1)+wbonus+" "+getGroup(id2, 1, 2)]
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"Damage", Damage)+ "'")]
    <!-- Two handed ? -->
    [eval(WpnName + "= '" + setStrProp(eval(WpnName),"TwoHanded", 0)+ "'")] <!-- Default to onehanded weapon -->
    [id2 = strfind(atkstr, "(longspear|quarterstaff| spear|falchion|glaive|greataxe|greatclub|greatsword|guisarme|halberd|lance|ranseur|scythe|spiked chain|elven curve blade|dire flail|two-bladed sword|urgrosh|hooked hammer)")]
    [IF(0<getFindCount(id2)):eval(WpnName + "= '" + setStrProp(eval(WpnName),"TwoHanded", 1)+ "'")]
    }]
  [wpnno = wpnno +1]
  }]

[H: output = output + "Weapons: " + wpnarray+"<br>"]
<!--
Name=Slam ; Primary=1 ; Quantity=1 ; Manufactured=0 ; AtkBonus=0 ; CritMult=2 ; CritRange=20 ; Damage=1d4 ; DmgMax=4 ; DmgExtra=0d6 ; DmgExtraCrit=0d10 ; DmgExtraName= ; DmgBonusCap=50 ; TwoHanded=0 ; Finesse=0 ; OHLight=0 ; Ranged=0  ;
ATTACKS END -->

[R,S,G: output]