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")]