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

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

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

Forum rules
Discussion regarding lmarkus001's framework only. Other posts deleted without notice! :)
Elorebaen
Dragon
Posts: 365
Joined: Sat Dec 22, 2007 5:37 pm

Re: [AM Macros] Lindsay's PF/3.5 Campaign FW

Post by Elorebaen »

Thank you sharing AM!!
aliasmask wrote:New update to statblock importer to fix statsheet variable input error when [] or {} appears in display text. I'm not sure if this fix breaks anything else that may use [] in the code. I did look through the other regex stuff and didn't find anything that referenced those characters directly.

Also see this post for the previous update: http://forums.rptools.net/viewtopic.php ... 19#p256632
Lib:libDnD35Pathfinder wrote:
||| pfStatBlockImport |||

Code: Select all

<!-- Statblock2Token v2.0.4 (3-15-15)
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)
-->
[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)]
}]

<!-- 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: 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.*?[0-9]) +(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 (.+?))(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 = ""]
}]

<!-- If Special Abilities section exists, break it up into a separate line for each ability with its description -->
[H, IF(0<getFindCount(SpecialAbilitiesSearch)), CODE: {
	[SAbNote = "

-----SPECIAL ABILITIES-----"]
	[SAList = ""]
	[SpecialAbilityString = getGroup(SpecialAbilitiesSearch, 1, 2)]
	[SingleSpecialAbilitySearch = strfind(SpecialAbilityString, "(?i)(.*?\\. +)(?=.*? \\((Ex|Su|Sp)\\))")]
	[IF(0<getFindCount(SingleSpecialAbilitySearch)), FOR(NumOfSpecialAbilities, 1, getFindCount(SingleSpecialAbilitySearch)+1), CODE: {
		[SingleSpecialAbility = getGroup(SingleSpecialAbilitySearch, NumOfSpecialAbilities, 1)]
		[SpecialAbilityString = replace(SpecialAbilityString, "(?i)(.*?\\. +)(?=.*? \\((Ex|Su|Sp)\\))", "")]
		[SearchingForExtraLines = strfind(SingleSpecialAbility, "(?i)\\((Ex|Su|Sp)\\)")]
		[IF(0<getFindCount(SearchingForExtraLines)): SAList = SAList + "
" + trim(SingleSpecialAbility) ; SAList = SAList + " " + trim(SingleSpecialAbility)]
	}]
	[SAList = SAList + "
" + trim(SpecialAbilityString)]
	[SAbNote = SAbNote + SAList]
};{
	[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 -->
[H: setGMNotes(trim(getGroup(NameSearch, 1, 1)) + "	" + trim(getGroup(CRSearch, 1, 1)) + "
------------
" + trim(getGroup(XPSearch, 1, 1)) +
 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, 1)) +
 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"),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(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); "" ]
   [ 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) ]
   
   [H: sysVars = table("SysVars", 0)]
   [H: sysFeats = table("SysVars", json.get(sysVars, "sysFeats"))]
   [H: tsfl = json.fields(sysFeats,"json")]
   [H: tFeatsSys = getProperty("Feats")]
   [H, FOREACH(p,tsfl),CODE: {
      [H: tfcont = json.contains(tFeatsJ2,p)]
      [H: tval = if(tfcont,1,0)]
      [H: tFeatsSys = setStrProp(tFeatsSys,json.get(sysFeats,p),tval)]
   }]
   [H: setProperty("Feats", tFeatsSys)]
   [H: output=output+tFeats ]
};{
}]
[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")]

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

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

Post by aliasmask »

I updated the OP with my most recent campaign. I think Lindsay got most of the updates that were important except may the MT bug with setLayer and token facing. You can see an example of the fix under Full HP macro. I basically note the token shape and if it changes after running the code I change it back.

I am working on an overhaul of the skills interface to add some functionality. It will be completely separate from the existing but be backwards compatible. The main difference will be in how skill modifiers are handled, but will allow adding and removing modifiers with a function call. Skill modifiers generally fall under 2 groups, always on and optional. Right now I'm just working on the basics and build upon that for some of the more complicated stuff. For example, a gm can create a skill check, set options, list success and failure conditions and text and post it as a link. Players would then select their token and click the link. That's already a part of my lib:dialog token, but wanted to clean it up.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

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

Post by aliasmask »

This is a minor/major fix for the lib:spell3 token.

When adding powers under the class independent group people may end up putting a ' or , in the name which really screws up everything and you end up having to clear the whole token to fix.

What happens is it creates a blank sub category which wasn't checked for. I put in a check and a fix to remove the bad data.

Code: Select all

@@ @Generate CI Straight Links
[h: SAcat = macro.args]

[h: specabils = CI_SpecAbils]
[h: catabils = json.get(specabils, SAcat)]
[h, if(json.type(catabils) != "object"): catabils = "{}"]

[H: badCatagories = "[]"]
[r,foreach(abilentry,catabils,""),code:{
   [H: abilentryData = json.get(catabils, abilentry)]
   [R, if(! json.isEmpty(abilentryData)), code: {
      [macro("Generate CI Straight Link@" + getMacroLocation()): abilentryData]
   };{
      [H: badCatagories = json.append(badCatagories,abilentry)]
   }]
}]

[H, if(! json.isEmpty(badCatagories)), code: {
   [H, foreach(catagory,badCatagories): catabils = json.remove(catabils,catagory)]
   [H: CI_SpecAbils = json.set(CI_SpecAbils, SAcat, catabils)]
};{}]

!!
 
This only addresses the class independent powers and not in the linked groups or class powers, so it's not an all encompassing fix. The design of the code and data structures makes it difficult to track down all the areas this fix would be needed. So, I just applied it to the most common area.

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

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

Post by lmarkus001 »

Yeah I had to scrub a LOT of data because of that issue.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

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

Post by aliasmask »

My friend updated a lot of the classes and spells for our game. I'm going to go through and try to fix the most glaring of bugs like templates not being used between levels and some other leveling bugs. I made a partial fix (no error and crash) but it doesn't update properly due to level and ability change. Also, I'm going to put in the filter so special abilities are not so finicky and fix the single quote thing (ie no more single quotes, just put in raw macro script). I'll share that when I'm done. I'm going to try and make it bullet proof.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

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

Post by aliasmask »

I'm currently working on a bunch of fixes/enhancement for lib:spells3 token. It's going pretty smoothly and I suspect I'll finish today.

Does anyone have any specific gripes about the spell manager? Things I've already done:

Code: Select all

AM Updates (3/29/13)
* Now all spells for others shows X cast Spell (?) where (?) can be clicked to roll spellcraft check. The (?) for owners and gm get to see common rolls used when casting a spell.
* Added variable to spell level boost that only effects spell level and not the number of spells for use with spell report.
* MUST BE USED WITH lib:players (special output) and Lib:libDnD35Pathfinder (skill checks).
* All new UDF functions and onCampaignLoad are in blank group and modified Manager macros are in yellow with width 94 set.
Updated by Ray to include more caster classes and spells. 3-17-14
Updated by Ray to include more spells 12-20-14
* Fixed level up for clerics. Removed spell convert question if already set. 12-20-14
* Fix CI Generate Straight Links to filter bad data (8-11-15)

Current Updates (8-13-15)
* Added Campaign Macros "Cast Spell" and "Special Abils"
* "Cast Spell" no longer errors when spell classes are empty.
* Domain and Spec School bugs fixed when leveling
* Fixed Template Restore. Now it counts spells to find difference in current SPD rather than using original SPD. Allows you to add more spells at higher levels than when the Template was saved.
* Added am.output.basic for better output options. This is a compatibility fix so no outside lib is needed.
* Fixed some ownership issues with skill rolls for spellcraft and updated uglySkillCheck.
* (WIP) Getting rid of that horrible formatting for special abilities. You'll be able to use any characters and macro code in the text. And no more darn quote checkboxes. BUT, this means your existing code will need to be updated to MT macro code format otherwise it'll just show the '+function()+' type formats. btw, you would simply put [r: function()] in the text. This will also help prevent you from ruining your token and starting over because of bad formatting.

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

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

Post by lmarkus001 »

The Plothos manager in my released version has some patches in it, and there are a lot of changes in my development version to include:
https://www.dropbox.com/s/fohprwz2oat9x ... rptok?dl=1

LGM Updates (08/12/2015)
CRITICAL: for all macros with Text in name (and a few others) - replace token.name with tToken (currentToken()) for macroLink()
Generate CI Staight Links - aliasmask

LGM Updates (08/04/2015)
Spell Cast - renamed frame so do not get 2 frames using the spell manager
Changed window width to 600 down from 800

LGM Updates (07/09/2015)
Output (macro)

LGM Updates (6/12/2015)
Corrected Cleric PF Healing Domain
Added Rogue PF Class
Added Barbarian PF Class

LGM Updates (5/13/2015)
Corrected Wizard Spell lists

LGM Updates (6/19/2013)
* Export/Import corrections

* Enhanced Witch
* Storm Druid

LGM Updates (5/22/2013)
* Unbound it from lib:players

Also of note, I find your lib:players library to be a huge overhead so changed the output to use a similar output routine in my library. Now that I think about it, the output routine used should probably just be on the Plothos token so it is stand alone.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

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

Post by aliasmask »

Ah that reminds me. I was going to completely unbound the frame in size and rework the header and body of output so it would fit in to a smaller window. I actually did that once before but it got lost in an update somewhere (wrathgon I believe).

I'm guessing the token.name change is MOTE related? The two frames thing was a personal preference of mine so I can look at my spell list and abilities at the same time. I can change that back and add another link that will split the spell window off.

I'll go ahead and make those changes you made, but you may have to explain the token.name and currentToken() thing in a little more detail. I also came to the same conclusion and unbound lib:players token functions. I'll see how the export tool for the spell manager works to merge the data. Actually, I'll just have my friend go through all the classes and books and add the missing stuff. He's been wanting a project.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

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

Post by aliasmask »

Well, I got the changes made for the class-independent stuff but not the classes. I'm thinking I may just leave that alone because of how all the class abilities are entered which count on eval rather evalMacro.

People can download it and tell me what they think. If there are any bugs, tell me how you got it, maybe attach the affected token and I'll try and fix it.

Also, I'm open to suggestions. I forgot the change the frame size and spell list popup, but I'll do that as well.

What do you think about having the special ability descriptions in a tool tip rather than listed so more space can be saved?
Lib_spells3 8-15-15.rptok
(540 KiB) Downloaded 142 times

Code: Select all

AM Updates (3/29/13)
* Now all spells for others shows X cast Spell (?) where (?) can be clicked to roll spellcraft check. The (?) for owners and gm get to see common rolls used when casting a spell.
* Added variable to spell level boost that only effects spell level and not the number of spells for use with spell report.
* MUST BE USED WITH lib:players (special output) and Lib:libDnD35Pathfinder (skill checks).
* All new UDF functions and onCampaignLoad are in blank group and modified Manager macros are in yellow with width 94 set.
Updated by Ray to include more caster classes and spells. 3-17-14
Updated by Ray to include more spells 12-20-14
* Fixed level up for clerics. Removed spell convert question if already set. 12-20-14
* Fix CI Generate Straight Links to filter bad data (8-11-15)

Current Updates (8-13-15)
* Added Campaign Macros "Cast Spell" and "Special Abils"
* "Cast Spell" no longer errors when spell classes are empty.
* Domain and Spec School bugs fixed when leveling
* Fixed Template Restore. Now it counts spells to find difference in current SPD rather than using original SPD. Allows you to add more spells at higher levels than when the Template was saved.
* Added am.output.basic for better output options. This is a compatibility fix so no outside lib is needed.
* Fixed some ownership issues with skill rolls for spellcraft and updated uglySkillCheck.
* Updated Add Power/Edit input menus to use macro code instead relying properly formated eval statements. This was causing critical errors which resulted tokens needing to be cleared and funky formatting requirements. Existing "code" will need to be reformatted to [r:] style macro code. I'm not updating the class dependent stuff because of the predefined entries. 
* Fixed comma (,) in power name problem.

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

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

Post by lmarkus001 »

aliasmask wrote: I'll go ahead and make those changes you made, but you may have to explain the token.name and currentToken() thing in a little more detail.
There are many macroLink() entries in the library, most are in macros with TEXT in the name. They look like:

Code: Select all

[r:macroLink("Rest", "Rest Router From Prep@" + getMacroLocation(), "none", classviewed+","+levelviewed,token.name)] 
token.name needs to be replaced with a token ID. So I set an id variable first thing in the macro, then use that variable in all macroLink() entries.

Code: Select all

[H: tToken = currentToken() ]
...
[r:macroLink("Rest", "Rest Router From Prep@" + getMacroLocation(), "none", classviewed+","+levelviewed,tToken)] 
The reason is if you have two tokens with the same name, the spell manager only works on one of them. If you change to using ID then it works no matter what.

User avatar
aliasmask
RPTools Team
Posts: 9023
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 »

Okay, I updated all the token.name's to currentToken() for the macroLinks. Not too hard with replace command. I just had to give it the once over to make sure I wasn't changing something else.

User avatar
aliasmask
RPTools Team
Posts: 9023
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 »

Make those last couple of changes. The frame width is now 100% so it scaled to window size. Removed the Spell Cast window so all views are in same window.
Attachments
Lib_spells3 8-18-15.rptok
(540.28 KiB) Downloaded 139 times

User avatar
lmarkus001
Great Wyrm
Posts: 1867
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 your updates to the library token with my Export tweaks and a several more classes and some additional data scrubbing.

EDIT: File removed, see later post for update.

User avatar
aliasmask
RPTools Team
Posts: 9023
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:Here is your updates to the library token with my Export tweaks and a several more classes and some additional data scrubbing.
Looks good. Thanks.

User avatar
lmarkus001
Great Wyrm
Posts: 1867
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 »

Was just testing in Mote and the output macro is failing, I will upload an update once it is corrected (and it will be in my framework).

Post Reply

Return to “D&D 3.5/Pathfinder 1e Campaign Macros”