• Please note: In an effort to ensure that all of our users feel welcome on our forums, we’ve updated our forum rules. You can review the updated rules here: http://forums.wolflair.com/showthread.php?t=5528.

    If a fellow Community member is not following the forum rules, please report the post by clicking the Report button (the red yield sign on the left) located on every post. This will notify the moderators directly. If you have any questions about these new rules, please contact support@wolflair.com.

    - The Lone Wolf Development Team

Programatically adding spells to a class: almost got it

wynlyndd

Well-known member
I've got it : Programatically adding spells to a class for UA Class Variants

Okay, thinking about how to add the expanded spell list for the UA Class Variants, I tried to use ExtendThing which was less than ideal. I decided to see how other options did it.

Enter the Stone Magic Sorcerous Origin from UA. It adds a bunch of non-sorcerer spells.

So following its lead I added this code:
Code:
<document signature="Hero Lab Data">
  <thing id="cElmSpSCM" name="Elemental Spell" description="When you cast a spell that deals a type of damage from the following list, you can spend 1 sorcery point to change that damage type to one of the other listed types: acid, cold, fire, lightning, thunder. If a wild sorc, roll d20. If 5 or below, determine dmg type randomly. If affected by Tides of Chaos, always randomly determine dmg type." compset="CustomSpec" summary="Spend 1 sorc. pt.: Choose dmg type from 5 elements." uniqueness="unique">
    <usesource source="p5eSorUACVAP"/>
	<tag group="Helper" tag="Secondary" name="Secondary" abbrev="Secondary"/>
    <tag group="SpecSource" tag="cHelpSor" name="Sorcerer" abbrev="Sorcerer"/>
    <tag group="abAction" tag="Free" name="Free" abbrev="Free"/>
    <tag group="abCategory" tag="SorMetamag" name="Sorcerer Metamagic Ability" abbrev="Sorcerer Metamagic Ability"/>
    <tag group="abDuration" tag="Instant" name="Instantaneous" abbrev="inst"/>
    <tag group="abRange" tag="Personal" name="Personal" abbrev="pers"/>
	<bootstrap thing="cUAExSpLt">
      <autotag group="ClSpecWhen" tag="1"/>
    </bootstrap>
    </thing>

	<thing id="cUAExSpLt" name="Expanded Spell List" description="You must otherwise obey all the restrictions for selecting the spell, and it becomes a sorcerer spell for you.\n\n" compset="ClSpecial">
    <usesource source="p5eSorUACVAP"/>
	<eval phase="Final" priority="21000"><![CDATA[doneif (tagis[Helper.ShowSpec] = 0)
doneif (tagis[Helper.Disable] <> 0)

~these vars will hold the tag expressions for spells and cantrips

var cantrip as string
var spell as string
var known as string 
var first as string
var second as string
var third as string
var fourth as string
var sixth as string
var eigth as string
var ninth as string


~ fetch spell expressions
spell = hero.childfound[cHelpSor].field[cSpKnoExpr].text 
known = hero.childfound[cHelpSor].field[cSpellExpr].text 


~ these receive the extra spells for each circle
cantrip = " | thingid.sp5CXGEPrS "
first = " | thingid.spGrease | thingid.spProtGoEv "
second = " | thingid.spFlamBlad | thingid.spFlamSphe "
third = " | thingid.spVampTouc "
fourth = " | thingid.spFireShie "
sixth = " | thingid.spFlesSton "
eigth = " | thingid.spDemiplan "
ninth = " | thingid.spForesigh "

hero.childfound[cHelpSor].field[cCnKnoExpr].text &= cantrip
spell &= cantrip

if (#levelcount[Sorcerer] >= 1) then
  spell &= first
  known &= first
  if (#levelcount[Sorcerer] >= 3) then
    spell &= second
    known &= second
    if (#levelcount[Sorcerer] >= 5) then
      spell &= third
      known &= third
      if (#levelcount[Sorcerer] >= 7) then
        spell &= fourth
        known &= fourth
  		if (#levelcount[Sorcerer] >= 11) then
		  spell &= sixth
		  known &= sixth
        endif		  
      endif
    endif
  endif
endif
if (#levelcount[Sorcerer] >= 15) then
  spell &= eigth
  known &= eigth
  if (#levelcount[Sorcerer] >= 17) then
    spell &= ninth
	known &= ninth
	endif
endif

hero.childfound[cHelpSor].field[cSpKnoExpr].text = spell
hero.childfound[cHelpSor].field[cSpellExpr].text = known
hero.childfound[cHelpSor].field[cSplBkExpr].text = known]]></eval>
    </thing>

  </document>

Which worked! It added Grease, Flame Blade, FlameShield, VampricTouch, Fire Shield, and Flesh to Stone, Demiplane, and Foresight to the Sorcerer's spell list. And it is toggleable via Configure Hero.

There appears to be a limit to nested if statements, so that's why the last two spell levels are checked in a separate if statement.

But I think I have it!!!!
 
Last edited:
one bit of odd behavior: If you aren't of the right level, that spell isn't going to show in the spell list, unlike all of the other spells. I may be able to fix that but I am not near my computer with Hero Lab right now.

Fixed in my second bit of code below.
 
Last edited:
Actually, it's even easier than that.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<document signature="Hero Lab Data">
  <thing id="cElmSpSCM" name="Elemental Spell" description="When you cast a spell that deals a type of damage from the following list, you can spend 1 sorcery point to change that damage type to one of the other listed types: acid, cold, fire, lightning, thunder. If a wild sorc, roll d20. If 5 or below, determine dmg type randomly. If affected by Tides of Chaos, always randomly determine dmg type." compset="CustomSpec" summary="Spend 1 sorc. pt.: Choose dmg type from 5 elements." uniqueness="unique">
    <usesource source="p5eSorUACVAP"/>
	<tag group="Helper" tag="Secondary" name="Secondary" abbrev="Secondary"/>
    <tag group="SpecSource" tag="cHelpSor" name="Sorcerer" abbrev="Sorcerer"/>
    <tag group="abAction" tag="Free" name="Free" abbrev="Free"/>
    <tag group="abCategory" tag="SorMetamag" name="Sorcerer Metamagic Ability" abbrev="Sorcerer Metamagic Ability"/>
    <tag group="abDuration" tag="Instant" name="Instantaneous" abbrev="inst"/>
    <tag group="abRange" tag="Personal" name="Personal" abbrev="pers"/>
	<bootstrap thing="cUAExSpLt">
      <autotag group="ClSpecWhen" tag="1"/>
    </bootstrap>
    </thing>

	<thing id="cUAExSpLt" name="Expanded Spell List" description="You must otherwise obey all the restrictions for selecting the spell, and it becomes a sorcerer spell for you.\n\n" compset="ClSpecial">
    <usesource source="p5eSorUACVAP"/>
	<eval phase="Final" priority="21000"><![CDATA[doneif (tagis[Helper.ShowSpec] = 0)
doneif (tagis[Helper.Disable] <> 0)

~these vars will hold the tag expressions for spells and cantrips

var cantrip as string
var spell as string
var known as string 
var first as string
var second as string
var third as string
var fourth as string
var sixth as string
var eigth as string
var ninth as string


~ fetch spell expressions
spell = hero.childfound[cHelpSor].field[cSpKnoExpr].text 
known = hero.childfound[cHelpSor].field[cSpellExpr].text 


~ these receive the extra spells for each circle
cantrip = " | thingid.sp5CXGEPrS "
first = " | thingid.spGrease | thingid.spProtGoEv "
second = " | thingid.spFlamBlad | thingid.spFlamSphe "
third = " | thingid.spVampTouc "
fourth = " | thingid.spFireShie "
sixth = " | thingid.spFlesSton "
eigth = " | thingid.spDemiplan "
ninth = " | thingid.spForesigh "

hero.childfound[cHelpSor].field[cCnKnoExpr].text &= cantrip

spell &= first
known &= first

spell &= second
known &= second

spell &= third
known &= third

spell &= fourth
known &= fourth

spell &= sixth
known &= sixth

  spell &= eigth
  known &= eigth

 spell &= ninth
known &= ninth


hero.childfound[cHelpSor].field[cSpKnoExpr].text = spell
hero.childfound[cHelpSor].field[cSpellExpr].text = known
hero.childfound[cHelpSor].field[cSplBkExpr].text = known]]></eval>
    </thing>

  </document>

You could even run all the spells into one massive string but this is spaced out for readablity
 
Last edited:
Awesome work so far! I haven't had a chance to put it into HL and play around with it, but I think you've got this covered!

I've been messing with ways to get the variant features in. I've landed on a working solution for many of the options. Players will need to select an adjustment for their class. The adjustment calls up a configurable for that class's features. Users can select any or all of their available variant features from a list.

The problems will come in whether or not those individual abilities can do what they're supposed to, especially when they're replacing existing features. But we'll have to take those on a case-by-case basis.

The other things, like new Warlock Invocations, Fighting Styles, etc. will all be available as long as the user has selected the Class Feature Variant source. They'll just show up as new options for those features, and can be ignored if necessary.
 
The other things, like new Warlock Invocations, Fighting Styles, etc. will all be available as long as the user has selected the Class Feature Variant source. They'll just show up as new options for those features, and can be ignored if necessary.

Yeah anything that was an enhancement, I figured to just add to the character sheet and if the DM did't allow it you should ignore.

It's when the feature replaces that I was going to try and figure out next. Was going to look into conditional bootstraps but I haven't had the time yet.
 
This is truly awesome coding, you've got a pretty good glimpse at playing with spells there.

I have a suggestion for the Community Pack - like Eldritch Knights are supposed to only be able to pick abjuration/evocation spells initially (at least till level 8). You could add something like the following to enforce that by modifying the code you posted:

Code:
doneif (tagis[Helper.ShowSpec] = 0)
doneif (tagis[Helper.Disable] <> 0)

~ Once we're 8th level and above we can remove the restriction on spells
~ We could probably do some counting of the sSchool tags to enforce 
~ that at most we have 4 non Abjuration/Evocation spells but 

doneif (#levelcount[Fighter] > 7)

~ Otherwise we modify the spell lists.

var spell as string
var known as string 

~ fetch spell expressions from the initial lists
spell = "((" & hero.childfound[cHelpFtr].field[cSpKnoExpr].text & ")"
known = "((" & hero.childfound[cHelpFtr].field[cSpellExpr].text & ")"

~ Narrow down to abjuration and evocation only
spell &= " & (sSchool.Abjur|sSchool.Evocation))"
known &= " & (sSchool.Abjur|sSchool.Evocation))"

~ push out the new lists
hero.childfound[cHelpFtr].field[cSpKnoExpr].text = spell
hero.childfound[cHelpFtr].field[cSpellExpr].text = known
hero.childfound[cHelpFtr].field[cSplBkExpr].text = known
 
May I see?
Awesome work so far! I haven't had a chance to put it into HL and play around with it, but I think you've got this covered!

I've been messing with ways to get the variant features in. I've landed on a working solution for many of the options. Players will need to select an adjustment for their class. The adjustment calls up a configurable for that class's features. Users can select any or all of their available variant features from a list.

The problems will come in whether or not those individual abilities can do what they're supposed to, especially when they're replacing existing features. But we'll have to take those on a case-by-case basis.

The other things, like new Warlock Invocations, Fighting Styles, etc. will all be available as long as the user has selected the Class Feature Variant source. They'll just show up as new options for those features, and can be ignored if necessary.
 
Here's the relevant stuff I have. It has everything that's relevant to the Bard with placeholders for each ability/feature.

Personally, I think sticking to 1 single CFV source and controlling the addition of the features through an adjustment -> configurable is the cleanest implementation. I mentally groan every time I see how bloated out our configure hero screen already is. I'm going to take a stab at adding your code in for the Bard expanded spell list.

Code:
  <thing id="p5CCFVBrd" name="Class Feature Variants - Bard" compset="InPlay">
    <usesource source="p5eCFVUACP"/>
    <bootstrap thing="cfg5CCFVBrd"></bootstrap>
    <exprreq message="Test"><![CDATA[#levelcount[Bard] >= 1]]></exprreq>
    </thing>
  <thing id="cfg5CCFVBrd" name="Bard Variant Features" compset="Configure" uniqueness="unique">
    <tag group="Helper" tag="Allow1Abil"/>
    </thing>
  <thing id="cBrd5CMgInsp" name="Magical Inspiration" description="<just needs text>" compset="CustomSpec" uniqueness="unique">
    <tag group="AllowRCust" tag="cfg5CCFVBrd"/>
    </thing>
  <thing id="cAll5CSplVers" name="Spell Versatility" description="<just needs text>" compset="CustomSpec" uniqueness="unique">
    <tag group="AllowRCust" tag="cfg5CCFVBrd"/>
    </thing>
  <thing id="cAll5CPrfVers" name="Proficiency Versatility" description="<Needs programming>" compset="CustomSpec" uniqueness="unique">
    <tag group="AllowRCust" tag="cfg5CCFVBrd" name="Bard Variant Features" abbrev="Bard Variant Features"/>
    </thing>
  <thing id="cBrd5CXSplLst" name="Expanded Spell List" description="<Specific to the Bard, needs coding>" compset="CustomSpec" uniqueness="unique">
    <tag group="AllowRCust" tag="cfg5CCFVBrd"/>
    <tag group="Helper" tag="SpecUp"/>
    </thing>
 
Thanks Fenris, I'll try to use what you did for the Sorcerer.

As you can probably tell, it's what I am currently playing so I tend to scratch those itches first.

As far as combining it into a single source, that's probably the best choice and I bow to your knowledge. I was just happy I figured out how to make subheadings and selectable options in Configure Hero. :D
 
As you can probably tell, it's what I am currently playing so I tend to scratch those itches first.

Totally understandable. The way I figure it, we're putting in the work so we deserve to be a little selfish with our priorities. For instance, I've yet to see anyone asking for the sidekicks from the Essentials Kit. But I needed them, so I programmed them.

Getting back on topic, I've taken your code and am about 90% done with a template that provides it for each class. I'll be testing it with the Bard first.

I'll leave the sorcerer alone for now, since you've got it covered. Just send me whatever you have when you're happy with it!

One note: I pictured Proficiency Versatility and Spell Versatility as only needing 1 ability that can be selected by all relevant classes. So instead of needing to make a new copy for each class, just modify the one I made to also be available to your Sorcerer Configurable.
 
Not to necro the thread, but I had a realization about this. I did something similar with the Ravnica guild backgrounds. When chosen, they add a specific set of spells to your overall spell list. Am I crazy, or could this code do the trick as well? It certainly works when added as part of a background.

Code:
  <thing id="ab5CGGRAzSpl" name="Azorius Spellcasting" description="Azorius spellcasters blah blah blah" compset="Ability">
    <tag group="Helper" tag="ShowSpec" name="Show Spec" abbrev="Show Spec"/>
    <tag group="ClsAllowSp" tag="sp5CFriend"/>
    <tag group="ClsAllowSp" tag="spMessage"/>
    <tag group="ClsAllowSp" tag="spCommand"/>
    <tag group="ClsAllowSp" tag="sp5CEnsnaS"/>
    <tag group="ClsAllowSp" tag="spArcaLock"/>
    <tag group="ClsAllowSp" tag="spCalmEmot"/>
    <tag group="ClsAllowSp" tag="spHoldPers"/>
    <tag group="ClsAllowSp" tag="spClairvoy"/>
    <tag group="ClsAllowSp" tag="sp5CCounte"/>
    <tag group="ClsAllowSp" tag="sp5CCompul"/>
    <tag group="ClsAllowSp" tag="spDivinati"/>
    <tag group="ClsAllowSp" tag="spDomiPers"/>
    <tag group="AbilFunc" tag="Background"/>
    <eval phase="First">foreach pick in hero from Class
    perform eachpick.pushtags[ClsAllowSp.?]
    nexteach</eval>
    </thing>

You'd need to modify the eval script to be specific to that class. Other than that, it seems sensical.
 
Last edited:
Seems so. Guess there is more than one way to skin a cat (as the saying goes, I would never condone violence towards animals except in an RPG)
 
I've programmed out the expanded spell lists my way and it's working really nicely. However, I don't think my way is going to work for the Eldritch Knight, Arcane Trickster, Arcana Cleric, etc. that basically borrow spell lists from other classes.

I haven't tested my way for those subclasses, but I'm going to give both mine and yours a shot and see what works.
 
To close this out, here's the final code for the Sorcerer's expanded spell list that was in Release 2.4 yesterday:
Code:
  <thing id="abSor5CXSpLst" name="Expanded Sorcerer Spell List" description="This feature adds the {i}Demiplane{/i}, {i}Fire Shield{/i}, {i}Flame Blade{/i}, {i}Flaming Sphere{/i}, {i}Flesh to Stone{/i}, {i}Foresight{/i}, {i}Grease{/i}, {i}Primal Savagery{/i}, {i}Protection from Evil and Good{/i}, and {i}Vampiric Touch{/i} spells to the Sorcerer&apos;s spell list." compset="Ability">
    <tag group="AllowRCust" tag="cfg5CCFVSor"/>
    <tag group="Helper" tag="Primary"/>
    <tag group="Helper" tag="SpecUp"/>
    <tag group="ClsAllowSp" tag="spDemiplan"/>
    <tag group="ClsAllowSp" tag="spFireShie"/>
    <tag group="ClsAllowSp" tag="spFlamBlad"/>
    <tag group="ClsAllowSp" tag="spFlamSphe"/>
    <tag group="ClsAllowSp" tag="spFlesSton"/>
    <tag group="ClsAllowSp" tag="spForesigh"/>
    <tag group="ClsAllowSp" tag="spGrease"/>
    <tag group="ClsAllowSp" tag="sp5CXGEPrS"/>
    <tag group="ClsAllowSp" tag="spProtGoEv"/>
    <tag group="ClsAllowSp" tag="spVampTouc"/>
    <eval phase="First"><![CDATA[
doneif (tagis[Helper.Disable] <> 0)
foreach pick in hero from Class where "Classes.Sorcerer"
perform eachpick.pushtags[ClsAllowSp.?]
    nexteach]]></eval>
    <exprreq message="Requires Sorcerer level 1"><![CDATA[#levelcount[Sorcerer] >= 1]]></exprreq>
    </thing>

The "AllowRCust" tag makes this feature selectable in the "cfg5CCFVSor" configurable. The "Primary" helper means it's a Primary ability in that configurable, and the "SpecUp" helper means this is an upgrade that shouldn't show up in the hero's specials list.

The "ClsAllowSp" tags normally would be on a class thing. They're there to say that the spell whose ID is in the tag is allowed by this class.

The eval script goes through and says that for every class on the hero that has the "Classes.Sorcerer" tag (so only the Sorcerer, basically), push the ClsAllowSp tags from this thing to that class. So it takes those tags from this ability, and shoves them onto the class, thereby making all those spells available.

The exprreq is just making sure the hero has a level in Sorcerer.

This worked for almost everything, including the Eldritch Knight and Arcane Trickster. For those, I had the foreach look for Classes.Fighter and Classes.Rogue, respectively.

But I did end up using wynlyndd's code for the Divine Soul Sorcerer, to make sure all the new Cleric spells ended up properly on that subclass's spell list. So it was definitely needed.

Code:
<thing id="abSor5CXSpLDv" name="Expanded Divine Soul Sorcerer Spell List" description="This feature adds the {i}Aura of Life{/i}, {i}Aura of Purity{/i}, {i}Aura of Vitality{/i}, {i}Branding Smite{/i}, {i}Cause Fear{/i}, {i}Power Word Heal{/i}, {i}Skill Empowerment{/i}, {i}Wall of Light{/i}, and {i}Wrathful Smite{/i} spells to the Cleric/Divine Soul Sorcerer&apos;s spell list." compset="Ability">
    <tag group="AllowRCust" tag="cfg5CCFVSor"/>
    <tag group="Helper" tag="Primary"/>
    <tag group="Helper" tag="SpecUp"/>
    <eval phase="Final" priority="21000"><![CDATA[doneif (tagis[Helper.Disable] <> 0)
~these vars will hold the tag expressions for spells and cantrips
var spell as string
var known as string 
var first as string
var second as string
var third as string
var fourth as string
var fifth as string
var ninth as string
~ fetch spell expressions
spell = hero.childfound[cHelpSor].field[cSpKnoExpr].text 
known = hero.childfound[cHelpSor].field[cSpellExpr].text 
~ these receive the extra spells for each circle
first = " | thingid.sp5CXGECaF | thingid.sp5CWrathS "
second = " | thingid.sp5CBrandS "
third = " | thingid.sp5CAurVit "
fourth = " | thingid.sp5CAurLif | thingid.sp5CAurPur "
fifth = " | thingid.sp5CXGESkE | thingid.sp5CXGEWaL "
ninth = " | thingid.sp5CPwrWH "
spell &= first
known &= first
spell &= second
known &= second
spell &= third
known &= third
spell &= fourth
known &= fourth
spell &= fifth
known &= fifth
spell &= ninth
known &= ninth
hero.childfound[cHelpSor].field[cSpKnoExpr].text = spell
hero.childfound[cHelpSor].field[cSpellExpr].text = known
hero.childfound[cHelpSor].field[cSplBkExpr].text = known]]></eval>
    <pickreq thing="c5CSorDiSo"/>
    </thing>
 
Back
Top