Aaron |
August 31st, 2017 07:10 AM |
This is the way that Pathfinder converts a series of ReqFeat tags into a pre-requisite script for matching feats on the hero. It uses the basic principle I described above, but is more complex because it has to work both while a pick and a thing and has to handle various wacky substitutions.
Code:
<prereq iserror="no" message="Feat Required">
<match><![CDATA[
count:ReqFeat.? <> 0
]]></match>
<validate><![CDATA[
~there's a tag that removes all feat-based prereqs for this ability
validif (isidentity[NoFeatPre] <> 0)
var namestrpos as number
var idstrpos as number
var splitlen as number
var tagidstr as string
var tagnamestr as string
var thistagid as string
var thistagnam as string
var failname as string
if (@ispick <> 0) then
~ If every ReqFeat tag is matched with a tag on the hero, then we're
~ valid.
~ Note that this isn't foolproof because some substitutions happen
~ later. Thus at the end of the process we will have one final validif.
validif (altpick.intersect[ReqFeat,HasFeat,allsource] <> 0)
~ If even one failed, create a string variable, and carve that up in
~ a loop, comparing each section to the tags on the hero. If no match,
~ then store that in another string we will use to generate our
~ failure message.
~loop until we run out of items to add as children
tagidstr = altpick.tagids[ReqFeat.?,#custsplitter[]]
tagnamestr = altpick.tagnames[ReqFeat.?,#custsplitter[]]
else
~ If every ReqFeat tag is matched with a tag on the hero, then we're
~ valid.
validif (altthing.intersect[ReqFeat,HasFeat,allsource] <> 0)
~ If even one failed, create a string variable, and carve that up in
~ a loop, comparing each section to the tags on the hero. If no match,
~ then store that in another string we will use to generate our
~ failure message.
~loop until we run out of items to add as children
tagidstr = altthing.tagids[ReqFeat.?,#custsplitter[]]
tagnamestr = altthing.tagnames[ReqFeat.?,#custsplitter[]]
endif
~if we're greater
splitlen = length(#custsplitter[])
tagidstr = replace(tagidstr,"ReqFeat","HasFeat",0)
var checktags as number
var foundtags as number
while (1 = 1)
if (empty(tagidstr) <> 0) then
~ Normally our valid state would have stoped us from getting this far,
~ as we only proceed if we lacked matches at the start. However, if we
~ found a match for every tag we checked (probably because of dirty
~ fighting substitutions), then we are valid after all!
validif (foundtags = checktags)
~ If that final check didn't work out then we really are invalid, so
~ stop now that we have no tags to examine and we have to set the
~ failure message.
@message = failname & " required."
done
endif
~ The checktags variable counts the number of tags we have considered
~ while processing this pre-req. Once we have iterated through all of
~ the starting tag string it will be compared to the foundtags variable
~ to see if, because of substitutions from Dirty Fighting or other
~ things, we actually are valid despite failing the initial test at
~ the beginning of this script.
checktags += 1
~find the next place the splitter occurs
namestrpos = pos(tagidstr, #custsplitter[])
idstrpos = pos(tagnamestr, #custsplitter[])
~if it doesn't, this is the last item in the list
if (namestrpos < 0) then
thistagid = tagidstr
tagidstr = ""
~if not, we should break the string in two around the splitter so we can
~repeat this operation on the remaining text
else
thistagid = left(tagidstr, namestrpos)
tagidstr = mid(tagidstr, namestrpos + splitlen, -1)
endif
~if it doesn't, this is the last item in the list
if (idstrpos < 0) then
thistagnam = tagnamestr
tagnamestr = ""
~if not, we should break the string in two around the splitter so we can
~repeat this operation on the remaining text
else
thistagnam = left(tagnamestr, idstrpos)
tagnamestr = mid(tagnamestr, idstrpos + splitlen, -1)
endif
~ If the currently considered tag is for a mythic feat, we need to
~ append that to the name in the list we build. Otherwise there is
~ nothing to distinguish Mythic Improved Critical vs. normal Improved
~ Critical.
var idonly as string
idonly = replace(thistagid,"HasFeat.","",0)
foreach thing in idlist idonly
if (eachthing.tagis[fCategory.Mythic] <> 0) then
thistagnam = splice(thistagnam,"(mythic)"," ")
endif
nexteach
~ Dirty Fighting can substitute for Combat Expertise and Improved Unarmed Attack,
~ but only for feats that require an improved maneuver feat
var tagexpr as string
tagexpr = #improvedcombatfeatreqexpr[]
~ We use a variable here as insurance against future expansion of the
~ abilities able to substitute.
var subtext as string
~ reset it each iteration
subtext = ""
~ Handle Dirty Fighting substitutions.
if (compare(thistagid,"HasFeat.fComExpert") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires one of the improved
~ maneauver feats which need combat expertise.
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
~ This checks if we ARE one of the improved maneauver feats which
~ need combat expertise.
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
else
~ This checks if we are something that requires one of the improved
~ maneauver feats which need combat expertise.
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
~ This checks if we ARE one of the improved maneauver feats which
~ need combat expertise.
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
endif
endif
~Kinetic Warrior substitutions.
~ Kinetic Warrior counts as Combat Expertise for feat pre-requisites
if (compare(thistagid,"HasFeat.fComExpert") = 0) then
if (@ispick <> 0) then
if (altpick.tagis[component.BaseFeat] <> 0) then
subtext = splice(subtext,"HasAbility.cKinWarrio", " | ")
endif
else
if (altthing.tagis[component.BaseFeat] <> 0) then
subtext = splice(subtext,"HasAbility.cKinWarrio", " | ")
endif
endif
endif
if (compare(thistagid,"HasFeat.fImpUnarm") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires one of the improved
~ maneauver feats which need improved grapple.
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
~ This checks if we ARE one of the improved maneauver feats which
~ need combat expertise.
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
else
~ This checks if we are something that requires one of the improved
~ maneauver feats which need improved grapple.
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
~ This checks if we ARE one of the improved maneauver feats which
~ need combat expertise.
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fDirtyFigh", " | ")
endif
endif
endif
~ Two-Weapon Grace substitutions.
~ Two-Weapon Grace counts as Double Slice for Two-Weapon Rend
if (compare(thistagid,"HasFeat.fDblSlice") = 0) then
if (@ispick <> 0) then
if (altpick.tagis[thingid.fTwoWepRen] <> 0) then
subtext = splice(subtext,"HasFeat.fTwoWepGra", " | ")
endif
else
if (altthing.tagis[thingid.fTwoWepRen] <> 0) then
subtext = splice(subtext,"HasFeat.fTwoWepGra", " | ")
endif
endif
endif
~ Craft Poppet substitutions.
~ Craft Poppet counts as Craft Magic Arms and Armor and Craft Wondrous
~ Item for Craft Construct
if (compare(thistagid,"HasFeat.fCraftMgc") = 0) then
if (@ispick <> 0) then
if (altpick.tagis[thingid.fCraftCons] <> 0) then
subtext = splice(subtext,"HasFeat.fCraftPopp", " | ")
endif
else
if (altthing.tagis[thingid.fCraftCons] <> 0) then
subtext = splice(subtext,"HasFeat.fCraftPopp", " | ")
endif
endif
endif
if (compare(thistagid,"HasFeat.fCraftWond") = 0) then
if (@ispick <> 0) then
if (altpick.tagis[thingid.fCraftCons] <> 0) then
subtext = splice(subtext,"HasFeat.fCraftPopp", " | ")
endif
else
if (altthing.tagis[thingid.fCraftCons] <> 0) then
subtext = splice(subtext,"HasFeat.fCraftPopp", " | ")
endif
endif
endif
~ Handle Guarded Charge substitutions.
~ Guarded Charge counts as Power Attack for Improved Bull Rush/Improved Overrun
~ and anything which requires either of those two feats.
tagexpr = "ReqFeat.fImpBull | ReqFeat.fImpOver"
if (compare(thistagid,"HasFeat.fPowerAtt") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires improved bull rush
~ or improved overrun
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fGuardChar", " | ")
endif
~ This checks if we ARE improved bull rush or improved overrun
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fGuardChar", " | ")
endif
else
~ This checks if we are something that requires improved bull rush
~ or improved overrun
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fGuardChar", " | ")
endif
~ This checks if we ARE improved bull rush or improved overrun
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasFeat.fGuardChar", " | ")
endif
endif
endif
~ Perfect Aid counts as Combat Expertise for
~ Swift Aid
~ and anything which requires that feat.
if (#hasability[cOraPerAid] <> 0) then
tagexpr = "ReqFeat.fComExpert"
if (compare(thistagid,"HasFeat.fComExpert") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires
~ Swift Aid
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cOraPerAid", " | ")
endif
~ This checks if we ARE Swift Aid
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cOraPerAid", " | ")
endif
else
~ This checks if we are something that requires
~ Swift Aid
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cOraPerAid", " | ")
endif
~ This checks if we ARE Swift Aid
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cOraPerAid", " | ")
endif
endif
endif
endif
~ Dirty Trickser counts as Combat Expertise for Improved Dirty Fighting
~ and anything which requires either of those two feats.
if (#hasability[cBrdDirtyT] <> 0) then
tagexpr = "ReqFeat.fImpDirTri"
if (compare(thistagid,"HasFeat.fComExpert") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires
~ improved dirty trick
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cBrdDirtyT", " | ")
endif
~ This checks if we ARE Improved Dirty Trick
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cBrdDirtyT", " | ")
endif
else
~ This checks if we are something that requires
~ Improved Dirty Trick
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cBrdDirtyT", " | ")
endif
~ This checks if we ARE Improved Dirty Trick
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cBrdDirtyT", " | ")
endif
endif
endif
endif
~ Consummate Liar counts as Combat Expertise for
~ Improved Feint/Greater Feint
~ and anything which requires either of those two feats.
if (#hasability[cMesConLia] <> 0) then
tagexpr = "ReqFeat.fImpFeint | ReqFeat.fGreatFein"
if (compare(thistagid,"HasFeat.fComExpert") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires
~ Improved Feint/Greater Feint
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConLia", " | ")
endif
~ This checks if we ARE Improved Feint/Greater Feint
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConLia", " | ")
endif
else
~ This checks if we are something that requires
~ Improved Feint/Greater Feint
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConLia", " | ")
endif
~ This checks if we ARE Improved Feint/Greater Feint
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConLia", " | ")
endif
endif
endif
endif
~ Consummate Cruelty counts as Combat Expertise for Improved Dirty
~ Trick/Greater Dirty Trick
~ and anything which requires either of those two feats.
if (#hasability[cMesConsCr] <> 0) then
tagexpr = "ReqFeat.fImpDirTri | ReqFeat.fGreatTric"
if (compare(thistagid,"HasFeat.fComExpert") = 0) then
if (@ispick <> 0) then
~ This checks if we are something that requires
~ Improved Dirty Trick/Greater Dirty Trick
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConsCr", " | ")
endif
~ This checks if we ARE Improved Dirty Trick/Greater Dirty Trick
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altpick.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConsCr", " | ")
endif
else
~ This checks if we are something that requires
~ Improved Dirty Trick/Greater Dirty Trick
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConsCr", " | ")
endif
~ This checks if we ARE Improved Dirty Trick/Greater Dirty Trick
tagexpr = replace(tagexpr,"ReqFeat","thingid",0)
if (altthing.tagsearch[tagexpr] <> 0) then
subtext = splice(subtext,"HasAbility.cMesConsCr", " | ")
endif
endif
endif
endif
~ If we have any substitute text, append that with an appropriate
~ linking "or" to the current tag id check used.
if (empty(subtext) = 0) then
subtext = " | " & subtext
thistagid &= subtext
endif
~ Compare the current thistagid against tags on the hero. If it
~ matches, great! Otherwise register the failed tag's name in the
~ failname string, for use later.
if (hero.tagsearch[thistagid] = 0) then
failname = splice(failname, thistagnam, ", ")
else
~ This variable counts the number of matches, for a final validation
~ check at the end.
foundtags += 1
endif
~ remove the substitute text we may have appended earlier
thistagid = replace(thistagid,subtext,"",1)
loop
]]></validate>
</prereq>
|