Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Thinking About] 5E Spell Management System Using Character Specific Rollable Tables

1410362301

Edited 1410362356
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
spellbooks = function() { //Checks for and Creates a Rollable Table as a Spell List checkCreateSpellListTable(); //Finds all the Classes a Character has and builds a spell list. getCurrentSpellList(); //builds all the spells as items on the tables buildTableItems(); //Deprecates extra spells deprecateSpells(); }; --------------------------------------------------------------------------------------------- checkCreateSpellListTable(); Creates tables with the characters name and the characters ID concatenated (character names sometimes change.) --------------------------------------------------------------------------------------------- getCurrentSpellList(); Based on the 5E sheet, grabs all classes with a Level value &gt; 0 and builds a spell list from an array of all 5E spells. --------------------------------------------------------------------------------------------- buildTableItems(); Based on the class or classes add spells as items (in this example Friar-Tuck is a cleric.) --------------------------------------------------------------------------------------------- deprecateSpells(); Deprecates spells if the class is abandoned... (in this example Friar-Tuck gave up all cleric levels and became a barbarian.) Why use tables at all? The table are character specific a lists of all spells available by class(s) to the character; however the "weight" of the item can be used as a UI to control which spells the player should have actually have access to. Spell exists on the list with the weight of "0" the character could have the spell based on class(s) but does not have access to the spell. Spell exists on the list with the weight of "1" the character could have the spell based on class(s) AND does have access to the spell. Parity between these player lists and the record sheets can be maintained and new spells automatically added to the record-sheet complete with Power Card friendly macros. Script as it exist so far (only some spells are provided as an example.) var roll20API = roll20API || {}; roll20API.AbjurationURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522736/JVPsnlD_KzqIIVRQSy5Htg/thumb.png?1410310482" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522736/JVPsnlD_KzqIIVRQSy5Htg/thumb.png?1410310482</a>"; roll20API.ConjurationURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522756/cCOJ2oR8b81-Tec2fI0-Ow/thumb.png?1410310524" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522756/cCOJ2oR8b81-Tec2fI0-Ow/thumb.png?1410310524</a>"; roll20API.DivinationURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522755/0Ml9HschVlL1OJ0xoqiH2Q/thumb.png?1410310524" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522755/0Ml9HschVlL1OJ0xoqiH2Q/thumb.png?1410310524</a>"; roll20API.EnchantmentURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522758/MHE0WbCtMuN37Ay7VtMOyw/thumb.png?1410310526" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522758/MHE0WbCtMuN37Ay7VtMOyw/thumb.png?1410310526</a>"; roll20API.EvocationURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522753/EtKxP8f9QiVvHW56b2ssGA/thumb.png?1410310523" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522753/EtKxP8f9QiVvHW56b2ssGA/thumb.png?1410310523</a>"; roll20API.IllusionURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522751/BpLZxpc8udFoPoP1z2RD0Q/thumb.png?1410310523" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522751/BpLZxpc8udFoPoP1z2RD0Q/thumb.png?1410310523</a>"; roll20API.NecromancyURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522752/mKpQqwceP8KcpV8E9oh-zA/thumb.png?1410310523" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522752/mKpQqwceP8KcpV8E9oh-zA/thumb.png?1410310523</a>"; roll20API.TransmutationURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5522754/O8SGFsdAgkca9g95Oqjyww/thumb.png?1410310523" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522754/O8SGFsdAgkca9g95Oqjyww/thumb.png?1410310523</a>"; roll20API.DeprecatedURL = "<a href="https://s3.amazonaws.com/files.d20.io/images/5527555/1ukVm1LJhbvtXrHtD8-vrg/thumb.png?1410358340" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5527555/1ukVm1LJhbvtXrHtD8-vrg/thumb.png?1410358340</a>"; roll20API.spellbooks = [ {Class: "Barbarian", Short: "BN", Name: "barbarian_level"}, {Class: "Bard", Short: "BD", Name: "bard_level"}, {Class: "Cleric", Short: "CC", Name: "cleric_level"}, {Class: "Druid", Short: "DD", Name: "druid_level"}, {Class: "Fighter", Short: "FR", Name: "fighter_level"}, {Class: "Monk", Short: "MK", Name: "monk_level"}, {Class: "Paladin", Short: "PN", Name: "paladin_level"}, {Class: "Ranger", Short: "RR", Name: "ranger_level"}, {Class: "Rogue", Short: "RE", Name: "rogue_level"}, {Class: "Sorcerer", Short: "SR", Name: "sorcerer_level"}, {Class: "Warlock", Short: "WK", Name: "warlock_level"}, {Class: "Wizard", Short: "WD", Name: "wizard_level"} ]; roll20API.spelllist = [ { Call:"acid_splash", Name:"Acid Splash", Class:"Sorcerer,Wizard", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Conjuration", component:"V,S", Material:"", Range:"60 feet", CastingTime:"1 action", Duration:"Instantaneous", Description:"I hurl a bubble of acid. Choose one creature within range, or choose two creatures within range that are within 5 feet of each other. A target must succeed on a Dexterity saving throw or take 1d6 acid damage.", HigherLevel:"This spell's damage increases by 1d6 when I reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).", Effect:"I target **@{target|t1|token_name}**, and **@{target|t2|token_name}** who is within 5 feet. --Make a DEX save|vs [[8+@{selected|spellcastingability}+@{selected|PB}]] --Failed Save|[[1d6+{floor((@{level})/5),1}kl1*1d6+{floor((@{level})/11),1}kl1*1d6+{floor((@{level})/17),1}kl1*1d6]] acid damage.", PowerCard:"I hurl a bubble of acid. --text1|^*Choose one creature within range, or choose two creatures within range that are within 5 feet of each other. --text2|^*A target must succeed on a **Dexterity Saving Throw** or take **1d6 Acid Damage**." }, { Call:"blade_ward", Name:"Blade Ward", Class:"Bard,Sorcerer,Warlock,Wizard", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Abjuration", component:"V,S", Material:"", Range:"Self", CastingTime:"1 action", Duration:"1 round", Description:"I extend my hand and trace a sigil of warding in the air. Until the end of my next turn, I have resistance against bludgeoning, piercing, and slashing damage dealt by weapon attacks.", HigherLevel:"", Effect:"", PowerCard:"I extend my hand and trace a sigil of warding in the air. --text1|^*Until the end of my next turn, I have **Resistance** against Bludgeoning, Piercing, and Slashing damage dealt by weapon attacks." }, { Call:"chill_touch", Name:"Chill Touch", Class:"Sorcerer,Warlock,Wizard", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Necromancy", component:"V,S", Material:"", Range:"120 feet", CastingTime:"1 action", Duration:"1 round", Description:"I create a ghostly, skeletal hand in the space of a creature within range. I make a ranged spell attack against the creature to assail it with the chill of the grave. On a hit, the target takes 1d8 necrotic damage, and it can't regain hit points until the start of my next turn. Until then, the hand clings to the target. If I hit an undead target, it also has disadvantage on attack rolls against me until the end of my next turn. ", HigherLevel:"The spell’s damage increases by 1d8 when I reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).", Effect:"My conjured ghostly hand attacks **@{target|t1|token_name}**. --Ranged Attack|[[1d20+@{selected|spellcastingability}+@{selected|PB}]]|[[1d20+@{selected|spellcastingability}+@{selected|PB}]] vs. AC --On Hit|[[1d8+{floor((@{level})/5),1}kl1*1d8+{floor((@{level})/11),1}kl1*1d8+{floor((@{level})/17),1}kl1*1d8]] necrotic damage, and it can't regain hit points until the start of my next turn.", PowerCard:"I create a ghostly, skeletal hand in the space of a creature within range. --text1|^*I make a **Ranged Spell Attack** against the creature to assail it with the chill of the grave. --text2|^*On a hit, the target takes **1d8 Necrotic Damage**, and it can't regain hit points until the start of my next turn. --text3|^*Until then, the hand clings to the target. If I hit an **Undead** target, it also has **Disadvantage** on attack rolls against me until the end of my next turn." }, { Call:"dancing_lights", Name:"Dancing Lights", Class:"Bard,Sorcerer,Wizard", SubClass:"", Ritual:"N", Concentration:"Y", Level:"0", School:"Evocation", component:"V,S,M", Material:"(a bit of phosphorus or wychwood, or a glowworm)", Range:"120 feet", CastingTime:"1 action", Duration:"Concentration, up to 1 minute", Description:"I create up to four torch-sized lights within range, making them appear as torches, lanterns, or glowing orbs that hover in the air for the duration. I can also combine the four lights into one glowing vaguely humanoid form of medium size. Whichever form I choose, each light sheds dim light in a 10-foot radius. As a bonus action on my turn, I can move the lights up to 60 feet to a new spot within range. A light must be within 20 feet of another light created by this spell, and a light winks out if it extends the spell's range.", HigherLevel:"", Effect:"", PowerCard:"I create up to four torch-sized lights within range, making them appear as torches, lanterns, or glowing orbs that hover in the air for the duration. --text1|^*I can also combine the four lights into one glowing vaguely humanoid form of medium size. --text2|^*Whichever form I choose, each light sheds dim light in a 10-foot radius. As a bonus action on my turn, I can move the lights up to 60 feet to a new spot within range. --text3|^*A light must be within 20 feet of another light created by this spell, and a light winks out if it extends the spell's range." }, { Call:"druidcraft", Name:"Druidcraft", Class:"Druid", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Transmutation", component:"V,S", Material:"", Range:"30 feet", CastingTime:"1 action", Duration:"Instantaneous", Description:"Whispering to the spirits of nature, I create one of the following effects within range: • I create a tiny, harmless sensory effect that predicts what the weather will be at my location for the next 24 hours. The effect might manifest as a golden orb for clear skies, a cloud for rain, falling snowflakes for snow, and so on. This effect persists for 1 round. • I instantly make a flower blossom, a seed pod open, or a leaf bud bloom. • I create an instantaneous, harmless sensory effect, such as falling leaves, a puff of wind, the sound of a small animal, or the faint odor of skunk. The effect must fit in a 5-foot cube. • I instantly light or snuff out a candle, a torch, or a small campfire.", HigherLevel:"", Effect:"", PowerCard:"Whispering to the spirits of nature, I create one of the following effects within range: --• 1|I create a tiny, harmless sensory effect that predicts what the weather will be at my location for the next 24 hours. The effect might manifest as a golden orb for clear skies, a cloud for rain, falling snowflakes for snow, and so on. This effect persists for 1 round. --• 2|I instantly make a flower blossom, a seed pod open, or a leaf bud bloom. --• 3|I create an instantaneous, harmless sensory effect, such as falling leaves, a puff of wind, the sound of a small animal, or the faint odor of skunk. The effect must fit in a 5-foot cube. --• 4|I instantly light or snuff out a candle, a torch, or a small campfire." }, { Call:"eldritch_blast", Name:"Eldritch Blast", Class:"Warlock", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Evocation", component:"V,S", Material:"", Range:"120 feet", CastingTime:"1 action", Duration:"Instantaneous", Description:"A beam of crackling energy streaks toward a creature within range. I make a ranged spell attack against the target. On a hit, the target takes 1d10 force damage.", HigherLevel:"The spell creates more than one beam when I reach higher levels, two beams at 5th level, three beams at 11th level, and four beams at 17th level. I can direct the beams at the same target or at different ones. I make a seperate attack roll for each beam.", Effect:"Crackling energy streaks toward **@{target|t1|token_name}**. --Ranged|[[1d20+@{selected|spellcastingability}+@{selected|PB}]]|[[1d20+@{selected|spellcastingability}+@{selected|PB}]] vs AC --(5th) Ranged:|[[{floor((@{level})/5),1}kl1*1d20+{floor((@{level})/5),1}kl1*(@{selected|spellcastingability}+@{selected|PB})}]]|[[{floor((@{level})/5),1}kl1*1d20+{floor((@{level})/5),1}kl1*(@{selected|spellcastingability}+@{selected|PB})}]] --(11th) Ranged|[[{floor((@{level})/11),1}kl1*1d20+{floor((@{level})/11),1}kl1*(@{selected|spellcastingability}+@{selected|PB})}]]|[[{floor((@{level})/11),1}kl1*1d20+{floor((@{level})/11),1}kl1*(@{selected|spellcastingability}+@{selected|PB})}]] --(17th) Ranged|[[{floor((@{level})/17),1}kl1*1d20+{floor((@{level})/17),1}kl1*(@{selected|spellcastingability}+@{selected|PB})}]]|[[{floor((@{level})/17),1}kl1*1d20+{floor((@{level})/17),1}kl1*(@{selected|spellcastingability}+@{selected|PB})}]] --On Hit|[[1d10]] force damage. ", PowerCard:"A beam of crackling energy streaks toward a creature within range. --text1|^*I make a **Ranged Spell Attack** against the target. --text2|^*On a hit, the target takes **1d10 Force Damage**." }, { Call:"fire_bolt", Name:"Fire Bolt", Class:"Sorcerer,Wizard", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Evocation", component:"V,S", Material:"", Range:"120 feet", CastingTime:"1 action", Duration:"Instantaneous", Description:"I hurl a mote of fire at a creature or object within range. I make a ranged spell attack against the target. On a hit, the target takes 1d10 fire damage. A flammable object hit by this spell ignites if it isn’t being worn or carried.", HigherLevel:"This spell’s damage increases by 1d10 when I reach 5th level (2d10), 11th level (3d10), and 17th level (4d10).", Effect:"I hurl a mote of fire at **@{target|t1|token_name}**. --Ranged Attack|[[1d20+@{selected|spellcastingability}+@{selected|PB}]]|[[1d20+@{selected|spellcastingability}+@{selected|PB}]] vs. AC. --On Hit|[[1d10+{floor((@{level})/5),1}kl1*1d10+{floor((@{level})/11),1}kl1*1d10+{floor((@{level})/17),1}kl1*1d10]] fire damage. --^Effect-|A flammable object hit by this spell ignites if it isn’t being worn or carried.", PowerCard:"I hurl a mote of fire at a creature or object within range. --text1|^*I make a **Ranged Spell Attack** against the target. On a hit, the target takes 1d10 fire damage. --text2|^*A flammable object hit by this spell ignites if it isn’t being worn or carried." }, { Call:"friends", Name:"Friends", Class:"Bard,Sorcerer,Warlock,Wizard", SubClass:"", Ritual:"N", Concentration:"Y", Level:"0", School:"Enchantment", component:"S,M", Material:"(a small amount of makeup applied to the face as this spell is cast)", Range:"Self", CastingTime:"1 action", Duration:"Concentration, up to 1 minute", Description:"For the duration, I have advantage on all Charisma checks directed at one creature of my choice that isn't hostile towards me. When the spell ends, the creature realizes that I used magic to influence its mood and becomes hostile towards me. A creature prone to violence might attack me. Another creature might seek retribution in other ways (at the Dm's discretion).", HigherLevel:"", Effect:"", PowerCard:"For the duration, I have **Advantage** on all Charisma checks directed at one creature of my choice that isn't hostile towards me. --text1|^*When the spell ends, the creature realizes that I used magic to influence its mood and becomes hostile towards me. A creature prone to violence might attack me. Another creature might seek retribution in other ways (at the Dm's discretion)." }, { Call:"guidance", Name:"Guidance", Class:"Cleric,Druid", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Divination", component:"V,S", Material:"", Range:"Touch", CastingTime:"1 action", Duration:"Concentration, up to 1 minute", Description:"I touch one willing creature. Once before the spell ends, the target can roll a d4 and add the number rolled to one ability check of its choice. It can roll the die before or after making the ability check. The spell then ends.", HigherLevel:"", Effect:"I touch **@target|t1|token_name}**.", PowerCard:"I touch one willing creature. Once before the spell ends, the target can roll a **1d4** and add the number rolled to one ability check of its choice. --text1|^*It can roll the die before or after making the ability check. The spell then ends." }, { Call:"light", Name:"Light", Class:"Bard,Cleric,Sorcerer,Wizard", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Evocation", component:"V,M", Material:"(a firefly or phosphorus moss)", Range:"Touch", CastingTime:"1 action", Duration:"1 hour", Description:"I touch one object that is no larger than 10 feet in any dimension. Until the spell ends, the object sheds bright light in a 20-foot radius and dim light for an additional 20 feet. The light can be colored as I like. Completely covering the object with something opaque blocks the light. The spell ends if I cast it again or dismiss it as an action. If I target an object held or worn by a hostile creature, that creature must succeed on a Dexterity saving throw to avoid the spell.", HigherLevel:"", Effect:"If object held by hostile creature, they must~ --Make a DEX save|vs [[8+@{selected|spellcastingability}+@{selected|PB}]] --Failed Save|The object is lit.", PowerCard:"I touch one object that is no larger than 10 feet in any dimension. --text1|^*Until the spell ends, the object sheds **Bright Light** in a **20-Foot Radius** and dim light for an additional 20 feet. The light can be colored as I like. --text2|^*Completely covering the object with something opaque blocks the light. --text3|^*The spell ends if I cast it again or dismiss it as an action. --text4|^*If I target an object held or worn by a hostile creature, that creature must succeed on a **Dexterity Saving Throw** to avoid the spell." }, { Call:"mage_hand", Name:"Mage Hand", Class:"Bard,Sorcerer,Warlock,Wizard", SubClass:"", Ritual:"N", Concentration:"Y", Level:"0", School:"Conjuration", component:"V,S", Material:"", Range:"30 feet", CastingTime:"1 action", Duration:"1 minute", Description:"A spectral, floating hand appears at a point I choose within range. The hand lasts for the duration or until I dismiss it as an action. The hand vanishes if it is ever more than 30 feet away from me or if I cast this spell again. I can use my action to control the hand. I can use the hand to manipulate an object, open an unlocked door or container, stow or retrieve an item from an open container, or pour the contents out of a vial. I can move the hand up to 30 feet each time I use it. The hand can’t attack, activate magic items, or carry more than 10 pounds.", HigherLevel:"", Effect:"", PowerCard:"A spectral, floating hand appears at a point I choose within range. --text1|^*The hand lasts for the duration or until I dismiss it as an action. The hand vanishes if it is ever more than 30 feet away from me or if I cast this spell again. --text2|^*I can use my action to control the hand. I can use the hand to manipulate an object, open an unlocked door or container, stow or retrieve an item from an open container, or pour the contents out of a vial. I can move the hand up to 30 feet each time I use it. --text3|^*The hand can’t attack, activate magic items, or carry more than 10 pounds." } ]; on("chat:message", function (msg) { // Exit if not an api command if (msg.type != "api") return; // Get the API Chat Command msg.who = msg.who.replace(" (GM)", ""); msg.content = msg.content.replace("(GM) ", ""); var command = msg.content.split(" ", 1); if(command == "!spellbooks") { spellbooks(); }; }); spellbooks = function() { //Checks for and Creates a Rollable Table as a Spell List checkCreateSpellListTable(); //Finds all the Classes a Character has and builds a spell list. getCurrentSpellList(); //builds all the spells as items on the tables buildTableItems(); //Deprecates extra spells var characters = findObjs({type: "character"}); var rollabletables = findObjs({type: "rollabletable"}); _.each(characters, function(indexCharacters) { if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(rollabletables, function(indexrollabletables) { var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){ var tableItems = findObjs({ _type: "tableitem", _rollabletableid: indexrollabletables.get("_id")}); if(tableItems.length != 0){ _.each(tableItems, function(indextListedSpells) { var validSpell = false; _.each(roll20API.spellListClass, function(indexPlayerSpells) { var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; if(indextListedSpells.get("name") == itemName){validSpell = true}; }); if(validSpell == false){ var itemID = indextListedSpells.get("_id") findObjs({ _type: "tableitem", _id: itemID })[0].set({name: "999_deprecated"}); }; }); }; }; }); }; }); }; buildTableItems = function() { var characters = findObjs({type: "character"}); var rollabletables = findObjs({type: "rollabletable"}); _.each(characters, function(indexCharacters) { if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(rollabletables, function(indexrollabletables) { var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){ var tableItems = findObjs({ _type: "tableitem", _rollabletableid: indexrollabletables.get("_id")}); if(tableItems.length != 0){ _.each(roll20API.spellListClass, function(indexPlayerSpells) { var spellFoundAsItem = false; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; _.each(tableItems, function(indextListedSpells) { if(indextListedSpells.get("name") == itemName){spellFoundAsItem = true}; }); if(spellFoundAsItem == false){ var avatarURL = ""; if(indexPlayerSpells.School == "Abjuration"){avatarURL = roll20API.AbjurationURL}; if(indexPlayerSpells.School == "Conjuration"){avatarURL = roll20API.ConjurationURL}; if(indexPlayerSpells.School == "Divination"){avatarURL = roll20API.DivinationURL}; if(indexPlayerSpells.School == "Enchantment"){avatarURL = roll20API.EnchantmentURL}; if(indexPlayerSpells.School == "Evocation"){avatarURL = roll20API.EvocationURL}; if(indexPlayerSpells.School == "Illusion"){avatarURL = roll20API.IllusionURL}; if(indexPlayerSpells.School == "Necromancy"){avatarURL = roll20API.NecromancyURL}; if(indexPlayerSpells.School == "Transmutation"){avatarURL = roll20API.TransmutationURL}; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; if(characterID == indexPlayerSpells.ID){ createObj("tableitem", { _rollabletableid: indexrollabletables.get("_id"), name: itemName, avatar: avatarURL, weight: 0, }); }; }; }); }else{ _.each(roll20API.spellListClass, function(indexPlayerSpells) { var avatarURL = ""; if(indexPlayerSpells.School == "Abjuration"){avatarURL = roll20API.AbjurationURL}; if(indexPlayerSpells.School == "Conjuration"){avatarURL = roll20API.ConjurationURL}; if(indexPlayerSpells.School == "Divination"){avatarURL = roll20API.DivinationURL}; if(indexPlayerSpells.School == "Enchantment"){avatarURL = roll20API.EnchantmentURL}; if(indexPlayerSpells.School == "Evocation"){avatarURL = roll20API.EvocationURL}; if(indexPlayerSpells.School == "Illusion"){avatarURL = roll20API.IllusionURL}; if(indexPlayerSpells.School == "Necromancy"){avatarURL = roll20API.NecromancyURL}; if(indexPlayerSpells.School == "Transmutation"){avatarURL = roll20API.TransmutationURL}; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; if(characterID == indexPlayerSpells.ID){ createObj("tableitem", { _rollabletableid: indexrollabletables.get("_id"), name: itemName, avatar: avatarURL, weight: 0, }); }; }); }; }; }); }; }); }; getCurrentSpellList = function() { var characters = findObjs({type: "character"}); var classString; roll20API.spellListClass = ""; roll20API.spellListClass = []; _.each(characters, function(indexCharacters) { classString = ""; if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(roll20API.spellbooks, function(indexBooks) { var characterSheet = getObj("character", characterID); var CurrentLevel = getAttrByName(characterSheet.id, indexBooks.Name) * 1; if(CurrentLevel &gt; 0){classString += indexBooks.Class + ","}; }); }; classString = classString.substring(0, classString.length - 1); if(classString.length &gt; 0){ _.each(roll20API.spelllist, function(indexSpells) { var spellFound = false; var arr1 = indexSpells.Class.split(","); var arr2 = classString.split(","); for (var i = 0; i &lt; arr1.length; i++) { for (var j = 0; j &lt; arr2.length; j++) { if (arr1[i] === arr2[j]) { if(spellFound == false){ roll20API.spellListClass.push({ID: characterID, Level: indexSpells.Level, School: indexSpells.School, Call: indexSpells.Call}); spellFound = true; }; }; }; }; }); }; }); }; checkCreateSpellListTable = function() { var rollabletables = findObjs({type: "rollabletable"}); var characters = findObjs({type: "character"}); _.each(characters, function(indexCharacters) { var characterID = indexCharacters.get("_id"); var characterName = indexCharacters.get("name").replace(/[^a-zA-Z ]/g, ""); characterName = characterName.split(' ').join('-'); if(indexCharacters.get("controlledby") != ""){ var tableFound = false; var tableID = ""; _.each(rollabletables, function(indexrollabletables) { if(tableFound == false){ var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){tableFound = true; tableID = indexrollabletables.get("_id");}; }; }); if(tableFound == false){ var nameToUse = characterName+"{"+characterID+"}"; createObj("rollabletable", {name: nameToUse, showplayers: false,}); tableID = findObjs( {_type: "rollabletable", name: nameToUse})[0].get("_id"); tableFound = true; }; }; }); }; Not sure this is the best way to go..... What do you think? Right track? Wrong Track? Once the tables are "set" and controlled... should be real easy to add/predicate: Spells in the record Sheet Standard Powercard Token Actions Query the tables for spells or such.
1410377908
Paul S.
Sheet Author
API Scripter
If I'm understanding what you're wanting to do correctly, it seems like an amazing amount of work on your end. You are basically going to have to type in every spell ... every spell. And as 5e expands, that list will have to be expanded and edited (as FAQs and errata are released, etc.). There is no wrong way to go. But to me, this is the path of most work for what benefit? The 5e character sheets already have a nice spell section which allows PCs to enter in their spells, effects, etc... This shifts the burden onto the PC of course. Couple the existing Character Sheet with a tracker API (see Aaron's Ammo Tracker that can be mod'd to be a universal tracker as I've done) and you've got an automated method of accounting for spell slots. Couple those two things with the Powercard API and you can have some fancy formatting in the text box for spells. Add a Table for Spell Effects that has preset icons for spells and I think you'd be good to go. Don't get me wrong - what you are trying to accomplish is admirable, but I don't envy you the amount of work entailed.
1410380249
Actoba
Pro
Sheet Author
If you do this you'd almost certainly not be able to release or distribute it in anyway...that may not have been your intention anyway but thought i'd better post a heads up anyway. There have already been cease and desist notices served on other sites/resources that have contained full spell lists, or anything beyond the spell names or the data available in the Basic pdf.
1410380537
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
I have the spells.... just tweaking the formatting in excel.... breaking out a few more fields. I wont post the file here.... But as of right now they are all put in an array that looks like this: { Call:"acid_splash", Name:"Acid Splash", Class:"Sorcerer,Wizard", SubClass:"", Ritual:"N", Concentration:"N", Level:"0", School:"Conjuration", Component:"V,S", Material:"", Range:"60 feet", CastingTime:"1 action", Duration:"Instantaneous", Description:"I hurl a bubble of acid. Choose one creature within range, or choose two creatures within range that are within 5 feet of each other. A target must succeed on a Dexterity saving throw or take 1d6 acid damage.", HigherLevel:"This spell's damage increases by 1d6 when I reach 5th level (2d6), 11th level (3d6), and 17th level (4d6).", Effect:"I target **@{target|t1|token_name}**, and **@{target|t2|token_name}** who is within 5 feet. --Make a DEX save|vs [[8+@{selected|spellcastingability}+@{selected|PB}]] --Failed Save|[[1d6+{floor((@{level})/5),1}kl1*1d6+{floor((@{level})/11),1}kl1*1d6+{floor((@{level})/17),1}kl1*1d6]] acid damage.", PowerCard:"I hurl a bubble of acid. --text1|^*Choose one creature within range, or choose two creatures within range that are within 5 feet of each other. --text2|^*A target must succeed on a **Dexterity Saving Throw** or take **1d6 Acid Damage**." }, Working to get an efficient sort, by name for the table item object: [ {"name":"999_deprecated","avatar":"<a href="https://s3.amazonaws.com/files.d20.io/images/5527555/1ukVm1LJhbvtXrHtD8-vrg/thumb.png?1410358340&quot;,&quot;weight&quot;:0,&quot;_type&quot;:&quot;tableitem&quot;,&quot;_id&quot;:&quot;-JWW9nCKAytqdCFBRpMD&quot;,&quot;_rollabletableid&quot;:&quot;-JWW9nC92UOzSWvYkHH-" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5527555/1ukVm1LJhbvtXrHtD8-vrg/thumb.png?1410358340","weight":0,"_type":"tableitem","_id":"-JWW9nCKAytqdCFBRpMD","_rollabletableid":"-JWW9nC92UOzSWvYkHH-</a>"}, {"name":"2_beast_sense","avatar":"<a href="https://s3.amazonaws.com/files.d20.io/images/5522755/0Ml9HschVlL1OJ0xoqiH2Q/thumb.png?1410310524&quot;,&quot;weight&quot;:0,&quot;_type&quot;:&quot;tableitem&quot;,&quot;_id&quot;:&quot;-JWW9vX-KUVDZUGJo31v&quot;,&quot;_rollabletableid&quot;:&quot;-JWW9nC92UOzSWvYkHH-" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/5522755/0Ml9HschVlL1OJ0xoqiH2Q/thumb.png?1410310524","weight":0,"_type":"tableitem","_id":"-JWW9vX-KUVDZUGJo31v","_rollabletableid":"-JWW9nC92UOzSWvYkHH-</a>"} ] That way each players table is sorted by level (123) then spell name (abc)...
1410399966
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
spellbooks = function() { //Checks for and Creates a Rollable Table as a Spell List arrayTest(); //Checks for and Creates a Rollable Table as a Spell List checkCreateSpellListTable(); //Finds all the Classes a Character has and builds a spell list. getCurrentSpellList(); //builds all the spells as items on the tables buildTableItems(); //Deprecates extra spells deprecateSpells(); //Clean up the sort of the table sortTable(); }; sortTable = function() { var characters = findObjs({type: "character"}); var rollabletables = findObjs({type: "rollabletable"}); _.each(characters, function(indexCharacters) { if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(rollabletables, function(indexrollabletables) { var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){ var tableItems = findObjs({ _type: "tableitem", _rollabletableid: indexrollabletables.get("_id")}) }; roll20API.tobesorted = [] _.each(tableItems, function(indextableItems) { roll20API.tobesorted.push({ name: indextableItems.get("name"), avatar: indextableItems.get("avatar"), weight: indextableItems.get("weight")}); }); sortedList = sortJSON(roll20API.tobesorted,"name"); var j=0; _.each(tableItems, function(indextableItems) { var itemID = indextableItems.get("_id") log(roll20API.tobesorted[j].name) findObjs({ _type: "tableitem", _id: itemID })[0].set({name: roll20API.tobesorted[j].name}); findObjs({ _type: "tableitem", _id: itemID })[0].set({avatar: roll20API.tobesorted[j].avatar}); findObjs({ _type: "tableitem", _id: itemID })[0].set({weight: roll20API.tobesorted[j].weight}); j++ }); }); }; }); }; function sortJSON(data, key) { return data.sort(function(a, b) { var x = a[key]; var y = b[key]; return ((x &lt; y) ? -1 : ((x &gt; y) ? 1 : 0)); }); }; deprecateSpells = function() { var characters = findObjs({type: "character"}); var rollabletables = findObjs({type: "rollabletable"}); _.each(characters, function(indexCharacters) { if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(rollabletables, function(indexrollabletables) { var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){ var tableItems = findObjs({ _type: "tableitem", _rollabletableid: indexrollabletables.get("_id")}); if(tableItems.length != 0){ _.each(tableItems, function(indextListedSpells) { var validSpell = false; _.each(roll20API.spellListClass, function(indexPlayerSpells) { var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; if(indextListedSpells.get("name") == itemName){validSpell = true}; }); if(validSpell == false){ var itemID = indextListedSpells.get("_id") findObjs({ _type: "tableitem", _id: itemID })[0].set({name: "999_deprecated"}); findObjs({ _type: "tableitem", _id: itemID })[0].set({avatar: roll20API.DeprecateURL}); }; }); }; }; }); }; }); }; buildTableItems = function() { var characters = findObjs({type: "character"}); var rollabletables = findObjs({type: "rollabletable"}); _.each(characters, function(indexCharacters) { if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(rollabletables, function(indexrollabletables) { var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){ var tableItems = findObjs({ _type: "tableitem", _rollabletableid: indexrollabletables.get("_id")}); if(tableItems.length != 0){ _.each(roll20API.spellListClass, function(indexPlayerSpells) { var spellFoundAsItem = false; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; _.each(tableItems, function(indextListedSpells) { if(indextListedSpells.get("name") == itemName){spellFoundAsItem = true}; }); if(spellFoundAsItem == false){ var checkFor999 = false; _.each(tableItems, function(indextListedSpells) { if(checkFor999 == false && indextListedSpells.get("name").substring(0, 3) == "999"){ checkFor999 = true; spellFoundAsItem = true; var itemID = indextListedSpells.get("_id") var avatarURL = ""; if(indexPlayerSpells.School == "Abjuration"){avatarURL = roll20API.AbjurationURL}; if(indexPlayerSpells.School == "Conjuration"){avatarURL = roll20API.ConjurationURL}; if(indexPlayerSpells.School == "Divination"){avatarURL = roll20API.DivinationURL}; if(indexPlayerSpells.School == "Enchantment"){avatarURL = roll20API.EnchantmentURL}; if(indexPlayerSpells.School == "Evocation"){avatarURL = roll20API.EvocationURL}; if(indexPlayerSpells.School == "Illusion"){avatarURL = roll20API.IllusionURL}; if(indexPlayerSpells.School == "Necromancy"){avatarURL = roll20API.NecromancyURL}; if(indexPlayerSpells.School == "Transmutation"){avatarURL = roll20API.TransmutationURL}; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; findObjs({ _type: "tableitem", _id: itemID })[0].set({name: itemName}); findObjs({ _type: "tableitem", _id: itemID })[0].set({avatar: avatarURL}) findObjs({ _type: "tableitem", _id: itemID })[0].set({weight: 0}) }; }); }; if(spellFoundAsItem == false){ var avatarURL = ""; if(indexPlayerSpells.School == "Abjuration"){avatarURL = roll20API.AbjurationURL}; if(indexPlayerSpells.School == "Conjuration"){avatarURL = roll20API.ConjurationURL}; if(indexPlayerSpells.School == "Divination"){avatarURL = roll20API.DivinationURL}; if(indexPlayerSpells.School == "Enchantment"){avatarURL = roll20API.EnchantmentURL}; if(indexPlayerSpells.School == "Evocation"){avatarURL = roll20API.EvocationURL}; if(indexPlayerSpells.School == "Illusion"){avatarURL = roll20API.IllusionURL}; if(indexPlayerSpells.School == "Necromancy"){avatarURL = roll20API.NecromancyURL}; if(indexPlayerSpells.School == "Transmutation"){avatarURL = roll20API.TransmutationURL}; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; if(characterID == indexPlayerSpells.ID){ marker = createObj("tableitem", { _rollabletableid: indexrollabletables.get("_id"), name: itemName, avatar: avatarURL, weight: 0, }); marker=fixNewObject(marker); }; }; }); }else{ _.each(roll20API.spellListClass, function(indexPlayerSpells) { var avatarURL = ""; if(indexPlayerSpells.School == "Abjuration"){avatarURL = roll20API.AbjurationURL}; if(indexPlayerSpells.School == "Conjuration"){avatarURL = roll20API.ConjurationURL}; if(indexPlayerSpells.School == "Divination"){avatarURL = roll20API.DivinationURL}; if(indexPlayerSpells.School == "Enchantment"){avatarURL = roll20API.EnchantmentURL}; if(indexPlayerSpells.School == "Evocation"){avatarURL = roll20API.EvocationURL}; if(indexPlayerSpells.School == "Illusion"){avatarURL = roll20API.IllusionURL}; if(indexPlayerSpells.School == "Necromancy"){avatarURL = roll20API.NecromancyURL}; if(indexPlayerSpells.School == "Transmutation"){avatarURL = roll20API.TransmutationURL}; var itemName = indexPlayerSpells.Level + "_" + indexPlayerSpells.Call; if(characterID == indexPlayerSpells.ID){ marker = createObj("tableitem", { _rollabletableid: indexrollabletables.get("_id"), name: itemName, avatar: avatarURL, weight: 0, }); marker=fixNewObject(marker); }; }); }; }; }); }; }); }; getCurrentSpellList = function() { var characters = findObjs({type: "character"}); var classString; roll20API.spellListClass = ""; roll20API.spellListClass = []; _.each(characters, function(indexCharacters) { classString = ""; if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(roll20API.spellbooks, function(indexBooks) { var characterSheet = getObj("character", characterID); var CurrentLevel = getAttrByName(characterSheet.id, indexBooks.Name) * 1; if(CurrentLevel &gt; 0){classString += indexBooks.Class + ","}; }); }; classString = classString.substring(0, classString.length - 1); if(classString.length &gt; 0){ _.each(roll20API.spelllist, function(indexSpells) { var spellFound = false; var arr1 = indexSpells.Class.split(","); var arr2 = classString.split(","); for (var i = 0; i &lt; arr1.length; i++) { for (var j = 0; j &lt; arr2.length; j++) { if (arr1[i] === arr2[j]) { if(spellFound == false){ roll20API.spellListClass.push({ID: characterID, Level: indexSpells.Level, School: indexSpells.School, Call: indexSpells.Call}); spellFound = true; }; }; }; }; }); }; }); }; checkCreateSpellListTable = function() { var rollabletables = findObjs({type: "rollabletable"}); var characters = findObjs({type: "character"}); _.each(characters, function(indexCharacters) { var characterID = indexCharacters.get("_id"); var characterName = indexCharacters.get("name").replace(/[^a-zA-Z ]/g, ""); characterName = characterName.split(' ').join('-'); if(indexCharacters.get("controlledby") != ""){ var tableFound = false; var tableID = ""; _.each(rollabletables, function(indexrollabletables) { if(tableFound == false){ var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){tableFound = true; tableID = indexrollabletables.get("_id");}; }; }); if(tableFound == false){ var nameToUse = characterName+"{"+characterID+"}"; createObj("rollabletable", {name: nameToUse, showplayers: false,}); tableID = findObjs( {_type: "rollabletable", name: nameToUse})[0].get("_id"); tableFound = true; }; }; }); };
1410400622
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
The above completes the rollable table. For each player (in their own table) it will list all possible spells based on class(es) and can be updated when new class are added (or in the rare case removed.) Lots of things could be done from this point.... setting the weight to add to a spell book or mark as prepared (or unprepared or deprecated.) Or changing the avatar to reflect spells prepared or to be drug over for an effect.
1410555033
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
So.... all the working parts are complete.... just added hundreds of spell names to all the characters on the map with one click. Fancy spell book provides all the token actions needed. Just fill it out a bit and polish it up. updatespell = function() { getSpellListFromTable(); }; getSpellListFromTable = function() { var characters = findObjs({type: "character"}); var rollabletables = findObjs({type: "rollabletable"}); _.each(characters, function(indexCharacters) { if(indexCharacters.get("controlledby") != ""){ var characterID = indexCharacters.get("_id"); _.each(rollabletables, function(indexrollabletables) { var rollabletableName = indexrollabletables.get("name"); var start_pos = rollabletableName.indexOf("{") + 1; var end_pos = rollabletableName.indexOf("}",start_pos); var characterIDfound = rollabletableName.substring(start_pos,end_pos); if(characterIDfound == characterID){ var tableItems = findObjs({ _type: "tableitem", _rollabletableid: indexrollabletables.get("_id")}) var j = -1; var levelOfSpell = ""; var lastLevel = ""; _.each(tableItems, function(indextableItems) { levelOfSpell = indextableItems.get("name").substr(0, 1); if(lastLevel != levelOfSpell){j = -1}; j++ var nameOfSpell = indextableItems.get("name").substr(2, indextableItems.get("name").length); var countOfSpell = j; lastLevel = levelOfSpell; addSpellToSheet(levelOfSpell,nameOfSpell,countOfSpell,characterIDfound) }); }; }); }; }); }; addSpellToSheet = function(levelOfSpell,nameOfSpell,countOfSpell,characterIDfound) { var attributeName = ""; if(levelOfSpell == 0){ attributeName = "repeating_spellbookcantrip_" + countOfSpell + "_spellname"; }else{ attributeName = "repeating_spellbooklevel" + levelOfSpell + "_" + countOfSpell + "_spellname"; }; var spellData = _.findWhere(roll20API.spelllist, {Call: nameOfSpell}); var attributesExists = findObjs({type: "attribute", _characterid: characterIDfound, name: attributeName}); if(attributesExists.length &gt; 0 ){ findObjs({ _type: "attribute", _id: attributesExists[0].get("_id") })[0].set({current: spellData.Name}); }else{ createObj("attribute", { name: attributeName, characterid: characterIDfound, current: spellData.Name }); }; };
If this adds every spell to a character sheet (judging by last post - still reading through it and PM sent) isn't this going to create a massive amount of lag?
1410797843

Edited 1410797988
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
You don't add all the spells... there is a user interface (of sorts) to control what spells are added. Here is a walkthru.... 1) Fill out a record sheet with Class(es) Level(s) and default token for one or more characters. (Wizard in the example above) 2) Drag the Character Token (one or many) to the Spell Book page. (This page is just for managing spells... doesn't even have to be in the active campaign if you use the Transmogrifier) You have three token actions for the spell book to use in order (Y/N confirmation provided.) 3) Click and confirm "Create/Update-Spell-List"This builds a rollable table for each token on the spell book page. Notice the red "Xs" ... all spells are "turned off" at the start... this is controlled by the item weight. Set all the spells you want to a weight of "1" or higher. 4) Close the table and click and confirm "Update_List_Icons" When you reopen the table you can validate your spell list by looking at the green "checks" and red "Xs." Repeat steps 3 and 4 process until you are happy... or if you want to add spells in the future. 5) Click and confirm "Update-Character-Spells. And you are done....this can be repeated and will reuse and reorder the spell list by level (123) and spell name (ABC) In this example I only added three cantrips to one character... but I could have added several levels of spells to many characters... or I can repeat this for this character as they progress later on by turning on more spells in their rollable table. Also... you can add classes but will have to update the rollable table (could add a state object to help with that too.) This is actually not doing anything more than saving the repeated process of adding spell (key strokes.) The sheets get no heavier then what they would normally be. Also will automate the adding of PowerCard friendly token actions by building them through the API as spells are added. The only issue is the spell array which is in progress (nearly complete just tweaking to match up nicely with the 5E sheet.) We will not be sharing any spells but the spells that are available through free content.
Crap, I'm going to have to adjust it for my advanced, more powerful character sheet :/
I'm also not exactly sure what you mean by step 2, of how you drag the token of a character "to the spell book page". You mean to the rollable table, or to the spellbook tab of a different character sheet? Or????
1410801442
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
You make a page in the campaign called "Spell Book." Put a big "Spell Book" token with token actions.... And drag the Tokens for the record sheet you want to edit the spells for... Just a way of controlling which characters you update.
1410801577
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Bryan K. said: Crap, I'm going to have to adjust it for my advanced, more powerful character sheet :/ We are going to clean up the powercard text in the array.. so its more data driven. Want to keep it a light as possible to it will not need many edits with sheet updates or power card updates. (Most of the spell array you see is just to work out the functionality... final array should be cleaner.)
Wow, a campaign page, interesting. Sounds pretty solid. I'll have to see how the API / excel is used, grabbing the data out of the excel to populate the spells, since my character sheet uses more customization for handling life clerics, max healing, max damage etc system.
1410802503
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Yea... its just in Excel to populate the information easily..... Then its converted into an array for placement in the API. Only going to share the spells in the free content... :/ if all of those... But it should be enough for people with the PHB to complete on their own.
1410887586
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
<a href="https://gist.github.com/BaldarSilveraxe/38819267c8" rel="nofollow">https://gist.github.com/BaldarSilveraxe/38819267c8</a>...
1410887915
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
That..... is almost it... Finds the "best" class when a character can gain a spell from more than one class... and chooses it. It populates all the drop downs for each spell choose. Working out a few more fields in the data set (someone pointed out to should break out damage for example and they are 100% correct.) Then we are working on designing a few types of PowerCards to spells can be feed into that and make similar Token Action PowerCards.... And that is about it... clean up the code a bit and test for bugs and refinements.
Break out the damage, as in multiple fields? That's how my enhanced sheet does it, as well as for heals. A field for number of dice, and a field for the type of die.
1410890588

Edited 1410890693
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
The goal is to properly update all these fields properly based on the spells selected from an variable list of spells based class(es.) And create an ability that is PowerCard friendly that can be set as a token action. All the drop downs are complete.... target/Area... saving stat... custom DC...on save....damage dice... other bonus... damage type and spell effect are being added ot cleaned up. After that we just organize the list of spells into groups of standard power card designs... And done.
1410969716
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
We made the Icons clearer.... set by the weight: Weight &lt;= 0 = Red = The Spell is within the Character's class wheelhouse but SHOULD NOT be added to the character sheet. Weight == 1 = Green = The Spell is within the Character's class wheelhouse and SHOULD be added to the character sheet. Weight &gt;= 2 = Blue = The Spell is within the Character's class wheelhouse and SHOULD be added to the character sheet WITH Token Action.
Still estimating end of next week?
1410974332
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Yes... 20th or so.
1410983521
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
At long last I have found a reason to use the hidden "description" property for the Ability object!!!!
1411001832

Edited 1411002311
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
Closer.... will likely be closing this thread soon and opening a [Script] thread.... PowerCards are created as a Token Action when the weight is set to "2" (blue icon in the table.) Emote changes based on spell components... see examples above. !power --emote| Speaking words of arcane power while making precise movement of the hand... --charid|-JWZiG0l2d7vdTE3VrgC --name|Acid Splash --txcolor|#fff --txcolor|#fff --leftsub|1 action --rightsub|60 feet --Cantrip|Conjuration --Components|V,S --Duration|InstantaneousI hurl a bubble of acid. --text1|^*Choose one creature within range, or choose two creatures within range that are within 5 feet of each other. --text2|^*A target must succeed on a **Dexterity Saving Throw** or take **1d6 Acid Damage**. --I target **@{target|t1|token_name}**, and **@{target|t2|token_name}** who is within 5 feet. --Make a DEX save|vs [[8+@{selected|spellcastingability}+@{selected|PB}]] --Failed Save|[[1d6+{floor((@{level})/5),1}kl1*1d6+{floor((@{level})/11),1}kl1*1d6+{floor((@{level})/17),1}kl1*1d6]] acid damage. Some things are added on the fly like "--charid|" since we already had them... Working to see if we can track or at least report out spell slot usage after casting. Code will have a URL to the Gist with spell data that is available through free PDF content ( Wesley L. has been the driving force behind this code and the data!!! BIG thanks to him!!!)
I have spell slot tracking in my API enchanced character sheet.
1411008053
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
I need to check it out!!!!
this looks freaking amazing - a bit confused implementing this though. Looking forward to your script thread!
1411040461

Edited 1411040638
Stephen S. said: I need to check it out!!!! It's in the mentor campaign without a minor fix I put in after. (the link to gist), but you can check out the latest in the campaign itself. The best part is all of the target based rolling it does and message the GM: option value="/w gm Target's @{savestat} [[ 1d20 + @{target|Target 1|strength_save_mod} ]] | [[ 1d20 + @{target|Target 1|strength_save_mod} ]]"&gt;STR Due to the targeting I had to not adopt Actoba's NPC dual sheet functionality for creatures/NPCs, as I needed a universal system for any character/creature to target any other one. With use the %{SelectedToken|MeleeAttack1} for weapons, spells was my only concern left for 5e in terms of character usage. I appreciate all of your hard work and plan to get my players to assist in completing this in attaching it to my character sheet. ALSO: I split out the healing and damage dice as I mentioned above so the rolled result can show both the rolled value and max if applicable : &lt;div&gt;&lt;input type="number" name="attr_spellhealdicenum" value="1" min="0" step="1"&gt;&lt;/div&gt; &lt;div&gt;&lt;input type="number" name="attr_spellhealdie" value="4" min="0" step="1"&gt;&lt;/div&gt; This helps with beacon of hope, etc. The spell slot tracking system allows you to press a button(token action) to see all of your spell slots remaining and max, another for using, same system for class resources.