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

[Script] D&D 4e Character Importer

1395510676

Edited 1396161463
Ok, here's the alpha version importer script... Export a D&D 4e character file from the DDI builder, open it up in notepad, and copy and paste the text from that into the GM Notes of a token, and use the macro !build-character @{selected|token_id} to make the character. It currently creates the character sheet, attributes, initiative/saving throw macros, and skill check macros. Updates Updated: March 30th, 2014 ~ 2:10 am eastern Major update... it now parses powers. Please import characters and let me know what kind of errors or problems you come across. I would also like to know what power, race, class combination is causing the problem and I will do what I can to sort it out. Updated: March 23rd, 2014 ~ 11:45 am eastern The script will now automatically link the token Bar 2 with the healing surge attribute and Bar 3 with the Hit Points attribute. The Macro !build-character @{selected|token_id} The Script var AddPCAttribute = AddPCAttribute || {}; var AddPCPower = AddPCPower || {}; 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 == "!build-character") { if (!msg.selected) return; var n = msg.content.split(" ", 2); var Token = getObj("graphic", n[1]) if (Token.get("subtype") != "token") return; if (Token.get("gmnotes").indexOf("D20Character") == -1) return; // USER CONFIGURATION var USE_POWER_CARDS = true; // REPLACE SPECIAL CHARACTERS StatBlock = StatBlock.replace(//g, ""); var StatBlock = Token.get("gmnotes"); StatBlock = StatBlock.replace(/%20/g, " "); // Replace %20 with a space StatBlock = StatBlock.replace(/%21/g, "!"); // Replace %21 with a ! StatBlock = StatBlock.replace(/%22/g, "'"); // Replace %22 (quotation) with ' StatBlock = StatBlock.replace(/%26amp/g, "&"); // Replace %26amp (ampersand) with & StatBlock = StatBlock.replace(/%26apos/g, "'"); // Replace %26apos with ' StatBlock = StatBlock.replace(/%26lt/g, "<"); // Replace %26lt with < StatBlock = StatBlock.replace(/%26gt/g, ">"); // Replace %26gt with > StatBlock = StatBlock.replace(/%27/g, "'"); // Replace %27 with ' StatBlock = StatBlock.replace(/%28/g, "("); // Replace %28 with ( StatBlock = StatBlock.replace(/%29/g, ")"); // Replace %29 with ) StatBlock = StatBlock.replace(/%2C/g, ","); // Replace %2C with , StatBlock = StatBlock.replace(/%3A/g, ":"); // Replace %3A with : StatBlock = StatBlock.replace(/%3B/g, ""); // Remove %3B (semi-colon) StatBlock = StatBlock.replace(/%3Cbr/g, ""); // Remove carriage returns StatBlock = StatBlock.replace(/%3D/g, "="); // Replace %3D with = StatBlock = StatBlock.replace(/%3E/g, ""); // Remove %3E (???) StatBlock = StatBlock.replace(/%3F/g, "?"); // Replace %3F with ? StatBlock = StatBlock.replace(/\s{2,}/g, " "); // Replace multiple spaces with one space StatBlock = StatBlock.replace(/%u2013/g, "-"); // Replace %u2013 with - StatBlock = StatBlock.replace(/%u2019/g, "'"); // Replace %u2019 with ' // END SPECIAL CHARACTER REPLACEMENT or REMOVAL // GET NAME OF CHARACTER var CharacterName = StatBlock.match(/<name>(.*?)<\/name>/g)[0].split(">")[1].split("<", 1)[0].trim(); // CHECK FOR DUPLICATE CHARACTERS var CheckSheet = findObjs({ _type: "character", name: CharacterName }); // DO NOT CREATE IF SHEET EXISTS if (CheckSheet.length > 0) { sendChat("ERROR", "This character already exists."); return; } // CREATE CHARACTER SHEET & LINK TOKEN TO SHEET var Character = createObj("character", { avatar: Token.get("imgsrc"), name: CharacterName, gmnotes: Token.get("gmnotes"), archived: false }); // CHARACTER LEVEL var Level = parseInt(StatBlock.match(/<Level>(.*?)<\/Level>/g)[0].match(/\d+/g)[0]); // ABILITY SCORE MODS, SKILLS, HP, & MORE var Stats = StatBlock.match(/<Stat value=(.*?)\/>/g); var Skills = {}; var SkillTraining = {}; var s = 0; while (s < Stats.length) { // ABILITY SCORE MODS if (Stats[s].indexOf("'Strength modifier'") != -1) var STRMod = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Constitution modifier'") != -1) var CONMod = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Dexterity modifier'") != -1) var DEXMod = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Intelligence modifier'") != -1) var INTMod = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Wisdom modifier'") != -1) var WISMod = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Charisma modifier'") != -1) var CHAMod = parseInt(Stats[s].match(/\d+/g)[0]); // DEFENSES if (Stats[s].indexOf("'AC'") != -1) var ArmorClass = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Fortitude Defense'") != -1) var Fortitude = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Reflex Defense'") != -1) var Reflex = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Will Defense'") != -1) var Will = parseInt(Stats[s].match(/\d+/g)[0]); // HIT POINTS & SURGES if (Stats[s].indexOf("'Hit Points'") != -1) var HitPoints = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Healing Surges'") != -1) var HealingSurges = parseInt(Stats[s].match(/\d+/g)[0]); // SKILLS if (Stats[s].indexOf("'Acrobatics'") != -1) Skills["Acrobatics"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Arcana'") != -1) Skills["Arcana"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Athletics'") != -1) Skills["Athletics"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Bluff'") != -1) Skills["Bluff"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Diplomacy'") != -1) Skills["Diplomacy"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Dungeoneering'") != -1) Skills["Dungeoneering"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Endurance'") != -1) Skills["Endurance"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'History'") != -1) Skills["History"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Heal'") != -1) Skills["Heal"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Insight'") != -1) Skills["Insight"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Intimidate'") != -1) Skills["Intimidate"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Nature'") != -1) Skills["Nature"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Perception'") != -1) Skills["Perception"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Religion'") != -1) Skills["Religion"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Stealth'") != -1) Skills["Stealth"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Streetwise'") != -1) Skills["Streetwise"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Thievery'") != -1) Skills["Thievery"] = parseInt(Stats[s].match(/\d+/g)[0]); // SKILL TRAINING if (Stats[s].indexOf("'Acrobatics Trained'") != -1) SkillTraining["Acrobatics"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Arcana Trained'") != -1) SkillTraining["Arcana"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Athletics Trained'") != -1) SkillTraining["Athletics"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Bluff Trained'") != -1) SkillTraining["Bluff"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Diplomacy Trained'") != -1) SkillTraining["Diplomacy"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Dungeoneering Trained'") != -1) SkillTraining["Dungeoneering"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Endurance Trained'") != -1) SkillTraining["Endurance"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'History Trained'") != -1) SkillTraining["History"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Heal Trained'") != -1) SkillTraining["Heal"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Insight Trained'") != -1) SkillTraining["Insight"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Intimidate Trained'") != -1) SkillTraining["Intimidate"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Nature Trained'") != -1) SkillTraining["Nature"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Perception Trained'") != -1) SkillTraining["Perception"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Religion Trained'") != -1) SkillTraining["Religion"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Stealth Trained'") != -1) SkillTraining["Stealth"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Streetwise Trained'") != -1) SkillTraining["Streetwise"] = parseInt(Stats[s].match(/\d+/g)[0]); if (Stats[s].indexOf("'Thievery Trained'") != -1) SkillTraining["Thievery"] = parseInt(Stats[s].match(/\d+/g)[0]); // SPEED if (Stats[s].indexOf("'Speed'") != -1) var Speed = parseInt(Stats[s].match(/\d+/g)[0]); s++; } // ADD ATTRIBUTES TO SHEET AddPCAttribute("Level", Level, Character.id); var HitPointsID = AddPCAttribute("Hit Points", HitPoints, Character.id).id; var SurgesID = AddPCAttribute("Surges", HealingSurges, Character.id).id; AddPCAttribute("Surge Value", Math.floor(HitPoints/4), Character.id); AddPCAttribute("AC", ArmorClass, Character.id); AddPCAttribute("Fortitude", Fortitude, Character.id); AddPCAttribute("Reflex", Reflex, Character.id); AddPCAttribute("Will", Will, Character.id); AddPCAttribute("STR Mod", STRMod, Character.id); AddPCAttribute("CON Mod", CONMod, Character.id); AddPCAttribute("DEX Mod", DEXMod, Character.id); AddPCAttribute("INT Mod", INTMod, Character.id); AddPCAttribute("WIS Mod", WISMod, Character.id); AddPCAttribute("CHA Mod", CHAMod, Character.id); AddPCAttribute("Half Level Mod", Math.floor(Level/2), Character.id); AddPCAttribute("isPC", "true", Character.id); AddPCAttribute("Passive Perception", 10 + Skills.Perception, Character.id); AddPCAttribute("Passive Insight", 10 + Skills.Insight, Character.id); AddPCAttribute("Speed", Speed, Character.id); // ADD INITIATIVE & SAVING THROW TOKEN ACTIONS AddPCPower("Initiative", "Initiative [[1d20 [Base] + @{DEX Mod} [Dexterity Mod] + @{Half Level Mod} [Half Level Mod] &{tracker}]]", Character.id, true); AddPCPower("Saving Throw", "Saving Throw [[1d20 + ?{Saving Throw Modifier|0}]]", Character.id, true); // ADD SKILLS AddPCPower("█▓▒░SKILLS░▒▓█", "", Character.id, false); var SkillList = ["Acrobatics", "Arcana", "Athletics", "Bluff", "Diplomacy", "Dungeoneering", "Endurance", "Heal", "History", "Insight", "Intimidate", "Nature", "Perception", "Religion", "Stealth", "Streetwise", "Thievery"]; var SkillMods = ["DEX Mod", "INT Mod", "STR Mod", "CHA Mod", "CHA Mod", "INT Mod", "CON Mod", "WIS Mod", "INT Mod", "WIS Mod", "CHA Mod", "WIS Mod", "WIS Mod", "INT Mod", "DEX Mod", "CHA Mod", "DEX Mod"]; var MiscMod = 0; var Trained = 0; var ShowTrained = ""; var k = 0; while (k < SkillList.length) { Trained = SkillTraining[SkillList[k]]; ShowTrained = (Trained == 5) ? " (Trained)" : ""; // REMOVE ABILITY SCORE MODIFIER & TRAINING MODIFIER TO GET MISCMOD switch (SkillMods[k]) { case "STR Mod": MiscMod = Skills[SkillList[k]] - STRMod - Trained; break; case "CON Mod": MiscMod = Skills[SkillList[k]] - CONMod - Trained; break; case "DEX Mod": MiscMod = Skills[SkillList[k]] - DEXMod - Trained; break; case "INT Mod": MiscMod = Skills[SkillList[k]] - INTMod - Trained; break; case "WIS Mod": MiscMod = Skills[SkillList[k]] - WISMod - Trained; break; case "CHA Mod": MiscMod = Skills[SkillList[k]] - CHAMod - Trained; break; default: MiscMod = 0; } AddPCPower(SkillList[k] + ShowTrained, SkillList[k] + " [[1d20 [Base] + @{" + SkillMods[k] + "} [" + SkillMods[k] + "] + " + Trained + " [Skill Training] + " + MiscMod + " [Misc Mods] ]]", Character.id, false); k++; } // ADD POWERS AddPCPower("█▓▒░POWERS░▒▓█", "", Character.id, true); var PowersBlock = StatBlock.match(/<Power name=(.*?)<\/Power>/g); var p = 0; while (p < PowersBlock.length) { // DEFINE VARIABLES var Power = {}; var Key = ""; var Content = ""; var PowerName = PowersBlock[p].match(/<Power name='(.*?)' >/g)[0].split("='")[1].split("' ")[0]; var AtkBonus = (PowersBlock[p].match(/<AttackBonus>(.*?)<\/AttackBonus>/g) != null) ? PowersBlock[p].match(/<AttackBonus>(.*?)<\/AttackBonus>/g)[0].match(/\d+/g)[0] : "0"; var DmgRoll = (PowersBlock[p].match(/<Damage>(.*?)<\/Damage>/g) != null) ? PowersBlock[p].match(/<Damage>(.*?)<\/Damage>/g)[0].split(">")[1].split("</")[0] : ""; var DmgType = (PowersBlock[p].match(/<DamageType>(.*?)<\/DamageType>/g) != null) ? PowersBlock[p].match(/<DamageType>(.*?)<\/DamageType>/g)[0].split(">")[1].split("</")[0] : ""; // REMOVE EMPTY ENTRIES TO PREVENT ERRORS PowersBlock[p] = PowersBlock[p].replace("<specific name='Keywords' />", ""); PowersBlock[p] = PowersBlock[p].replace("<specific name='Keywords'/>", ""); PowersBlock[p] = PowersBlock[p].replace("<specific name='Level' />", ""); PowersBlock[p] = PowersBlock[p].replace("<specific name='Level'/>", ""); PowersBlock[p] = PowersBlock[p].replace("<specific name='Attack Type' />", ""); PowersBlock[p] = PowersBlock[p].replace("<specific name='Attack Type'/>", ""); PowersBlock[p] = PowersBlock[p].replace("<table>", ""); PowersBlock[p] = PowersBlock[p].replace("</table>", ""); // PARSE POWER INFORMATION var PowerInfo = PowersBlock[p].match(/<specific name=(.*?)<\/specific>/g); var i = 0; var HitCount = 1; while (i < PowerInfo.length) { Key = PowerInfo[i].match(/='(.*?)' >/g)[0].split("='")[1].split("' >")[0]; Content = PowerInfo[i].split(" > ")[1].split(" </")[0].split("Level")[0]; // CHECK FOR DUPLICATE HIT ENTRIES - MOST OFTEN FOUND ON POWERS // WITH SECONDARY/TERTIARY TARGETS &/OR ATTACKS if (Key == "Hit" && Power["Hit"] != undefined) { HitCount++; Key = "Hit" + HitCount; } Power[Key] = Content; i++; } // CHECK FOR MULTIPLE ATTACK POWERS var MultiAttack = ""; if (Power["Target"] != undefined) { switch (Power["Target"]) { case "One ally": case "One creature": case "One enemy": case "Same as primary target": case "The triggering ally": case "The triggering creature": case "The triggering enemy": case "The triggering ally in the burst": case "The triggering creature in the burst": case "The triggering enemy in the burst": case "One ally adjacent to you": case "One creature adjacent to you": case "One enemy adjacent to you": MultiAttack = ""; break; default: MultiAttack = "?{Number of Targets|1}"; } } // BUILD POWERSTRING PowerString = "!power --format|dnd4e --name|" + PowerName; PowerString += (Power["Display"] != undefined) ? " --title|" + Power["Display"] : ""; PowerString += (Power["Power Usage"] != undefined) ? " --usage|" + Power["Power Usage"] : ""; PowerString += (Power["Action Type"] != undefined) ? " --action|" + Power["Action Type"] : ""; PowerString += (Power["Flavor"] != undefined) ? " --emote|" + Power["Flavor"] : ""; PowerString += (Power["Attack Type"] != undefined) ? " --Range|" + Power["Attack Type"] : ""; PowerString += (Power["Trigger"] != undefined) ? " --Trigger|" + Power["Trigger"] : ""; PowerString += (Power["Requirement"] != undefined) ? " --Requirement|" + Power["Requirement"] : ""; PowerString += (Power["Target"] != undefined) ? " --Target(s)|" + Power["Target"] : ""; PowerString += (Power["Attack"] != undefined) ? " --attack" + MultiAttack + "|[[1d20 + " + AtkBonus + "]]" : ""; PowerString += (Power["Attack"] != undefined) ? " --defense|" + Power["Attack"].split("vs.")[1] : ""; PowerString += (Power["Primary Target"] != undefined) ? " --Primary Target|" + Power["Primary Target"] : ""; PowerString += (Power["Primary Attack"] != undefined) ? " --Primary Attack|[[1d20 + " + AtkBonus + "]] vs " + Power["Primary Attack"].split("vs.")[1] : ""; PowerString += (Power["Hit"] != undefined && isNaN(Power["Hit"].charAt(0)) == true) ? " --On Hit|" + Power["Hit"] : ""; PowerString += (Power["Hit"] != undefined && isNaN(Power["Hit"].charAt(0)) == false) ? " --damage|[[" + DmgRoll + "]] " + DmgType.toLowerCase() + " " + Power["Hit"].substring(Power["Hit"].indexOf("damage")) : ""; PowerString += (Power["Secondary Target"] != undefined) ? " --^Secondary Target|" + Power["Secondary Target"] : ""; PowerString += (Power["Secondary Attack"] != undefined) ? " --^Secondary Attack|[[1d20 + " + AtkBonus + "]] vs " + Power["Secondary Attack"].split("vs.")[1] : ""; PowerString += (Power["Hit2"] != undefined && isNaN(Power["Hit2"].charAt(0)) == true) ? " --^Secondary Hit|" + Power["Hit2"] : ""; PowerString += (Power["Hit2"] != undefined && isNaN(Power["Hit2"].charAt(0)) == false) ? " --^Secondary Hit|[[" + DmgRoll + "]] " + DmgType.toLowerCase() + " " + Power["Hit2"].substring(Power["Hit2"].indexOf("damage")) : ""; PowerString += (Power["Tertiary Target"] != undefined) ? " --^2Tertiary Target|" + Power["Tertiary Target"] : ""; PowerString += (Power["Tertiary Attack"] != undefined) ? " --^2Tertiary Attack|[[1d20 + " + AtkBonus + "]] vs " + Power["Tertiary Attack"].split("vs.")[1] : ""; PowerString += (Power["Hit3"] != undefined && isNaN(Power["Hit3"].charAt(0)) == true) ? " --^2Tertiary Hit|" + Power["Hit3"] : ""; PowerString += (Power["Hit3"] != undefined && isNaN(Power["Hit3"].charAt(0)) == false) ? " --^2Tertiary Hit|[[" + DmgRoll + "]] " + DmgType.toLowerCase() + " " + Power["Hit3"].substring(Power["Hit3"].indexOf("damage")) : ""; PowerString += (Power["Miss"] != undefined) ? " --Miss|" + Power["Miss"] : ""; PowerString += (Power["Effect"] != undefined) ? " --Effect|" + Power["Effect"] : ""; // PowerString += (Power[""] != undefined) ? " --Range|" + Power[""] : ""; AddPCPower(PowerName, PowerString, Character.id, true); p++; } // SET TOKEN SETTINGS Token.set("represents", Character.id); Token.set("name", CharacterName); Token.set("showplayers_name", true); Token.set("bar2_link", SurgesID); Token.set("bar3_link", HitPointsID); Token.set("showplayers_bar3", true); Token.set("aura2_radius", "0"); Token.set("aura2_color", "#14A70A"); Token.set("aura2_square", true); } }); function AddPCAttribute (attr, value, charid) { var attrid = ""; if (attr === "Hit Points") { attrid = createObj("attribute", { name: attr, current: value, max: value, characterid: charid }); } else { attrid = createObj("attribute", { name: attr, current: value, characterid: charid }); } return attrid; } function AddPCPower (powername, powerstring, charid, tokenaction) { createObj("ability", { name: powername, description: "", action: powerstring, istokenaction: tokenaction, characterid: charid }); }
Hi...does this take the input from the character summary export from the wizards of the coast browser character builder function? I don't have the old character builder, so only using the browser version. It starts like this.. ====== Created Using Wizards of the Coast D&D Character Builder ====== Claydon, level 1 Human, Fighter (Weaponmaster) Build: Arena Fighter Fighter Option: Combat Agility Fighter Talents Option: One-handed Weapon Talent Human Power Selection Option: Heroic Effort FINAL ABILITY SCORES STR 16, CON 15, DEX 16, INT 8, WIS 12, CHA 9 STARTING ABILITY SCORES STR 16, CON 15, DEX 16, INT 8, WIS 12, CHA 9 AC: 17 Fort: 16 Ref: 14 Will: 12 HP: 30 Surges: 11 Surge Value: 7 TRAINED SKILLS Athletics +8, Endurance +7, Heal +6, Streetwise +4 UNTRAINED SKILLS Acrobatics +3, Arcana –1, Bluff –1, Diplomacy –1, Dungeoneering +1, History –1, Insight +1, Intimidate –1, Nature +1, Perception +1, Religion –1, Stealth +3, Thievery +3 POWERS Basic Attack: Melee Basic Attack Basic Attack: Ranged Basic Attack Human Racial Power: Heroic Effort Fighter Attack: Combat Challenge Fighter Attack: Combat Agility Fighter Attack 1: Brash Strike Fighter Attack 1: Crushing Surge Fighter Attack 1: Covering Attack Fighter Attack 1: Harrier's Ploy FEATS Level 1: Human Perseverance Level 1: Action Surge ITEMS Scale Armor x1 Adventurer's Kit Greatsword x1 ====== End ====== Thanks, Ian
No, not that. The actual exported file from the DDI Character Builder. Should end in .dnd4e
got it...pasted into GM notes for token...and setup macro. However...not sure what i should expect to see when clicked macro having selected token...no change to token character
Hi..how do I apply the macro to the token/character?
1395537034

Edited 1395543030
I'm getting an error TypeError: Cannot call method 'get' of undefined at Sandbox. (evalmachine.:311:19) at eval ( Also, you're planning on incorporating your power card script into this? Edit: You did not include the @{selected|token_id} in the macro. It works now. Now that the script works, I have some feedback. Can you format the skills so they look nice with the power cards, or no? Bar 2 and 3 while they use the correct values, are just that. They don't actually represent that stat. Can we have an option for the initiative to roll to the tracker?
1. Given that players often roll skills all at the same time, I don't use power cards for skills. It's easier for me as a GM to see four to six skills in the concise form than power cards. 2. Doh... I forgot to link the bars. Thanks for pointing that out. I'll be sure to add that. I just copied that part from my monster importer and with monsters, you do not want to link the bars. 3. The initiative macro in the script includes the &{tracker} option to send the token/roll to the turn order tracker. 4. No, I'm not going to include the power card script... I'm going to keep them separate, but this will be using the power card script for attacks. You'll just have to make sure you install them both. The reason for this is to make it easier for me to keep all the scripts updated. It would be a pain in the ass to update power card stuff in three different scripts.
So you'll be making it so that if we have the power card script installed, importing a character will make power cards?
My goal is to add power card and non-powercard options for both the monster and character importers. That way GM's can choose whether or not they want to use power cards.
Hi...still not getting it. Edited the macro name to include the @{selected|token_id} bit and reran. Having selected the player token which has the dnd4e text in the GM notes for that token before running the macro, the macro seems to run but doesn't actually update anything in the character. I assume you can run this with just the DM logged in. Help!
1395584947

Edited 1395584988
What text are you pasting into the gmnotes? This does NOT use the character summary. You must export the character from the builder and save it as a .dnd4e file on your computer. You can then open that file with notepad and paste the contents of that file into the gmnotes.
Yes, have used text in the dnd4e file...see a short extract from this below... <D20Character game-system="D&D4E" Version="0.07a" legality="houserule" > <!-- Dungeons and Dragons Insider: Character Builder character save file --> <CharacterSheet> <Details> <name> Claydon </name> <Level> 1 </Level> <Player> </Player> <Height> </Height> <Weight> </Weight> <Gender> </Gender> <Age> 23 </Age> <Alignment> </Alignment> <Company> </Company> <Portrait> </Portrait> <Experience> </Experience> <CarriedMoney> 10 gp </CarriedMoney> <StoredMoney> </StoredMoney> <Traits> </Traits> <Appearance> </Appearance> <Companions> </Companions> <Notes> </Notes> </Details> <!-- Base ability scores (see stats of same name for final adjusted score) --> <AbilityScores legality="houserule" > <Strength score="16" /> <Constitution score="15" /> <Dexterity score="16" /> <Intelligence score="8" /> <Wisdom score="12" /> <Charisma score="9" /> </AbilityScores>
It should be working then. I don't know why it wouldn't.
This doesn't update an already created character. It only creates a brand new character. If you have a character sheet/journal entry with the same name already... it won't run.
I did have a character, so will delete it and delete the campaign, re-create macro and go from there. Will let you know and thanks for responding.
Don't need to delete the campaign. Just the character.
Its ok, thought I had better start from scratch. So, here's what I did. 1. Created the campaign. 2. Added the macro and named as per latest edit above. 3. Added a token of a fighter. 4. Added the character text to the GM token notes (all of the dnd4e file text) 5. clicked on the token 6. clicked the macro on the quick bar. 7. The macro then ran, but does not seem to have added a character. the token attributes remain the same as when I created it. I am sure this is user error, but for the life of me cant work out where I went wrong apart from my first 24 hours using rolld20 Oh, the other thing, I am running this Roll20 tabletop as GM with no players logged in. I am also running in Chrome.
Where are you putting the script? It only works if you install the script in the api and you have to be a mentor for that.
lol....and I was just coming to that conclusion! Mentor here I come...
1395592021

Edited 1395596195
This is how tokens that are used with the monster and character importers are going to look after the script is done. My reason for using this format and changing the order of the bars (making Health Bar3 instead of the usual Bar1) is to keep the health bar with the token and prevent token overlap confusion. Once your players know their tokens and their allies tokens, you can remove the nameplates and completely remove the need for them... thus getting rid of all overlapping token problems. When the script gets closer to being done, I'll add in more user configuration variables at the top of the script so you can set your own style instead of using what I've done.
Ok...so now a mentor...though it is somewhat ironic a term to use on myself! Have added the script into the API...does it matter what I call it? On creating the macro...how exactly do you call this particular script?
It doesn't matter what you name it, but I suggest naming it Character Importer. And you use the macro: !build-character @{selected|token_id} to activate the script.
WOW. Tooooo slick!
Hmmm, done a debug log and the msg.type is not "api" but is "general" hence it does not perform the script. Any ideas?
You are including the ! at the start of the macro right? All api scripts require the ! mark to start.
I've played around with the macro name and inside the macro...both are now !build-character@{selected|token_id}. Based on the instructions, I assume the macro name would activate the API wtihout any macro content, but I guess this is not the case. However, am I getting this right with both macro name and macro being the same? The instructions did not identify what should be in the macro itself. I am now getting the api type but i'm failing to get further which suggests something is wrong about the macro.
right, sorted it. api now working. Inside the macro the text needed to have a space between "character" and the "@" Thanks very much for the help.
Any idea when you're going make this work with the power cards script? Also, you were talking about customization. What's the extent of that going to be?
I've hit a wall on my scripts for the moment. Taking a short break and then will be returning to them. The customization I mentioned would be for setting what the various bars are used for.
Given the help you gave me... I will have a play around and see if I can help on the power cards side of the script. I have a few other ideas too. Hope that's ok HoneyBadger?
That's fine. I did manage to get some work done on importing powers. Just having issues with identifying exceptions with all the powers and the different ways they are put into the xml files. For example... Heroic Effort has zero keywords and its entry in the character builder file shows up with <specific name='Keywords' /> and this was causing a big problem with parsing Heroic Effort. I got that problem fixed simply by removing it from the string before I start tearing it apart for the various parts of each power.
Updated!! Character Importer now imports powers. This script is still in a very rough draft... so I would not suggest using it with less tech savvy players in your campaigns unless you intend to go through all their powers and sort them out. Please post about any powers / characters you're having problems with and I'll try to sort it out.
Is the DND4E file "version" dependent? I have a couple from the desktop CB that import powers but with no rolls (just the name and type)
I didn't bother using the offline CB. It's not officially supported and the only way to get it is pirating. So yeah... online DDI builder files only.
Actually if you had it, it still works. But that is good to know (I do have DDI access)
Hey HoneyBadger, as with all your scripts this one is another home run. I only had a few very easy fixes after importing like seven characters. Stuff like one of my player's warpriest including ?{Number of Targets|1} on smite undead (which is unnecessary since it's a single enemy ability) and changing up my wizard's magic missile so that under --Effect it is --Effect|[[2 + @{INT Mod}]] force damage instead of just displaying --Effect|2 + int modifier force damage. In any event, great work. In my mind, the mentor sub is 100% worth it for anyone running a 4e campaign largely because it gives you access to your three great scripts.
1396644591

Edited 1396644613
Glad you like them. I do intend to try and add a little more parsing to the character importer and find things like Magic Missile, but it's difficult... since there are so many exceptions in D&D 4e. I also need to add a non-power card output for macros.
1396648974

Edited 1396650216
Inline rolls of multiple different die seem to be an issue. For example, my Barbarian's Howling Strike is 1d12 (e.g. 1[W]) + 1d6 + STR mod. The script imports it correctly, but using Howling Strike actually crashes it. So, the script looks something like --damage|[[1d12 +1d6 +@STR Mod]] damage, but returns an error of: evalmachine.<anonymous>:579 if (operand.search(/([\d+\-*/() d]|floor\(|ceil\()+\)?/g) === 0) { and turns off power cards. I fixed it for my player by just splitting off the 1d6 resulting in: --damage|[[1d12 +1d6 +@STR Mod]] + [[1d6]] damage. Just means we do math the old fashioned D&D way. ***EDIT*** HoneyBadger responded to this issue in his power cards thread: "Grouped rolls don't work at the moment, but I have a fix ready. Still testing it..."
1397143578

Edited 1397143638
How well will you be integrating with the data delve character sheets? I am referring to the community 4e sheet.
I'm waiting until the dust on character sheets settles before I even think about integrating them.
Ah I see. Alright
Looks freaking amazing HB! Any chance you'd also consider doing this on creature exports from the adventure tools?
He has... See <a href="https://app.roll20.net/forum/post/703974/script-d-and-d-4e-monster-importer" rel="nofollow">https://app.roll20.net/forum/post/703974/script-d-and-d-4e-monster-importer</a>
giggity giggity
1397223255

Edited 1397223312
I wouldn't go overboard on importing monsters and characters just yet. Once the dust settles on the new character sheets, I'm going to see what i can do about adjusting the output of these two scripts to match character sheets and/or monster statblocks. May take some time though. I'm starting a new job in just a little over a week.
HB, Yeah that seems like good advice. Thanks again for all the work you've put into this - it's exceptional. Something one can appreciate after macro'ing about 120+ monsters individually &gt;.&lt; haha. Looking forward to the post update scripting but don't kill yourself, good look on the new job :-)
Thanks. The luck part is past. Now it's just on to orientation and training. It's just going to be nice to not have that stress of living paycheck to paycheck for the first time in my life.
Any ideas on how to add @target to the macros (i.e. where in the syntax) ?
Wherever you want. The macro is interpreted by Roll20 first, which includes the @target and @selection stuff.
so from this: !power --format|dnd4e --emote|ANAX THE FOUL --name|Corrosive Vomit --usage|Encounter --action|Immediate Reaction Action --Range|Close blast 5 --attack|[[1d20 + 15]] --defense|Reflex --damage|[[2d6 + 6]] acid damage, and ongoing 10 acid damage (save ends) I could change to: -defense|@{target|Reflex} Reflex correct? (tested, and SUCCESS!)
Yup. I specifically didn't include any targeting or automation like that because there are so many ways a defense or attack roll can be altered.