[AM Macros] Lindsay's PF/3.5 Campaign FW (11-13-15)

Discussion concerning lmarkus' campaign framework for D&D3.x and Pathfinder.

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

Forum rules
Discussion regarding lmarkus001's framework only. Other posts deleted without notice! :)
User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by aliasmask »

Which output, casting a spell?

User avatar
lmarkus001
Great Wyrm
Posts: 1862
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by lmarkus001 »

Yeah, output.basic. I am going to resolve that now.

It is a Mote bug around isGM(). I am reporting it.

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by aliasmask »

lmarkus001 wrote:Yeah, output.basic. I am going to resolve that now.

It is a Mote bug around isGM(). I am reporting it.


Ah, I think that was an MT change after their port. I have a better solution.

REMOVE

Code: Select all

[H: players.gm = "[]"]
[H, foreach(player,players.all), if(isGM(player)): players.gm = json.append(players.gm,player)]

ADD IN ITS PLACE

Code: Select all

[H: players.gm = json.get(getInfo("server"),"gm")]


This too was a change after their port, but I recall discussion on one of their forums about adding this. This will only work in MT b91 or later though. I'm not sure if they kept the same function name or changed it to one of their other naming conventions.

User avatar
lmarkus001
Great Wyrm
Posts: 1862
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by lmarkus001 »

Nice thought, but the Mote getInfo() reports differently...


So here is my near term workaround where I do version checking and use isGM() on MT and getGMNames() on Mote. Next Mote version will include isGM() functionality :-)

Code: Select all

[H: info        = getInfo("client")]
[
H: mtVersion    = json.get(info, "version")]
[
h: id        = strfind(mtVersion, ".+\\.(.+)\\.[A-z]*([0-9]+)[A-z]*")]
[
H: mtMainVerNr    = getGroup(id, 1, 1)]
[
H: mtVersionNr    = getGroup(id, 1, 2)]

<!--
 validate token is on current map -->
[
H: tokenId = findToken(token)]
[
H, if(! json.isEmpty(tokenId)), code: {
   [H: tokenName = getName(tokenId)]
   [H: tokenImage = getImage(tokenName)]
};{}]

<!--
 parse list of targets in to list of players names -->
[
H, if(json.type(targets) == "UNKNOWN"): targets = json.fromList(targets)]

<!--
 get player groups -->
[
H: players.all = getAllPlayerNames("json")]

[
H,IF(mtVersionNr>88),CODE: {
    [H: players.gm = "[]"]
    [H, foreach(player,players.all), if(isGM(player)): players.gm = json.append(players.gm,player);""]    
};{
    [H: players.gm = getGMNames("json")]
}]
 
Attachments
Libspells3.rptok
(549.52 KiB) Downloaded 74 times

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by aliasmask »

Maybe we can skip the longer version checking with this:

Code: Select all

...
<!--
 get player groups -->
[
H: isMT = json.contains(json.fields(getInfo("server"),"json"),"gm")]
[
H, if(isMT): players.gm = json.get(getInfo("server"),"gm"); players.gm = getGMNames("json")]
[
H: players.all = getAllPlayerNames("json")]
[
H: player.self = json.append("",getPlayerName())]
...
 

It accomplishes the same thing, but has the same flaw of when "gm" doesn't exist it will call a function that doesn't exist. We already know it's a pain in the behind to get gm Names before b89(?) so I'll just leave this as an incompatibility with versions.

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by aliasmask »

I just found a mistake (oversight) with the template fix. When counting spells, I only counted the number of different spells rather than the number memorized. This only matter if you're a prep caster and you memorized duplicates. It will wrongly show you have an extra spell(s) to memorize. I'll fix this tomorrow.

User avatar
lmarkus001
Great Wyrm
Posts: 1862
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by lmarkus001 »

Here is the output.basic version check I am now using...

Code: Select all

[HclientType json.get(getInfo("client"),"name")]
[
Hplayers.gm "[]"]
[
H, if(clientType=="Mote"), CODE: {
    [ 
players.gm getGMNames() ]
};{
    [
H, foreach(player,players.all), if(isGM(player)): players.gm json.append(players.gm,player);""]
}] 

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by aliasmask »

isGM(player) and json.get(getInfo("server"),"gm") were implemented at the same time so there's no advantage as far as version-ing goes, but I personally like getting a single value over running a loop. So, it's just apples and oranges at this point and they accomplish the same thing. I also try to avoid the double roll option which is an MT quirk that MOTE gave in and implemented by prioritizing left to right when running the roll options.

User avatar
lmarkus001
Great Wyrm
Posts: 1862
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by lmarkus001 »

The isGM("player") was broken as implemented in Mote (hence my alternate code). They just repaired it today :-)

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by aliasmask »

More updates to lib:spells3

(9-1-15)
* Fixed Local Cheating when there is more than one spell casting class.
* Fixed template restore to include correct remainder of spells when prep casters memorize the same spell more than once.
* Fixed ability score mod when ability changes between levels. Re-calcs on Rest now.
* Fixed formula for calculating SPD so it uses class base for that level + ability adjustment.
* Added spells known per level for Oracle (don't know why this was blank)


The recalc of SPD on rest was needed for ability adjustments between levels, but this means if you usually have an abnormal number of spell per day from the class and you change it under local cheating, then you're going not want to do that because of the reset on rest. You will want to make new class and adjust the SPD there. Pain in the behind either way. I may, if there is demand put in an SPD modifier that you can edit. I know there are some classes where you get -1 SPD each level.
Attachments
Lib_spells3 9-1-15.rptok
(548.45 KiB) Downloaded 84 times

User avatar
lmarkus001
Great Wyrm
Posts: 1862
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (8-15-15)

Post by lmarkus001 »

aliasmask wrote:More updates to lib:spells3

(9-1-15)
...
* Added spells known per level for Oracle (don't know why this was blank)

...


So you did not make your changes off of my updated token where there are a lot of class and list updates. Any list of macros modified for your token? That way I can just apply those and avoid another data massage...

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (9-1-15)

Post by aliasmask »

I did actually use your token. After I did a quick code comparison to make sure we were matched up, I then started using your token as a base for modding. You'll notice the druid_storm in class list which is something I didn't have before. I just remade the oracle by making a new one, using the cleric list and setting the spd and spell known to sorcerer.

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (9-1-15)

Post by aliasmask »

Let's see I updated:
* Rest Start
* Ability Adjustments
* Tweak Token
* Restore Template

I think that's it.

User avatar
lmarkus001
Great Wyrm
Posts: 1862
Joined: Sat Mar 29, 2008 12:30 am
Location: Layfayette Hill, PA

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (9-1-15)

Post by lmarkus001 »

Yep I spoke too soon, lookin good!

User avatar
aliasmask
Deity
Posts: 8578
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW (9-1-15)

Post by aliasmask »

Update to pfStatBlockImport

AM (v2.0.5): Fixed Speed when "Spd" is used. Puts blank for XP when not in stat block instead of error. Fixed init/senses line error. (updated)

UPDATED CODE to v2.0.6
Lib:libDnD35Pathfinder wrote:
||| pfStatBlockImport |||

Code: Select all

@@ @pfStatBlockImport
<!-- Statblock2Token v2.0.4 (3-15-15)
   Statblock2Token v2.0.4 (10-29-15)
   Statblock2Token v2.0.6 (02-13-16)
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
LGM: Corrected vulnerability, Added Feats
AM (v2.0.3): fixed hp each; name get; default sight off; valid ulnerability check; made UDF; output change 
AM (v2.0.4): fixed stat sheet input error (infinite loop on mouse over)
AM (v2.0.5): Fixed Speed when "Spd" is used. Puts blank for XP when not in stat block instead of error. Fixed init/senses line error.
AM (v2.0.6): Fixed AC notes when () isnt present. 
   Fixed feats so it sets the states. Problem was that sysVars didnt have SysFeats.
   Put Special Abilities in to json for later use and to put line spaces between abilities.
-->
[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 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: TAB = decode("%09")]
[H: EOL = decode("%0A")]
[H: gmNotes = "[]"]

<!-- 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, "Ecolo gy", "Ecology")]
[H: statblock = replace(statblock, "  ", " ")]

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

<!-- Start formatting imported statblock and set it to GM notes -->

<!-- If Ecology section comes before Special Abilities, cut it out and put it last. If the Ecology section doesn't exist, create a blank one last -->
[H: EcologyThenSAbSearch = strfind(statblock, "(?i)(ECOLOGY)(.*?)(Special Abilities)")]
[H, IF(0<getFindCount(EcologythenSAbSearch)), CODE: {
	[IF(getGroup(EcologyThenSAbSearch, 1, 3) == "special abilities" || getGroup(EcologyThenSAbSearch, 1, 3) == "Special Abilities" || getGroup(EcologyThenSAbSearch, 1, 3) == "Special abilities" || getGroup(EcologyThenSAbSearch, 1, 3) == "SPECIAL ABILITIES"), CODE: {
		[EcologyString = getGroup(EcologyThenSAbSearch, 1, 0)]
		[statblock = replace(statblock, "(?i)((ECOLOGY)(.*?))((Special Abilities)(.*))", "\$4  \$1" )]
		[EcologySearch = strfind(statblock, "(?i)(ECOLOGY)(.*)")]
	};{
	}]
};{
	[EcologySearch = strfind(statblock, "(?i)(ECOLOGY)(.*)")]
	[IF(0<getFindCount(EcologySearch)), CODE: {
		[statblock = statblock]
	};{
		[statblock = statblock + " ECOLOGY "]
		[EcologySearch = strfind(statblock, "(?i)(ECOLOGY)(.*)")]
	}]
}]

<!-- Search for lines and sections of statblock individually -->
[H: NameSearch = strfind(statblock, "(.*?)CR")]
[H: assert(getFindCount(NameSearch),"CR could not be found",0)]
[H: CRSearch = strfind(statblock, "(CR ?[0-9]+(/[0-9]+)?)")]
[H: XPSearch = strfind(statblock, "(XP [0-9,]+)")]
[H: GenderEtcSearch = strfind(statblock, "(?i)XP [0-9,]+ ([a-z ,0-9]*)( LG | LN | LE | NG | N | NE | CG | CN | CE )")]
[H: AlignmentSizeRaceSearch = strfind(statblock, "(?i)(( LG | LN | LE | NG | N | NE | CG | CN | CE ).*?)Init")]
[H: InitSensesSearch = strfind(statblock, "(?i)(Init.*?) +(Aura|DEFENSE|Weakness)")]
[H: AuraSearch = strfind(statblock, "(?i)(Aura.*?) DEFENSE")]
[H: ACStringSearch = strfind(statblock, "(?i)(AC [0-9].*?) +HP")]
[H: HPStringSearch = strfind(statblock, "(?i)((HP) .*?)Fort ")]
[H: SavesSearch = strfind(statblock, "(?i)(Fort (.*?))(Defensive Abilit|DR [0-9]|Immune|Resist|SR|Weakness|OFFENSE)")]
[H: DefensiveAbilitiesSearch = strfind(statblock, "(?i)((Defensive Abilities|Defensive Ability|DR |Immune|Resist|SR)(.*?))(Weakness|OFFENSE)")]
[H: WeaknessesSearch = strfind(statblock, "(?i)(Weaknesse?s?(.*?))(DEFENSE|OFFENSE)")]
[H: SpeedSearch = strfind(statblock, "(?i)((Speed|Spd) (.+?))(Melee|Ranged|Tactic|Space|Reach|Special|Ranged|Stat|Spell)")]
[H: MeleeSearch = strfind(statblock, "(?i)(Melee (.+?))(Tactic|Space|Reach|Special|Ranged|Stat|Spell)")]
[H: RangedSearch = strfind(statblock, "(?i)(Ranged (.+?))(Tactic|Space|Reach|Special|Stat|Spell)")]
[H: SpaceReachSearch = strfind(statblock, "(?i)((Space|Reach)(.*?))(Tactic|Special|Stat|Spell)")]
[H: SpecialAttackSearch = strfind(statblock, "(?i)(Special Attacks?(.*?))(Tactic|Stat|Spell)")]
[H: SpellLikeSearch = strfind(statblock,  "(?i)((Arcane School |Gnome |Domain |Bloodline |Hellknight |Transmuter |Paladin |Cleric |Conjurer |Enchanter |Evoker |Necromancer |Arcane )?Spell-Like Abilities (.*?))(?=(Alchemist|Antipaladin|Bard|Cleric|Oracle|Druid|Inquisitor|Magus|Paladin|Ranger|Sorcerer|Wizard|Summoner|Witch|Arcane School|Gnome|Domain|Bloodline|Hellknight|Arcane|Abjurer|Conjurer|Diviner|Enchanter|Evoker|Illusionist|Necromancer|Transmuter|Tactic|Face|Stat|Spell))")]
[H: SpellsKnownSearch = strfind(statblock, "(?i)((Alchemist|Antipaladin|Bard|Cleric|Oracle|Druid|Inquisitor|Magus|Paladin|Ranger|Sorcerer|Wizard|Summoner|Witch|Abjurer|Conjurer|Diviner|Enchanter|Evoker|Illusionist|Necromancer|Transmuter)? ?Spells Known(.*?))(?=(Alchemist|Antipaladin|Bard|Cleric|Oracle|Druid|Inquisitor|Magus|Paladin|Ranger|Sorcerer|Wizard|Summoner|Witch|Abjurer|Conjurer|Diviner|Enchanter|Evoker|Illusionist|Necromancer|Transmuter|Tactic|Stat|Spells))")]
[H: SpellsPreparedSearch = strfind(statblock, "(?i)((Alchemist|Antipaladin|Bard|Cleric|Oracle|Druid|Inquisitor|Magus|Paladin|Ranger|Sorcerer|Wizard|Summoner|Witch|Abjurer|Conjurer|Diviner|Enchanter|Evoker|Illusionist|Necromancer|Transmuter)? ?Spells Prepared(.*?))(?=(Alchemist|Antipaladin|Bard|Cleric|Oracle|Druid|Inquisitor|Magus|Paladin|Ranger|Sorcerer|Wizard|Summoner|Witch|Abjurer|Conjurer|Diviner|Enchanter|Evoker|Illusionist|Necromancer|Transmuter|Tactic|Stat|Spells))")]
[H: TacticsSearch = strfind(statblock, "(?i)TACTICS(.*?)(?<!base )(?:STATISTICS)")]
[H: StatisticsSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?(Str (.*?))Base Atk")]
[H: BaseAtkSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?(Base Atk(.*?))(Feats|Skills|Languages|SQ|Combat Gear|Other Gear|Gear|SPECIAL|ECOLOGY)")]
[H: FeatsSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?(Feats (.*?))(Skills|Languages|SQ|Combat Gear|Other Gear|Gear|SPECIAL|ECOLOGY)")]
[H: SkillsSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?(Skills (.*?))(Languages|SQ|Combat Gear|Other Gear|Gear|SPECIAL|ECOLOGY)")]
[H: LanguagesSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?(Languages? (.*?))(SQ|Combat Gear|Other Gear|Gear|SPECIAL|ECOLOGY)")]
[H: SQSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?(SQ (.*?))(Combat Gear|Other Gear|Gear|SPECIAL|ECOLOGY)")]
[H: GearSearch = strfind(statblock, "(?i)(base statistics)?.*?Statistics.*?((Combat )?Gear (.*?))(BASE STATISTICS|SPECIAL|ECOLOGY) ")]
[H: BaseStatisticsSearch = strfind(statblock, "(?i)(Base Statistics.*?)(STATISTICS.*?Str [0-9]|SPECIAL ABILITIES|ECOLOGY)")]
[H: SpecialAbilitiesSearch = strfind(statblock, "(?i)(SPECIAL ABILITIES)(.*?)(ECOLOGY)")]

<!-- If the line or section exists, put it in a variable and give it a line break before it -->
[H, IF(0<getFindCount(GenderEtcSearch)), CODE: {
	[GenderEtcNote = "
" + trim(getGroup(GenderEtcSearch, 1, 1))]
};{
	[GenderEtcNote = ""]
}]

[H, IF(0<getFindCount(AuraSearch)), CODE: {
	[AuraNote = "
" + trim(getGroup(AuraSearch, 1, 1))]
};{
	[AuraNote = ""]
}]

[H, IF(0<getFindCount(DefensiveAbilitiesSearch)), CODE: {
	[DANote = "
" + trim(getGroup(DefensiveAbilitiesSearch, 1, 1))]
};{
	[DANote = ""]
}]

[H, IF(0<getFindCount(WeaknessesSearch)), CODE: {
	[WeaknessNote = "
" + trim(getGroup(WeaknessesSearch, 1, 1))]
};{
	[WeaknessNote = ""]
}]

[H, IF(0<getFindCount(MeleeSearch)), CODE: {
	[MeleeNote = "
" + trim(getGroup(MeleeSearch, 1, 1))]
};{
	[MeleeNote = ""]
}]

[H, IF(0<getFindCount(RangedSearch)), CODE: {
	[RangedNote = "
" + trim(getGroup(RangedSearch, 1, 1))]
};{
	[RangedNote = ""]
}]

[H, IF(0<getFindCount(SpaceReachSearch)), CODE: {
	[SpaceReachNote = "
" + trim(getGroup(SpaceReachSearch, 1, 1))]
};{
	[SpaceReachNote = ""]
}]

[H, IF(0<getFindCount(SpecialAttackSearch)), CODE: {
	[SANote = "
" + trim(getGroup(SpecialAttackSearch, 1, 1))]
};{
	[SANote = ""]
}]

<!-- If a Spell-Like Abilities section exists, break it up into subsections based on constant/at will/or # per day. Multiple Spell-Like sections may exist from different sources, so create a section for each -->
[H: SLNote = ""]
[H, IF(0<getFindCount(SpellLikeSearch)), FOR(NumSpellLikeTypes, 1, getFindCount(SpellLikeSearch)+1), CODE: {
	[SpellLikeString = getGroup(SpellLikeSearch, NumSpellLikeTypes, 1)]
	[SpellLikeHeaderSearch = strfind(SpellLikeString, "(?i)(.*?)(Constant|At[ -]Will|[0-9]+/day)")]
	[ConstantSearch = strfind(SpellLikeString, "(?i)((Constant)(.*?))(At[ -]Will|[0-9]+/day)")]
	[IF(1>getFindCount(ConstantSearch)): ConstantSearch = strfind(SpellLikeString, "(?i)((Constant)(.*))")]
	[AtWillSearch = strfind(SpellLikeString, "(?i)((At[ -]Will)(.*?))([0-9]+/day)")]
	[IF(1>getFindCount(AtWillSearch)): AtWillSearch = strfind(SpellLikeString, "(?i)((At[ -]Will)(.*))")]
	[PerDayStringSearch = strfind(SpellLikeString, "(?i)(([0-9]+/day)(.*))")]
	[IF(0<getFindCount(PerDayStringSearch)): PerDayString = getGroup(PerDayStringSearch, 1, 1); PerDayString = ""]
	[PerDayTypesSearch = strfind(PerDayString, "(?i)(([0-9]+/day)(.*?))(?=([0-9]+/day))")]
	[PerDayAbilities = ""]
	[IF(0<getFindCount(PerDayTypesSearch)), FOR(DifferentPerDays, 1, getFindCount(PerDayTypesSearch)+1), CODE: {
		[PerDayString = replace(PerDayString, "(?i)([0-9]+/day(.*?))(?=([0-9]+/day))", "")]
		[ThisPerDay = trim(getGroup(PerDayTypesSearch, DifferentPerDays, 1))]
		[PerDayAbilities = PerDayAbilities + "
	" + ThisPerDay]
	}]
	[IF(PerDayString != ""): PerDayAbilities = PerDayAbilities + "
	" + trim(PerDayString)]
	[IF(0<getFindCount(SpellLikeHeaderSearch)): SpellLikeHeader = "

" + trim(getGroup(SpellLikeHeaderSearch, 1, 1)); SpellLikeHeader = "" ]
	[IF(0<getFindCount(ConstantSearch)), CODE: {
		[ConstantAbilities = "
	" + trim(getGroup(ConstantSearch, 1, 1))]
	};{
		[ConstantAbilities = ""]
	}]
	[IF(0<getFindCount(AtWillSearch)), CODE: {
		[AtWillAbilities = "
	" + trim(getGroup(AtWillSearch, 1, 1))]
	};{
		[AtWillAbilities = ""]
	}]
	[SLNote = SLNote + SpellLikeHeader + ConstantAbilities + AtWillAbilities + PerDayAbilities]
};{
	[SLNote = ""]
}]

<!-- If a Spells Known section exists, break it up into subsections based on spell level. Multiple Spells Known sections may exist from different sources, so create a section for each -->
[SKNote = ""]
[H, IF(0<getFindCount(SpellsKnownSearch)), FOR(NumSpellClasses, 1, getFindCount(SpellsKnownSearch)+1), CODE: {
	[ThisSpellClass = getGroup(SpellsKnownSearch, NumSpellClasses, 1)]
	[SpellsKnownHeaderSearch = strfind(ThisSpellClass, "(?i)(.*?\\)) +(9th|8th|7th|6th|5th|4th|3rd|2nd|1st|0 \\(|0th \\()")]
	[NinthLevelSearch = strfind(ThisSpellClass, "(?i)((9th)(.*?))(8th)")]
	[EighthLevelSearch = strfind(ThisSpellClass, "(?i)((8th)(.*?))(7th)")]
	[SeventhLevelSearch = strfind(ThisSpellClass, "(?i)((7th)(.*?))(6th)")]
	[SixthLevelSearch = strfind(ThisSpellClass, "(?i)((6th)(.*?))(5th)")]
	[FifthLevelSearch = strfind(ThisSpellClass, "(?i)((5th)(.*?))(4th)")]
	[FourthLevelSearch = strfind(ThisSpellClass, "(?i)((4th)(.*?))(3rd)")]
	[ThirdLevelSearch = strfind(ThisSpellClass, "(?i)((3rd)(.*?))(2nd)")]
	[SecondLevelSearch = strfind(ThisSpellClass, "(?i)((2nd)(.*?))(1st)")]
	[FirstLevelSearch = strfind(ThisSpellClass, "(?i)((1st)(.*?))(0 \\(|0th \\()")]
	[ThisClassSpecialSearch = strfind(ThisSpellClass, "(?i)((D Domain|Bloodline|Domain|Mystery|Opposition School|Prohibited School)(.*))")]
	[IF(0<getFindCount(ThisClassSpecialSearch)): ZerothLevelSearch = strfind(ThisSpellClass, "(?i)((0 \\(|0th \\()(.*?))(D Domain|Bloodline|Domain|Mystery|Opposition School|Prohibited School)"); ZerothLevelSearch = strfind(ThisSpellClass, "((0 \\(|0th \\()(.*))")]
	[IF(0<getFindCount(SpellsKnownHeaderSearch)): SpellsKnownHeader = "

" + trim(getGroup(SpellsKnownHeaderSearch, 1, 1)); SpellsKnownHeader = ""]
	[IF(0<getFindCount(NinthLevelSearch)), CODE: {
		[NinthLevel = "
	" + trim(getGroup(NinthLevelSearch, 1, 1))]
	};{
		[NinthLevel = ""]
	}]
	[IF(0<getFindCount(EighthLevelSearch)), CODE: {
		[EighthLevel = "
	" + trim(getGroup(EighthLevelSearch, 1, 1))]
	};{
		[EighthLevel = ""]
	}]
	[IF(0<getFindCount(SeventhLevelSearch)), CODE: {
		[SeventhLevel = "
	" + trim(getGroup(SeventhLevelSearch, 1, 1))]
	};{
		[SeventhLevel = ""]
	}]
	[IF(0<getFindCount(SixthLevelSearch)), CODE: {
		[SixthLevel = "
	" + trim(getGroup(SixthLevelSearch, 1, 1))]
	};{
		[SixthLevel = ""]
	}]
	[IF(0<getFindCount(FifthLevelSearch)), CODE: {
		[FifthLevel = "
	" + trim(getGroup(FifthLevelSearch, 1, 1))]
	};{
		[FifthLevel = ""]
	}]
	[IF(0<getFindCount(FourthLevelSearch)), CODE: {
		[FourthLevel = "
	" + trim(getGroup(FourthLevelSearch, 1, 1))]
	};{
		[FourthLevel = ""]
	}]
	[IF(0<getFindCount(ThirdLevelSearch)), CODE: {
		[ThirdLevel = "
	" + trim(getGroup(ThirdLevelSearch, 1, 1))]
	};{
		[ThirdLevel = ""]
	}]
	[IF(0<getFindCount(SecondLevelSearch)), CODE: {
		[SecondLevel = "
	" + trim(getGroup(SecondLevelSearch, 1, 1))]
	};{
		[SecondLevel = ""]
	}]
	[IF(0<getFindCount(FirstLevelSearch)), CODE: {
		[FirstLevel = "
	" + trim(getGroup(FirstLevelSearch, 1, 1))]
	};{
		[FirstLevel = ""]
	}]
	[IF(0<getFindCount(ZerothLevelSearch)), CODE: {
		[ZerothLevel = "
	" + trim(getGroup(ZerothLevelSearch, 1, 1))]
	};{
		[ZerothLevel = ""]
	}]
	[IF(0<getFindCount(ThisClassSpecialSearch)), CODE: {
		[ThisClassSpecial = "
	" + trim(getGroup(ThisClassSpecialSearch, 1, 1))]
	};{
		[ThisClassSpecial = ""]
	}]
	[SKNote = SKNote + SpellsKnownHeader + NinthLevel + EighthLevel + SeventhLevel + SixthLevel + FifthLevel + FourthLevel + ThirdLevel + SecondLevel + FirstLevel + ZerothLevel + ThisClassSpecial]
};{
	[SKNote = ""]
}]

<!-- If a Spells Prepared section exists, break it up into subsections based on spell level. Multiple Spells Prepared sections may exist from different sources, so create a section for each -->
[H: SPNote = ""]
[H, IF(0<getFindCount(SpellsPreparedSearch)), FOR(NumSpellClasses, 1, getFindCount(SpellsPreparedSearch)+1), CODE: {
	[ThisSpellClass = getGroup(SpellsPreparedSearch, NumSpellClasses, 1)]
	[SpellsPreparedHeaderSearch = strfind(ThisSpellClass, "(?i)(.*?\\)) +(9th|8th|7th|6th|5th|4th|3rd|2nd|1st|0 \\(|0th \\))")]
	[NinthLevelSearch = strfind(ThisSpellClass, "(?i)((9th)(.*?))(8th)")]
	[EighthLevelSearch = strfind(ThisSpellClass, "(?i)((8th)(.*?))(7th)")]
	[SeventhLevelSearch = strfind(ThisSpellClass, "(?i)((7th)(.*?))(6th)")]
	[SixthLevelSearch = strfind(ThisSpellClass, "(?i)((6th)(.*?))(5th)")]
	[FifthLevelSearch = strfind(ThisSpellClass, "(?i)((5th)(.*?))(4th)")]
	[FourthLevelSearch = strfind(ThisSpellClass, "(?i)((4th)(.*?))(3rd)")]
	[ThirdLevelSearch = strfind(ThisSpellClass, "(?i)((3rd)(.*?))(2nd)")]
	[SecondLevelSearch = strfind(ThisSpellClass, "(?i)((2nd)(.*?))(1st)")]
	[FirstLevelSearch = strfind(ThisSpellClass, "(?i)((1st)(.*?))(0 \\(|0th \\()")]
	[ThisClassSpecialSearch = strfind(ThisSpellClass, "(?i)((D Domain|Bloodline|Domain|Mystery|Opposition School|Prohibited School)(.*))")]
	[IF(0<getFindCount(ThisClassSpecialSearch)): ZerothLevelSearch = strfind(ThisSpellClass, "(?i)((0 \\(|0th \\()(.*?))(D Domain|Bloodline|Domain|Mystery|Opposition School|Prohibited School)"); ZerothLevelSearch = strfind(ThisSpellClass, "((0 \\(|0th \\()(.*))")]
	[IF(0<getFindCount(SpellsPreparedHeaderSearch)): SpellsPreparedHeader = "

" + trim(getGroup(SpellsPreparedHeaderSearch, 1, 1)); SpellsPreparedHeader = ""]
	[IF(0<getFindCount(NinthLevelSearch)), CODE: {
		[NinthLevel = "
	" + trim(getGroup(NinthLevelSearch, 1, 1))]
	};{
		[NinthLevel = ""]
	}]
	[IF(0<getFindCount(EighthLevelSearch)), CODE: {
		[EighthLevel = "
	" + trim(getGroup(EighthLevelSearch, 1, 1))]
	};{
		[EighthLevel = ""]
	}]
	[IF(0<getFindCount(SeventhLevelSearch)), CODE: {
		[SeventhLevel = "
	" + trim(getGroup(SeventhLevelSearch, 1, 1))]
	};{
		[SeventhLevel = ""]
	}]
	[IF(0<getFindCount(SixthLevelSearch)), CODE: {
		[SixthLevel = "
	" + trim(getGroup(SixthLevelSearch, 1, 1))]
	};{
		[SixthLevel = ""]
	}]
	[IF(0<getFindCount(FifthLevelSearch)), CODE: {
		[FifthLevel = "
	" + trim(getGroup(FifthLevelSearch, 1, 1))]
	};{
		[FifthLevel = ""]
	}]
	[IF(0<getFindCount(FourthLevelSearch)), CODE: {
		[FourthLevel = "
	" + trim(getGroup(FourthLevelSearch, 1, 1))]
	};{
		[FourthLevel = ""]
	}]
	[IF(0<getFindCount(ThirdLevelSearch)), CODE: {
		[ThirdLevel = "
	" + trim(getGroup(ThirdLevelSearch, 1, 1))]
	};{
		[ThirdLevel = ""]
	}]
	[IF(0<getFindCount(SecondLevelSearch)), CODE: {
		[SecondLevel = "
	" + trim(getGroup(SecondLevelSearch, 1, 1))]
	};{
		[SecondLevel = ""]
	}]
	[IF(0<getFindCount(FirstLevelSearch)), CODE: {
		[FirstLevel = "
	" + trim(getGroup(FirstLevelSearch, 1, 1))]
	};{
		[FirstLevel = ""]
	}]
	[IF(0<getFindCount(ZerothLevelSearch)), CODE: {
		[ZerothLevel = "
	" + trim(getGroup(ZerothLevelSearch, 1, 1))]
	};{
		[ZerothLevel = ""]
	}]
	[IF(0<getFindCount(ThisClassSpecialSearch)), CODE: {
		[ThisClassSpecial = "
	" + trim(getGroup(ThisClassSpecialSearch, 1, 1))]
	};{
		[ThisClassSpecial = ""]
	}]
	[SPNote = SPNote + SpellsPreparedHeader + NinthLevel + EighthLevel + SeventhLevel + SixthLevel + FifthLevel + FourthLevel + ThirdLevel + SecondLevel + FirstLevel + ZerothLevel + ThisClassSpecial]
};{
	[SPNote = ""]
}]

<!-- If a tactics section exists, break it up into subsections of Before Combat, During Combat, and Morale. The Base Statistics section, if it exists, may be in Tactics or it may be at the end of the Statistics section. In either case, put it in Tactics -->
[H, IF(0<getFindCount(TacticsSearch)), CODE: {
	[TacticsNote = "

-----TACTICS-----"]
	[TacticsString = getGroup(TacticsSearch, 1, 1)]
	[TacticsBeforeCombatSearch = strfind(TacticsString, "(?i)(Before Combat.*?)(During Combat|Morale|Base Statistics)")]
	[IF(1>getFindCount(TacticsBeforeCombatSearch)): TacticsBeforeCombatSearch = strfind(TacticsString, "(?i)(Before Combat.*)")]
	[IF(0<getFindCount(TacticsBeforeCombatSearch)): TacticsNote = TacticsNote + "
" + trim(getGroup(TacticsBeforeCombatSearch, 1, 1))]
	[TacticsDuringCombatSearch = strfind(TacticsString, "(?i)(During Combat.*?)(Morale|Base Statistics)")]
	[IF(1>getFindCount(TacticsDuringCombatSearch)): TacticsDuringCombatSearch = strfind(TacticsString, "(?i)(During Combat.*)")]
	[IF(0<getFindCount(TacticsDuringCombatSearch)): TacticsNote = TacticsNote + "
" + trim(getGroup(TacticsDuringCombatSearch, 1, 1))]
	[TacticsMoraleSearch = strfind(TacticsString, "(?i)(Morale.*?)(Base Statistics)")]
	[IF(1>getFindCount(TacticsMoraleSearch)): TacticsMoraleSearch = strfind(TacticsString, "(?i)(Morale.*)")]
	[IF(0<getFindCount(TacticsMoraleSearch)): TacticsNote = TacticsNote + "
" + trim(getGroup(TacticsMoraleSearch, 1, 1))]
};{
	[TacticsNote = ""]
}]
[H, IF(0<getFindCount(BaseStatisticsSearch)), CODE: {
	[IF(TacticsNote == ""), CODE: {
		[TacticsNote = "

-----TACTICS-----
" + trim(getGroup(BaseStatisticsSearch, 1, 1)))]
	};{
		[TacticsNote = TacticsNote + "
" + trim(getGroup(BaseStatisticsSearch, 1, 1)))]
	}]
}]

[H, IF(0<getFindCount(FeatsSearch)), CODE: {
	[FeatsNote = "
" + trim(getGroup(FeatsSearch, 1, 2))]
};{
	[FeatsNote = ""]
}]

[H, IF(0<getFindCount(SkillsSearch)), CODE: {
	[SkillsNote = "
" + trim(getGroup(SkillsSearch, 1, 2))]
};{
	[SkillsNote = ""]
}]

[H, IF(0<getFindCount(LanguagesSearch)), CODE: {
	[LangNote = "
" + trim(getGroup(LanguagesSearch, 1, 2))]
};{
	[LangNote = ""]
}]

[H, IF(0<getFindCount(SQSearch)), CODE: {
	[SQNote = "
" + trim(getGroup(SQSearch, 1, 2))]
};{
	[SQNote = ""]
}]

[H, IF(0<getFindCount(GearSearch)), CODE: {
	[GearNote = "
" + trim(getGroup(GearSearch, 1, 2))]
};{
	[GearNote = ""]
}]

<!-- AM Update - SPECIAL ABILITIES: format "name (ex|su|sp) text -->
[H, if(getFindCount(SpecialAbilitiesSearch)), code: {
   [H: SA.string = getGroup(SpecialAbilitiesSearch, 1, 2)]
   <!-- find the beginning of the special ability name in group 2 -->
   [H: SA.id = strfind(SA.string,"(?i)(^ *|[.] *)([^.]*? ([(](Ex|Su|Sp)[)]))")]
   [H: SA.count = getFindCount(SA.id)]
   [H: SA.strings = json.append("[]","-----SPECIAL ABILITIES-----")]
   [H, for(i,0,SA.count), code: {
      [H: start = getGroupStart(SA.id,i+1,2)]
      [H, if(i+1 < SA.count): end = getGroupStart(SA.id,i+2,2); end = ""]
      [H, if(json.isEmpty(end)): SA.strings = json.append(SA.strings,substring(SA.string,start)); SA.strings = json.append(SA.strings,substring(SA.string,start,end))]
   }]
   [H: SAbNote = EOL+EOL+json.toList(SA.strings,EOL+EOL)]
};{
   [H: SAbNote = ""]
}]

<!-- If the Ecology section exists, break it up into lines for Environment, Organization, and Treasure -->
[H, IF(0<getFindCount(EcologySearch)), CODE: {
	[EcologyNote = "

-----ECOLOGY-----"]
	[EcologyString = trim(getGroup(EcologySearch, 1, 2))]
	[EnvironmentSearch = strfind(EcologyString, "(?i)(Environment.*?)(Organization|Treasure)")]
	[IF(0<getFindCount(EnvironmentSearch)): EcologyNote = EcologyNote + "
" + trim(getGroup(EnvironmentSearch, 1, 1))]
	[OrganizationSearch = strfind(EcologyString, "(?i)(Organization.*?)(Treasure)")]
	[IF(0<getFindCount(OrganizationSearch)): EcologyNote = EcologyNote + "
" + trim(getGroup(OrganizationSearch, 1, 1))]
	[TreasureSearch = strfind(EcologyString, "(?i)(Treasure.*)")]
	[IF(0<getFindCount(TreasureSearch)), CODE: {
		[TreasureTypeSearch = strfind(getGroup(TreasureSearch, 1, 1), "(none|standard|double|incidental|NPC gear)")]
		[IF(0<getFindCount(TreasureTypeSearch)): TreasureType = getGroup(TreasureTypeSearch, 1, 1)]
	}]
	[IF(0<getFindCount(TreasureSearch)): EcologyNote = EcologyNote + "
" + trim(getGroup(TreasureSearch, 1, 1))]
};{
	[EcologyNote = ""]
}]

<!--Put it all together in the GM notes -->

<!-- AM Update - may use this later to redo gm notes. Needed for debugging. -->
[H: note.name = trim(getGroup(NameSearch, 1, 1))]
[H: note.cr = trim(getGroup(CRSearch, 1, 1))]
[H: note.align = trim(getGroup(AlignmentSizeRaceSearch, 1, 1))]
[H: note.senses = trim(getGroup(InitSensesSearch, 1, 1))]
[H: note.ac = trim(getGroup(ACStringSearch, 1, 1))]
[H: note.hp = trim(getGroup(HPStringSearch, 1, 1))]
[H: note.saves = trim(getGroup(SavesSearch, 1, 1))]
[H: note.speed = trim(getGroup(SpeedSearch, 1, 2))]
[H: note.stats = trim(getGroup(StatisticsSearch, 1, 2))]
[H: note.bab = trim(getGroup(BaseAtkSearch, 1, 2))]

<!-- This is actual fix for XP not being in statblock. It simply puts a blank -->
[H, if(getFindCount(XPSearch)): note.xp = trim(getGroup(XPSearch, 1, 1)); note.xp = ""]

[H: setGMNotes(trim(getGroup(NameSearch, 1, 1)) + "	" + trim(getGroup(CRSearch, 1, 1)) + "
------------
" + note.xp +
 GenderEtcNote + "
" + trim(getGroup(AlignmentSizeRaceSearch, 1, 1)) + "
" + trim(getGroup(InitSensesSearch, 1, 1)) + 
 AuraNote + "

-----DEFENSE-----
" + trim(getGroup(ACStringSearch, 1, 1)) + "
" + trim(getGroup(HPStringSearch, 1, 1)) + "
" + trim(getGroup(SavesSearch, 1, 1)) + 
 DANote + 
 WeaknessNote + "

-----OFFENSE-----
" + trim(getGroup(SpeedSearch, 1, 2)) +
 MeleeNote +
 RangedNote +
 SpaceReachNote +
 SANote +
 SLNote +
 SKNote +
 SPNote +
 TacticsNote + "

-----STATISTICS-----
" + trim(getGroup(StatisticsSearch, 1, 2)) + "
" + trim(getGroup(BaseAtkSearch, 1, 2)) + 
 FeatsNote +
 SkillsNote +
 LangNote +
 SQNote +
 GearNote +
 SAbNote +
 EcologyNote)]

[H: output="Token Updated:<br>"]
[H: id = strfind(statblock, "^(.+?)CR ?[0-9]")]
[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 ?)([0-9]+)(/[1-9]+)?")]
[H, IF(0<getFindCount(id)), CODE: {
	[ECL = trim(getGroup(id, 1, 0))]
	[output=output+"CR: "+ECL+", "]
	[IF(ECL == "1/3"): ECL=".338"]
	[IF(ECL == "1/6"): 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(0)]
	[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+", "]
}]

 <!-- Defensive Abilities Sample: Defensive Abilities incorporeal, channel resistance +2; -->
[H: DefensiveAbil = ""]
[H, IF(0<getFindCount(DefensiveAbilitiesSearch)),CODE: {
	[DefensiveAbilitiesString=getGroup(DefensiveAbilitiesSearch, 1, 1)]
	[DefensiveAloneSearch=strfind(DefensiveAbilitiesString, "(?i)Defensive (Abilities|Ability)(.*?); +(SR|Immune|Resist|DR)")]
	[IF(0==getFindCount(DefensiveAloneSearch)): DefensiveAloneSearch = strfind(DefensiveAbilitiesString, "(?i)Defensive (Ability|Abilities)(.*)")]
	[IF(0<getFindCount(DefensiveAloneSearch)), CODE: {
		[DefensiveAbil = trim(getGroup(DefensiveAloneSearch, 1, 2))]
		[output=output+"Defensive Abilities: "+DefensiveAbil+", "]
	}]
}]

<!-- Find Immunities. Sample: Immune acid, curse effects, flanking, mind-affecting effects, paralysis, poison, sleep -->
[H: Immune = ""]
[H: id = strfind(statblock, "(Immune )(([a-zA-Z]+[-]?,? ?)+);? +(Weakness|OFFENSE|Resist|SR)")]
[H, IF(0<getFindCount(id)),CODE: {
	[id2 = strfind(getGroup(id, 1, 2), "(.*) (Weakness|OFFENSE|[;]|Resist)")]
	[IF(0<getFindCount(id2)): Immune = getGroup(id2, 1, 1); Immune = getGroup(id, 1, 2)]
	[Immune = trim(Immune)]
	[output=output+"Immunities: "+Immune+", "]
 }]

<!-- Find Weaknesses. Sample: Weaknesses light sensitivity -->
[H: Weaknesses = ""]
[H: id = strfind(statblock, "(Weaknesse?s? )((\\w+-?,? ?)+) +(?i)(OFFENSE|DEFENSE)")]
[H, IF(0<getFindCount(id)),CODE: {
	[Weaknesses = trim(getGroup(id, 1, 2))]
	[output=output+"Weaknesses: "+Weaknesses+", "]
}]
  
<!-- Find DR. Sample: 1/-, 2/adamantine, 3/good -->
[h: DRSearch = ""]
[H: id = strfind(statblock, "DR(.?[0-9]+)/(.+?)[,;O]")]
[H, IF(0<getFindCount(id)), COUNT(getFindCount(id)), CODE: {
	[thisrollcount = roll.count+1]
	[thissearchDR = replace(getGroup(id, thisrollcount, 2), "Cold Iron", "ColdIron")]
	[drANDORsearch = strfind(thissearchDR, "(and|or) ([a-zA-Z]+ ?)")]
	[if(0<getFindCount(drANDORsearch)), COUNT(getFindCount(drANDORsearch)), CODE: {
	[DRSearch = trim(DRSearch + " " + getGroup(id, thisrollcount, 1)+"/"+getGroup(drANDORsearch, roll.count+1, 2))]
	}]
	[DRSearch = trim(DRSearch + " " + getGroup(id, thisrollcount, 1)+"/"+thissearchDR)]
	[DRSearch = replace(DRSearch, "/-", "/All")]
	[DRSearch = replace(DRSearch, "(?i)/Cold Iron", "/ColdIron")]
	[DRSearch = replace(DRSearch, "(?i)/Chaotic", "/Chaos")]
	[DRSearch = replace(DRSearch, "(?i)/Lawful", "/Law")]
}]
  
<!-- Find Resistences. Sample: Resist acid 5, cold 5, electricity 5 -->
[h: resistSearch = ""]
[H: id = strfind(statblock, "(\\bResist \\b(\\w+ \\d+,? ?)+)")]
[H, IF(0<getFindCount(id)),CODE: {
	[resistSearch = trim(getGroup(id, 1, 1))]
}]

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

<!-- Put DR & Resist values into JSON Property DRER -->
[H, FOREACH( reduceTypes, json.fields( DRER ) ), CODE: {
 	[jRC = json.get( DRER, reduceTypes ) ]
 	[prefixT = substring( reduceTypes, 0, 1 ) ]
   

 	[FOREACH( reduceType, json.fields( jRC ) ), CODE: {
    	[preValue = json.get( jRC, reduceType ) ]
		[reduceVal = 0]

		[resistFind = strfind(resistSearch, "(?i)("+reduceType+" )(\\d+)")]

		 [IF(0<getFindCount(resistFind) && prefixT == "E"): reduceVal = getGroup(resistFind, 1, 2)]

		[drFind = strfind(DRSearch, "(?i)(\\d+)/("+reduceType+")")]
		[IF(0<getFindCount(drFind) && prefixT == "D"): reduceVal = getGroup(drFind, 1, 1)]
  
		[ chgValue = eval( "" + prefixT + reduceType + "=" + reduceVal ) ]
	  
    	[IF( reduceType != "Note" ): tmodV = json.get( DRERMod, if( reduceTypes == "Damage Reduction", "DR_", if( reduceTypes == "Energy Resistance", "ER_", if( reduceTypes == "Spell Resistance", "SR_", "EV_") ) ) + reduceType ) ]
    	[IF( reduceType != "Note" ): tVal = max( chgValue, tmodV ); tVal = chgValue ]
     
		[IF( tVal != 0 && tVal != "" && prefixT == "D"): DR = listAppend( DR, tVal + if( reduceType == "Note", "", "/" + if( reduceType == "All", "-", reduceType ) ) ) ]
	  
		[IF( tVal != 0 && tVal != "" && prefixT == "E"): DR = listAppend( DR, reduceType + if( reduceType == "Note", "", " (" + if( tVal == "999", "Immune", tVal ) + ")" ) ) ]
    	[IF( reduceType == "Note" && chgValue == 0 ): chgValue = "" ]
    	[IF( preValue != chgValue ):  output = output + reduceType + "= " + chgValue + " (" + preValue + "), "]
    	[IF( preValue != chgValue ): jRC = json.set( jRC, reduceType, chgValue ) ]

}]
 	[setProperty( "DRER", json.set( DRER, reduceTypes, jRC ) ) ]
   
 	[IF(DRSearch != "" || resistSearch != ""): output=output+"<br>"]
}]

[H, If(Immune != ""), CODE:{
	[jRC = json.get( DRER, "Energy Resistance" )]
	[jRCnew = json.set( jRC, "Note", "Immune: "+Immune)]
	[newDRER = json.set( DRER, "Energy Resistance", JRCnew)]
	[setProperty("DRER", newDRER)]
	[DR = listAppend(DR, "Immune: "+Immune)]
}]

[H, If(SRSearch != ""), CODE:{
	[jRC = json.get( DRER, "Spell Resistance" )]
	[jRCnew = json.set( jRC, "SR", SRSearch)]
	[newDRER = json.set( DRER, "Spell Resistance", JRCnew)]
	[setProperty("DRER", newDRER)]
	[DR = listAppend(DR, "SR: "+SRSearch)]
}]

[H: vulnerability =  strfind(Weaknesses, "(Vulnerability|Vulnerable|vulnerability|vulnerable) to ([a-zA-Z]*)")]
[H: vtypes = getFindCount(vulnerability)]
[H, IF(0<vtypes), CODE: {
	[COUNT(vtypes), CODE: {
		[jRC = json.get(DRER, "Energy Vulnerability")]
      [ tvuln = "V."+upper(lower(getGroup(vulnerability,roll.count+1,2)),1)]
      [ validVulnerability = listContains("V.Acid,V.Cold,V.Electricity,V.Fire,V.Sonic",tvuln)]
		[if(validVulnerability): jRCnew = json.set(jRC, tvuln, 1)]
		[if(validVulnerability): newDRER = json.set(DRER, "Energy Vulnerability", jRCnew)]
		[if(validVulnerability): setProperty("DRER", newDRER)]
		[if(validVulnerability): DR = listAppend(DR, tvuln)]
	}]
}]

<!-- STATS BEGIN -->
[H: id = strfind(statblock, "(?i)(?<!base )STATISTICS.*?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>"]
}]
[H: SavesString = getGroup(SavesSearch, 1, 1)]
[H: id = strfind(SavesString, ";(.*)")]
[H, IF(0<getFindCount(id)), CODE: {
	[SaveMisc = trim(getGroup(id, 1, 1))]
	[output=output+"Saves Misc Notes: "+SaveMisc+"<br>"]
}]

<!-- SAVES END -->

[H: id = strfind(statblock, "hp ([0-9]+).*?\\(([0-9]+ ?HD; ?)?([0-9d+]*)")]
[H: hpString = getGroup(id, 1, 3)]
[H: numOfEachHDType = strfind(hpString, "([0-9]+)d")]
[H: numOfHDTypes = getFindCount(numOfEachHDType)]
[H, IF(0<getFindCount(id)),CODE: {
	[H: HD = 0]
	[H, COUNT(numOfHDTypes), CODE:
	{
		[HD = HD + getGroup(numOfEachHDType, (roll.count+1), 1)]
	}]
	[HP=getGroup(id, 1, 1)]
	[Level = max(1,HD)]
	[HPmax="[R: max(Level, ( "+(HP-(ConB*Level))+" + (ConB * Level) ) )]"]
	[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-natural]
	[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)]
	[Movement = spd]
}; {
	[Speed = json.set(Speed, "base",0)]
	[Movement = 0]
}]
[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)]
	[Movement = getProperty("Movement") + ", Climb:" + 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)]
	[Movement = getProperty("Movement") + ", 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)]
	[Movement = getProperty("Movement") + ", Burrow:" + spd]
}]
[H: id = strfind(statblock, "fly ([0-9]+) ft\\.? ?\\(?([a-zA-Z]+)?\\)? ?(Melee|Ranged|Special|Spell)")]
[H, IF(0<getFindCount(id)), CODE: {
	[spd=getGroup(id, 1, 1)]
	[output=output+", Fly: "+spd]
	[Speed = json.set(Speed, "fly",spd)]
	[flyman=lower(getGroup(id, 1, 2))]
	[SWITCH(flyman), CODE:
		case "": {
			[flyman = "average"]
			[Speed = json.set(Speed, "flymaneuver",4)]
		};
		case "clumsy": {
			[flyman = "Clumsy"]
			[Speed = json.set(Speed, "flymaneuver",2)]
		};
		case "poor": {
			[flyman = "Poor"]
			[Speed = json.set(Speed, "flymaneuver",3)]
		};
		case "average": {
			[flyman = "Average"]
			[Speed = json.set(Speed, "flymaneuver",4)]
		};
		case "good": {
			[flyman = "Good"]
			[Speed = json.set(Speed, "flymaneuver",5)]
		};
		case "perfect": {
			[flyman = "Perfect"]
			[Speed = json.set(Speed, "flymaneuver",6)]
		};
	]
	[Movement = getProperty("Movement") + ", Fly:" + spd + " (" + flyman + ")"]
}]
[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 -->

<!--Languages Begin-->
[H, IF(0<getFindCount(LanguagesSearch)), CODE: {
	[LanguageList = strfind(getGroup(LanguagesSearch, 1, 2), "Languages?(.*)")]
	[IF(0<getFindCount(LanguageList)): Race = json.set(Race, "notes", trim(getGroup(LanguageList, 1, 1)))]
}]
<!--Languages End-->

<!-- SQ on popup begin -->
[H, IF(0<getFindCount(SQSearch)), CODE: {
	[SpecialQualWithoutSQ = strfind(getGroup(SQSearch, 1, 2), "SQ (.*)")]
	[SpecialQual = trim(getGroup(SpecialQualWithoutSQ, 1, 1))]
}]
<!--SQ on popup end-->

<!-- FEATS BEGIN -->
[H: output=output+"Feats: "]
[H, IF(0<getFindCount(FeatsSearch)), CODE: {
   [ tFeats = replace(getGroup(FeatsSearch, 1, 2), "Feats ", "", 1) ]
   [ tFeats = trim(replace(tFeats, "B,", ",")) ]
   [ tFEflag = endsWith(tFeats, "B") ]
   [ IF(tFEflag): tFeats = substring(tFeats, 0, length(tFeats)-1); "" ]
   [H: output=output+tFeats ]
   [ tFeatsJ = json.fromList(tFeats) ]
   [ tFeatsJ2 = "[]" ]
   [ tFeatsJparam = "{}" ]
   [ Foreach(p, tFeatsJ), CODE: {
      [ tidxp = indexOf(p,"(") ]
      [ IF( tidxp < 0 ): tpm = trim(p); tpm = trim(substring(p,0,tidxp)) ]
      [ IF( tidxp < 0 ): tpsub = ""; tpsub = substring(p,tidxp+1,length(p)-1) ]
      [ tFeatsJ2 = json.append(tFeatsJ2, tpm) ]
      [ IF( tidxp < 0 ): "";  tFeatsJparam = json.set(tFeatsJparam, tpm, tpsub) ]
   }]
   [ tFeatsJ2 = json.sort(tFeatsJ2) ]
   [ setProperty("FeatsJ", tFeatsJ2) ]
   [ setProperty("FeatsJparam", tFeatsJparam) ]
     
   <!-- AM Update - table SysVars doesnt have sysFeats so basing feats off of Feat var. 
      Also looping through known feats rather than global feats.
      Setting value to one. This will add new feats not know as well.
   -->
   [H: sysFeats = json.fromStrProp(getProperty("Feats"))]
   [H, foreach(featName,tFeatsJ2): sysFeats = json.set(sysFeats,replace(trim(featName)," |-",""),1)]
   [H: setProperty("Feats",json.toStrProp(sysFeats))]
      
};{
}]
[H: output=output+"<br>"]
<!-- FEATS END -->

<!-- Ferocity for Orcs works just like Die Hard -->
[H: id = strfind(DefensiveAbil, "(?i)(ferocity)")]
[H, IF(0<getFindCount(id)), CODE: {
	[Feats=setStrProp(Feats, "DieHard",1)]
	[H: output=output+getGroup(id, 1, 1)+", "]
}]  
  
[H: output=output+"<br>"]

[H: id = strfind(statblock, "(?i)Special Attacks? (.*?)(Spells|Spell[ -]Like|Statistics|Tactics)")]
[H, IF(0<getFindCount(id)), CODE: {
	[SpecialATK = trim(getGroup(id, 1, 1))]
	[SpecialATK=substring(SpecialATK,0,min(length(SpecialATK),70))]
	[output=output+"SA: "+SpecialATK+"<br>"]
  };{
	[SpecialATK=""]
}]

[H: id = strfind(statblock, "(Fast Healing )([0-9]+)")]
[H, IF(0<getFindCount(id)),CODE: {
	[FastHealing=getGroup(id, 1, 2)]
	[output=output+"Fast Healing: "+FastHealing+"<br>"]
}]

<!-- Setting Regeneration and Anti Regen Damage Type if present -->
[H: id = strfind(statblock, "(Regeneration )([0-9]+) ([(][a-zA-Z]+[)])?")]
[H, IF(0<getFindCount(id)),CODE: {
	[Regeneration=getGroup(id, 1, 2)]
	[output=output+"Regeneration: "+Regeneration+"<br>"]
	[AntiRegen=if(getGroup(id, 1, 3)=="", "", getGroup(id, 1, 3))]
	[output=output+"Regeneration: "+Regeneration+"<br>"+if(AntiRegen=="", "", "Anti-Regen Damage Type: "+AntiRegen+"<br>")]
}]
  
<!-- ATTACKS BEGIN -->
<!-- Insert empty weapon strings -->
[H: wpnstr=getPropertyDefault( "Weapon0" )]
[H, c(17,""),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|Space|Reach|Special|Ranged|Spell-Like|Spells|Statistic)")]
[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=replace(atkstr, "((\\(.+\\)) and )", "\$2, ")]
	[atkstr=stringToList(atkstr, "(, )",":")]
	[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 (.+?)(?i)(Tactic|Space|Reach|Special|Spell-Like|Spells|Statistic)")]
[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+)(/[+-]\\d+)?( [a-zA-Z]* )?[/+\\-0-9\\s]*?\\((.+)\\)")]
	[IF(0<getFindCount(id)), CODE: {
	[WpnName = "Weapon" + wpnno]

    <!-- Quantity / Weapon Bonus-->
  	[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)+ "'")]
	
		<!-- Weapon Name -->
	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"Name", replace(wbonus, " ", "") + if(wbonus=="", "", " ") + getGroup(id, 1, 3) + if(getGroup(id, 1, 6)=="", "", " ("+trim(getGroup(id, 1, 6))+")")) + "'")]
    
	<!-- Manufactured ? -->
  	[id2 = strfind(atkstr, "(?i)(slam|bite|claw|gore|hoof|tentacle|wing|pincer|tail|sting|talon|Constrict)")]
  	[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-SizeM;watkbonus=watkbonus-BAB-StrB-SizeM]
  	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"AtkBonus", watkbonus)+ "'")]

	<!-- CritMult-->
  	[wdam=getGroup(id, 1, 7)]
  	[CritMult=2]
  	[id2 = strfind(wdam, "([0-9]+d[0-9]+)?([+-][0-9]+)?/?([0-9]+-?[0-9]+)?([xX]([0-9]))? ?([a-zA-Z0-9 ]+)?")]
  	[IF(getGroup(id2, 1, 5)==""): CritMult=2; CritMult=replace(getGroup(id2, 1, 5),"[xX]", "")]
  	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"CritMult", CritMult)+ "'")]

	<!-- CritRange-->
  	[CritRange=20]
  	[critFromWdam = getGroup(id2, 1, 3)]
	[critFromWdamsearch = strfind(critFromWdam, "([0-9]+)-20")]
	[IF(0<getFindCount(critFromWdamsearch)): CritRange = getGroup(critFromWdamsearch, 1, 1)]
  	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"CritRange", CritRange)+ "'")]
	
	<!-- CritExtraDamage-->
  	[DmgExtraCrit=""]

	<!-- Damage -->
  	[Damage=if(getGroup(id2, 1, 1) == "", "0", getGroup(id2, 1, 1))]
	[DmgExtra = 0]
	[DmgExtraName = ""]
	[newPlusStr= ""]
	[DamagePlus = ""]
	[DmgExtraCrit = "0d10"]
	[DamagePlus = if(0<getFindCount(id2), getGroup(id2, 1, 6), "")]
	[DmgBonus = 0]
	[DmgBonus = if(0<getFindCount(id2), getGroup(id2, 1, 2), 0)]
  	[id2 = strfind(atkstr, "(?i)(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)")]
	[DmgBonus = if(DmgBonus == "", 0, DmgBonus)]
  	[IF(0<getFindCount(id2)):eval(WpnName + "= '" + setStrProp(eval(WpnName),"TwoHanded", 1)+ "'")]
	[DmgBonus = round(if(0<getFindCount(id2), DmgBonus-Str2hB, DmgBonus-StrB))]
	
  	[ExtraDamageSearch = strfind(DamagePlus, "(.*) ([0-9]+[d0-9]+?) ?([a-zA-Z]+) ?(.*)")]
	[IF(0<getFindCount(ExtraDamageSearch)):DmgExtra=getGroup(ExtraDamageSearch, 1, 2)]
	[IF(0<getFindCount(ExtraDamageSearch)):DmgExtraName=getGroup(ExtraDamageSearch, 1, 3)]
	[IF(0<getFindCount(ExtraDamageSearch)):newPlusStr = getGroup(ExtraDamageSearch, 1, 1) + getGroup(ExtraDamageSearch, 1, 4)]
	[IF(0<getFindCount(ExtraDamageSearch)):newPlusStr = replace(newPlusStr, "plus", "")]
	[IF(0<getFindCount(ExtraDamageSearch)):newPlusStr = replace(newPlusStr, "and", "")]
	[IF(0<getFindCount(ExtraDamageSearch)):newPlusStr = trim(newPlusStr)]
	[IF(0<getFindCount(ExtraDamageSearch)):newPlusStr = replace(newPlusStr, "  ", " ")]
	[IF(0<getFindCount(ExtraDamageSearch)):newPlusStr = replace(newPlusStr, " ", " plus ")]
	[IF(0<getFindCount(ExtraDamageSearch)):Damage = trim(Damage + if(DmgBonus<0, "", "+") + DmgBonus + " " + IF(newPlusStr != "", "plus " + newPlusStr, newPlusStr)); Damage = trim(Damage + if(DmgBonus<0, "", "+") + DmgBonus + " " + DamagePlus)]
	[burstsearch = strfind(getGroup(id, 1, 3), "([a-zA-Z]+ )([Bb]urst)")]
	[IF(0<getFindCount(burstsearch)): DmgExtraCrit = "1d10 " + trim(getGroup(burstsearch, 1, 1))]
	
	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"Damage", Damage)+ "'")]
	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"DmgExtra", DmgExtra)+ "'")]
	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"DmgExtraName", DmgExtraName)+ "'")]
	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"DmgExtraCrit", DmgExtraCrit)+ "'")]
	<!-- Two handed ? -->
    <!-- Default to onehanded weapon -->
	[eval(WpnName + "= '" + setStrProp(eval(WpnName),"TwoHanded", 0)+ "'")]

}]
  
	[wpnno = wpnno +1]
}]

[H: broadcast(strformat("Import Complete %{name}"),"self,gm")]
!!

Post Reply

Return to “[D&D3.5 + Pathfinder] Campaign Macros”