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 Monster Importer

1394465136

Edited 1402763597
Here's draft of the Monster Importer script. So far, I have only tried it on a few basic monsters. The script also has the most current version of the Power Card script included. Minor Update (June 14 2014 - 12:30 pm est): Added an option to show defenses as token actions for imported monsters. It is on by default. Look for SHOW_DEFENSES in the user configuration system and replace true with false to turn them off. Minor Update (May 10 2014 - 8:30 am edt): The gmnotes field of the token will be cleared after importing the monster. Leaving the xml in the token gmnotes did create some strange lag issues. Minor Update (Apr 1 2014 - ??? time): Added --format|dnd4e to support a recent change to the Power Card script. Major Update (Mar 23 2014 - 11:45 am edt): Added support for text only macros with no power card. Change USE_POWER_CARDS to true if you want to create monsters with pre-made power card macros. Otherwise you will get text only macros that do not require any other scripts to use. Major Update (Mar 23 2014 - 11:45 am edt): I have also removed the Power Card script from the Monster Importer. You must install the Power Card script separately to use power cards with monsters. Power Card Script - <a href="https://app.roll20.net/forum/post/673780/script-c" rel="nofollow">https://app.roll20.net/forum/post/673780/script-c</a>... Minor Update (Mar 10 2014 - 9:45 pm edt): Added support for distinguishing between single target and multi-target attack powers. Minor Update (Mar 11 2014 - 2:00 pm edt): Added a check for a blank entry in gmnotes on the selected token. Using the Script Step 1: Install the script into your campaign's API Step 2: Using DDI Adventure Tools, find or create the monster you want to import. Step 3: Preview and export the monster to a .monster file Step 4: Open the .monster file using Notepad Step 5: Copy the text from Notepad and paste it into the GM Notes section of the monsters token. Step 6: Run the following macro: !build-monster @{selected|token_id} The Script // VARIABLE & FUNCTION DECLARATIONS var AddAttribute = AddAttribute || {}; var AddSkill = AddSkill || {}; var AddPower = AddPower || {}; 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-monster") { 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("xml") == -1) return; // USER CONFIGURATION var USE_POWER_CARDS = true; // Uses power cards instead of text only macros var SHOW_DEFENSES = true; // Adds monster defenses as token actions // 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(/%22/g, "'"); // Replace %22 (quotation) with ' StatBlock = StatBlock.replace(/%26lt/g, "&lt;"); // Replace %26lt with &lt; StatBlock = StatBlock.replace(/%26gt/g, "&gt;"); // Replace %26gt with &gt; 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(/%u2019/g, "'"); // Replace %u2019 with ' // END SPECIAL CHARACTER REPLACEMENT or REMOVAL // GET NAME OF MONSTER var MonsterName = StatBlock.match(/&lt;Name&gt;(.*?)&lt;\/Name&gt;/g).pop().split("&gt;")[1].split("&lt;", 1)[0]; // CHECK FOR DUPLICATE CHARACTERS var CheckSheet = findObjs({ _type: "character", name: MonsterName }); // DO NOT CREATE IF SHEET EXISTS if (CheckSheet.length &gt; 0) { sendChat("ERROR", "This monster already exists."); return; } // CREATE CHARACTER SHEET & LINK TOKEN TO SHEET var Character = createObj("character", { avatar: Token.get("imgsrc"), name: MonsterName, gmnotes: Token.get("gmnotes"), archived: false }); // GET LEVEL, ROLE, & XP var Level = parseInt(StatBlock.match(/&lt;Level&gt;(.*?)&lt;\/Level&gt;/g)[0].match(/\d+/g)[0]); var Role = StatBlock.match(/&lt;Role(.*?)&lt;\/Role&gt;/g)[0].match(/&lt;Name&gt;(.*?)&lt;\/Name&gt;/g)[0].split("&gt;")[1].split("&lt;")[0]; var XP = parseInt(StatBlock.match(/&lt;Experience FinalValue(.*?)'&gt;/g)[0].match(/\d+/g)[0]); AddAttribute("Level", Level, Character.id); AddAttribute("Role", Role, Character.id); AddAttribute("XP", XP, Character.id); // GET INITIATIVE & HIT POINTS var Initiative = parseInt(StatBlock.match(/&lt;Initiative FinalValue(.*?)'&gt;/g)[0].match(/\d+/g)[0]); var HitPoints = parseInt(StatBlock.match(/&lt;HitPoints FinalValue(.*?)'&gt;/g)[0].match(/\d+/g)[0]); AddAttribute("Initiative", Initiative, Character.id); AddAttribute("Hit Points", HitPoints, Character.id); // GET DEFENSES var Defenses = StatBlock.match(/&lt;Defenses&gt;(.*?)&lt;\/Defenses&gt;/g)[0].match(/\d+/g); var DEF_AC = parseInt(Defenses[0]); var DEF_FORT = parseInt(Defenses[3]); var DEF_REF = parseInt(Defenses[6]); var DEF_WILL = parseInt(Defenses[9]); AddAttribute("AC", DEF_AC, Character.id); AddAttribute("Fortitude", DEF_FORT, Character.id); AddAttribute("Reflex", DEF_REF, Character.id); AddAttribute("Will", DEF_WILL, Character.id); // GET ABILITY SCORE MODIFIERS var AbilityScores = StatBlock.match(/&lt;AbilityScoreNumber(.*?)'&gt;/g); var STRMod = Math.floor((parseInt(AbilityScores[0].match(/\d+/g)) - 10) / 2); var CONMod = Math.floor((parseInt(AbilityScores[1].match(/\d+/g)) - 10) / 2); var DEXMod = Math.floor((parseInt(AbilityScores[2].match(/\d+/g)) - 10) / 2); var INTMod = Math.floor((parseInt(AbilityScores[3].match(/\d+/g)) - 10) / 2); var WISMod = Math.floor((parseInt(AbilityScores[4].match(/\d+/g)) - 10) / 2); var CHAMod = Math.floor((parseInt(AbilityScores[5].match(/\d+/g)) - 10) / 2); AddAttribute("STR Mod", STRMod, Character.id); AddAttribute("CON Mod", CONMod, Character.id); AddAttribute("DEX Mod", DEXMod, Character.id); AddAttribute("INT Mod", INTMod, Character.id); AddAttribute("WIS Mod", WISMod, Character.id); AddAttribute("CHA Mod", CHAMod, Character.id); // ADD ATTACK POWERS createObj("ability", { name: "█▓▒░POWERS░▒▓█", description: "", action: "", istokenaction: false, characterid: Character.id }); var Powers = StatBlock.match(/&lt;MonsterPower(.*?)&lt;\/MonsterPower&gt;/g); var p = 0; var PowerString = ""; var MPName; var MPAction; var MPUsage; var MPUsageDetails; var MPRange; var MPRequire; var MPTrigger; var MPTarget; var MPAttackBonus; var MPDefense; var MPDamage; var MPOnHit; var MPOnMiss; var MPEffect; var MultiAttack; while (p &lt; Powers.length) { MPName = Powers[p].match(/&lt;\/Attacks&gt;(.*?)&lt;\/Type&gt;/g)[0].match(/&lt;Name&gt;(.*?)&lt;\/Name&gt;/g); MPAction = Powers[p].match(/&lt;Action&gt;(.*?)&lt;\/Action&gt;/g); MPUsage = Powers[p].match(/&lt;Usage&gt;(.*?)&lt;\/Usage&gt;/g); MPUsageDetails = Powers[p].match(/&lt;UsageDetails&gt;(.*?)&lt;\/UsageDetails&gt;/g); MPRange = Powers[p].match(/&lt;Range&gt;(.*?)&lt;\/Range&gt;/g); MPRequire = Powers[p].match(/&lt;Requirements&gt;(.*?)&lt;\/Requirements&gt;/g); MPTrigger = Powers[p].match(/&lt;Trigger&gt;(.*?)&lt;\/Trigger&gt;/g); MPTarget = Powers[p].match(/&lt;Targets&gt;(.*?)&lt;\/Targets&gt;/g); MPAttackBonus = Powers[p].match(/&lt;MonsterPowerAttackNumber FinalValue(.*?)'&gt;/g); MPDefense = Powers[p].match(/&lt;DefenseName&gt;(.*?)&lt;\/DefenseName&gt;/g); MPDamage = Powers[p].match(/&lt;Expression&gt;(.*?)&lt;\/Expression&gt;/g); MPOnHit = Powers[p].match(/&lt;Hit&gt;(.*?)&lt;\/Hit&gt;/g)[0].match(/&lt;Description&gt;(.*?)&lt;\/Description&gt;/g); MPOnMiss = Powers[p].match(/&lt;Miss&gt;(.*?)&lt;\/Miss&gt;/g)[0].match(/&lt;Description&gt;(.*?)&lt;\/Description&gt;/g); MPEffect = Powers[p].match(/&lt;Effect&gt;(.*?)&lt;\/Effect&gt;/g)[0].match(/&lt;Description&gt;(.*?)&lt;\/Description&gt;/g); MPName = MPName[0].split("&gt;")[1].split("&lt;")[0]; MPAction = (MPAction != null) ? MPAction[0].split("&gt;")[1].split("&lt;")[0] : ""; // Add the word 'Action' if it does not have it already... MPAction += (MPAction != "" && MPAction.indexOf("Action") == -1) ? " Action" : ""; MPUsage = (MPUsage != null) ? MPUsage[0].split("&gt;")[1].split("&lt;")[0] : ""; MPUsageDetails = (MPUsageDetails != null) ? MPUsageDetails[0].split("&gt;")[1].split("&lt;")[0] : ""; // If MPUsageDetails is longer than one character, don't add to MPUsage MPUsage += (MPUsageDetails.length === 1) ? " " + MPUsageDetails + "+" : ""; MPRange = (MPRange != null) ? MPRange[0].split("&gt;")[1].split("&lt;")[0] : ""; MPRequire = (MPRequire != null) ? MPRequire[0].split("&gt;")[1].split("&lt;")[0] : ""; MPTrigger = (MPTrigger != null) ? MPTrigger[0].split("&gt;")[1].split("&lt;")[0] : ""; MPTarget = (MPTarget != null) ? MPTarget[0].split("&gt;")[1].split("&lt;")[0] : ""; MPAttackBonus = (MPAttackBonus != null) ? parseInt(MPAttackBonus[0].match(/\d+/g)[0]) : ""; MPDefense = (MPDefense != null) ? MPDefense[0].split("&gt;")[1].split("&lt;")[0] : ""; MPDamage = (MPDamage != null) ? MPDamage[0].split("&gt;")[1].split("&lt;")[0] : ""; MPOnHit = (MPOnHit != null) ? MPOnHit[0].split("&gt;")[1].split("&lt;")[0] : ""; MPOnMiss = (MPOnMiss != null) ? MPOnMiss[0].split("&gt;")[1].split("&lt;")[0] : ""; MPEffect = (MPEffect != null) ? MPEffect[0].split("&gt;")[1].split("&lt;")[0] : ""; MultiAttack = (MPTarget.toLowerCase().indexOf("close burst") != -1 && MPTarget.toLowerCase().indexOf("area burst") != -1) ? "?{Number of Attacks|1}" : ""; // CREATE POWERSTRING PowerString = ""; if (USE_POWER_CARDS) { // USE POWER CARD FORMAT PowerString += "!power --format|dnd4e --emote|" + MonsterName.toUpperCase() + " --name|" + MPName; PowerString += " --usage|" + MPUsage + " --action|" + MPAction; PowerString += (MPRequire != "") ? " --Requirement|" + MPRequire : ""; PowerString += (MPTrigger != "") ? " --Trigger|" + MPTrigger : ""; PowerString += (MPRange != "") ? " --Range|" + MPRange : ""; PowerString += (MPTarget != "") ? " --Target(s)|" + MPTarget : ""; PowerString += (MPAttackBonus != "") ? " --attack" + MultiAttack + "|[[1d20 + " + MPAttackBonus + "]]" : ""; PowerString += (MPDefense != "") ? " --defense|" + MPDefense : ""; PowerString += (MPDamage != "") ? " --damage|[[" + MPDamage + "]] " + MPOnHit : ""; PowerString += (MPOnMiss != "") ? " --On Miss|" + MPOnMiss : ""; PowerString += (MPEffect != "") ? " --Effect|" + MPEffect : ""; PowerString += (MPUsageDetails.length &gt; 1) ? " --Recharge|" + MPUsageDetails : ""; } else { // NO POWER CARD FORMAT PowerString += '/emas ' + MonsterName.toUpperCase() + '\n'; PowerString += '/as "Power" ' + MPName + '\n'; PowerString += (MPRequire != "") ? '/as "Require" ' + MPRequire + '\n' : ''; PowerString += (MPTrigger != "") ? '/as "Trigger" ' + MPTrigger + '\n' : ''; PowerString += (MPRange != "") ? '/as "Range" ' + MPRange + '\n' : ''; PowerString += (MPTarget != "") ? '/as "Target" ' + MPTarget + '\n' : ''; PowerString += (MPAttackBonus != "") ? '/as "Attack" ' + MPAction + ' \n' : ''; PowerString += (MPAttackBonus != "") ? '/as "Attack" [[1d20 + ' + MPAttackBonus + ']] vs ' + MPDefense + '\n' : ''; PowerString += (MPDamage != "") ? '/as "Attack" [[' + MPDamage + ']] ' + MPOnHit + '\n' : ''; PowerString += (MPOnMiss != "") ? '/as "On Miss" ' + MPOnMiss + '\n' : ''; PowerString += (MPEffect != "") ? '/as "Effect" ' + MPEffect + '\n' : ''; PowerString += (MPUsageDetails.length &gt; 1) ? '/as "Recharge" ' + MPUsageDetails : ''; // Remove last newline... PowerString = PowerString.substring(0, PowerString.length-2); } // ADD POWER TO CHARACTER SHEET AddPower(MPName, PowerString, Character.id); p++; } // GET TRAINED SKILL NAMES var TrainedSkillsBlock = StatBlock.match(/&lt;Skills&gt;(.*?)&lt;\/Skills&gt;/g)[0]; var NumOfSkills = TrainedSkillsBlock.match(/FinalValue='(.*?)'/g).length; var SkillTraining = TrainedSkillsBlock.match(/&lt;Trained&gt;(.*?)&lt;\/Trained&gt;/g); var SkillNames = TrainedSkillsBlock.match(/&lt;Name&gt;(.*?)&lt;\/Name&gt;/g); var MonsterTraining = []; var MonsterSkills = []; var Trained; var k = 0; while (k &lt; NumOfSkills) { MonsterTraining.push(SkillTraining[k].split("&gt;")[1].split("&lt;")[0]); MonsterSkills.push(SkillNames[k * 2].split("&gt;")[1].split("&lt;")[0]); k++; } // ADD SKILLCHECK MACROS createObj("ability", { name: "█▓▒░SKILLS░▒▓█", description: "", action: "", istokenaction: false, characterid: Character.id }); var SkillList = ["Acrobatics", "Arcana", "Athletics", "Bluff", "Diplomacy", "Dungeoneering", "Endurance", "Heal", "History", "Insight", "Intimidate", "Nature", "Perception", "Religion", "Stealth", "Streetwise", "Thievery"]; var SkillMods = [DEXMod, INTMod, STRMod, CHAMod, CHAMod, INTMod, CONMod, WISMod, INTMod, WISMod, CHAMod, WISMod, WISMod, INTMod, DEXMod, CHAMod, DEXMod]; var Trained = 0; var ShowTrained = ""; var j = 0; while (j &lt; SkillList.length) { Trained = (MonsterTraining[MonsterSkills.indexOf(SkillList[j])] == "true") ? 5 : 0; ShowTrained = (Trained != 0) ? " (Trained)" : ""; AddSkill(MonsterName, SkillList[j], SkillMods[j] + Trained, Character.id, ShowTrained); // ADDS PASSIVE INSIGHT/PERCEPTION ATTRIBUTES if (SkillList[j] == "Insight") AddAttribute("Passive Insight", 10 + SkillMods[j] + Trained, Character.id); if (SkillList[j] == "Perception") AddAttribute("Passive Perception", 10 + SkillMods[j] + Trained, Character.id); j++; } // Add defenses as token actions... if (SHOW_DEFENSES) { AddPower("█▓▒░DEFENSES░▒▓█", "", Character.id); AddPower("AC " + DEF_AC, "", Character.id); AddPower("Fortitude " + DEF_FORT, "", Character.id); AddPower("Reflex " + DEF_REF, "", Character.id); AddPower("Will " + DEF_WILL, "", Character.id); } // SET TOKEN VALUES Token.set("represents", Character.id); Token.set("name", MonsterName); Token.set("showplayers_name", true); Token.set("bar3_max", HitPoints); Token.set("bar3_value", HitPoints); Token.set("showplayers_bar3", true); Token.set("aura1_radius", "0"); Token.set("aura1_color", "#660000"); Token.set("aura1_square", true); Token.set("aura2_radius", "0"); Token.set("aura2_color", "#660000"); Token.set("aura2_square", true); Token.set("gmnotes", ""); } }); function AddAttribute(attr, value, charid) { if (attr === "Hit Points") { createObj("attribute", { name: attr, current: value, max: value, characterid: charid }); } else { createObj("attribute", { name: attr, current: value, characterid: charid }); } return; } function AddSkill(monstername, skill, value, charid, showtrained) { createObj("ability", { name: skill + showtrained, description: "", action: '/as "' + monstername + '" ' + skill + ' [[1d20 + ' + value + ']]', istokenaction: false, characterid: charid }); } function AddPower(mpname, powerstring, charid) { createObj("ability", { name: mpname, description: "", action: powerstring, istokenaction: true, characterid: charid }); }
1394480697

Edited 1394480880
Could just check if the GMNotes starts with the expected XML every time a change is recorded to a character. Skip the step of running a macro. Player XML from the Character Builder is nearly identical, IIRC. Edit: And excellent work! Glad someone finally made this.
John M. said: Could just check if the GMNotes starts with the expected XML every time a change is recorded to a character. Skip the step of running a macro. Player XML from the Character Builder is nearly identical, IIRC. Edit: And excellent work! Glad someone finally made this. Thanks. It has some issues... some powers don't have the damage where I expected... so DM's will need to tweak the powers a little, but it won't be nearly as much work as making a monster from scratch. I'm adding in checks for bursts and blasts so that the script automatically adds in the prompt for multiple attacks in the Power Card part. As for auto-creating the monsters when xml is found... I dunno. I could, but the macro is more satisfying.
Currently trying to import a Kobold Horde as a test but it keeps going off the error: TypeError: Cannot call method 'get' of undefined at evalmachine.&lt;anonymous&gt;:1359:19 at eval ( UNsure as to why right now,
Hrm, let me check.
It worked for me... you are opening the .monster file from DDI Adventure Tools in notepad and the pasting the text into the GM Notes of the token?
It should look something like... &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;Monster xsi:type="Monster" xmlns:loader="<a href="http://www.wizards.com/listloader" rel="nofollow">http://www.wizards.com/listloader</a>" xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance" rel="nofollow">http://www.w3.org/2001/XMLSchema-instance</a>"&gt; ... &lt;ID xsi:type="ContentIdentifier" /&gt; &lt;Name&gt;Kobold Horde&lt;/Name&gt; &lt;/Monster&gt;
I did just that, does it require another script to work? Shall I post a screenshot of it?
1394490548

Edited 1394490847
No, it should work just fine... let me double check. edit - Just did a second check... pasted in the script from the post and it works for me.
Weird, I don't know what's causing this, I did the same, repaste the code, now trying a Kobold Minion, thought it'd be simpler but no dice, here's a screenshot, just to make sure I'm doing it right. This time it spouted the error TypeError: Cannot call method 'get' of undefined at evalmachine.&lt;anonymous&gt;:226:19 at eval (
Do you have any other scripts running?
Aha! I figured out the problem... Use: @{selected|token_id} instead of @{selected||token_id} That is totally my fault.
1394492228

Edited 1394492251
Okay tested on the horde, it works, the minions though got this though TypeError: Cannot read property '0' of null at Sandbox.&lt;anonymous&gt; (evalmachine.&lt;anonymous&gt;:314:66) at eval ( unsure as to why, but it works for some monsters at least which will help, thanks for the help! Edit: I disbaled all other scripts too
I had no trouble with the Kobold Minion, though it did grab two Javelin powers and for some reason WotC left a damage expression in the Kobold shifty powers.
1394493920

Edited 1394493980
Okay after a few tests, including a custom mob, I've customized it so that the current Auto, damage script I use for players moves works on them correctly. It all works and I will use it in the future, even if it has no auto damage per-say, it's a great tool and will save me a lot of time in the future.
Minor Update (Mar 10 2014 - 9:45 pm edt): Added support for distinguishing between single target and multi-target attack powers.
HoneyB this is some awesome stuff. I can't wait to try it out. With regards to the messy XML from WOTC, I am making some progress on my little webapp. It will hopefully provide a good intermediate step in the process so that crappy xml can be handled manually before taking it over to roll20 for import
Just tested it out a bit. It's working very well for me so far. I've tried 4 monsters and they all worked well Here is one bug that I noticed I accidentally clicked the macro on a token that didn't have the xml in yet and it threw an error which shut down the api. Catching this or checking for null/empty string would fix this I think. This is amazing and I am so grateful that you put the time into writing all of this. Thanks so much. pugz
Ah yeah... I'm dumb and forgot that. Will add that here in a bit.
Minor Update (Mar 11 2014 - 2:00 pm edt): Added a check for a blank entry in gmnotes on the selected token after the first line: if (Token.get("subtype") != "token") return; if (Token.get("gmnotes").indexOf("xml") == -1) return;
After testing it with pre-made, and custom-made monsters. I can say that this is an awesome script now and have made encounters quickly with ease.
Calhanol said: After testing it with pre-made, and custom-made monsters. I can say that this is an awesome script now and have made encounters quickly with ease. Awesome! ^_^ Please remember to post any monsters that have issues with how they import. That way I can attemp to add checks in to catch those outliers.
Everything up to "if (command == !build monster)..." Is the power card scripting, correct? Would it still function as intended without that in there? (I wouldn't want it to conflict with the 4e combat script.. which already uses your power card output)
Yeah, it would.
HoneyBadger said: Yeah, it would. Awesome. You're awesome. Just sayin'
Well actually... after looking at what Alex is doing with his Combat Automation script... the monster importer will not be compatible with his CA script. Only my Power Cards.
1394704379
Alex L.
Pro
Sheet Author
HoneyBadger said: Well actually... after looking at what Alex is doing with his Combat Automation script... the monster importer will not be compatible with his CA script. Only my Power Cards. Year it would be hard to get anything more than the basic Attack and damage, mainly because their is no syntax to the Hit and effect texts.
1394709214

Edited 1394709327
Actually the xml for monsters is pretty good, minus a few quibbles like Kobolds having a damage expression included in their Shifty minor action. My importer does a pretty good job of capturing all the monsters attack details. I'm going to work on adding auras and such next.
that's unfortunate. Oh well, at least it automatically imports their stats.. guess I can just do the macros myself. It still saves a bunch of time.
I made some changes a couple of days ago to the importer formatting to work with Alex's Combat Automation script. It still requires some cleanup but it captures most of the power's text with the correct preceding formatting. You have to manually enter any hit, miss or effect status markers but it will import the text. Changes to the power formatting: // CREATE POWERSTRING PowerString = "/emas \"" + MonsterName + "\" attacks!\n"; PowerString += "!power @{selected|token_id} \"" + MPName + "\""; PowerString += " -u \"" + MPUsage + "\"" + " -A \"" + MPAction + "\""; PowerString += (MPRequire != "") ? " -s \"" + MPRequire + "\"" : ""; PowerString += (MPTrigger != "") ? " -R \"" + MPTrigger + "\"" : ""; PowerString += (MPRange != "") ? " -r \"" + MPRange + "\"" : ""; PowerString += (MPTarget != "") ? " -t @{target|token_id} -S \"" + MPTarget + "\"" : ""; PowerString += (MPAttackBonus != "") ? " -a " + "[[1d20 + " + MPAttackBonus + " + ?{Attack Modifiers|0}]]" : ""; PowerString += (MPDefense != "") ? " -v " + MPDefense : ""; PowerString += (MPDamage != "") ? " -d [[" + MPDamage + " + ?{Damage Modifiers|0}]]" : ""; PowerString += (MPOnHit != "") ? " -h \"" + MPOnHit + "\"" : ""; PowerString += (MPOnMiss != "") ? " -m \"" + MPOnMiss + "\"" : ""; PowerString += (MPEffect != "") ? " -e \"" + MPEffect + "\"" : ""; PowerString = PowerString.replace(/%u2019/g, "'") I'm a pretty novice coder so if I made any mistakes, please let me know.
Looks good Quetz, but that format won't be supported by Alex's script here soon.
HoneyBadger said: Looks good Quetz, but that format won't be supported by Alex's script here soon. Hopefully he doesn't mind tweaking it for the new version :P (I know I wouldn't mind that happening) Actually, on the topic of updates, how much work do you think you'll need to do with the upcoming character sheet update? I was just reading the new blog post and it seems there's going to be a lot more added.
Maybe not much at all... depending on how much control we have over the look of the new sheets. Could be a complete re-write if we can control the CSS... I could import the monster and style it just like the card.
Hm, well, on the one hand I'm hopeful that there's not a tone of work for you to do to adapt it to the new sheets.. on the other hand I think it would be awesome to have an imported CSS.. I guess, in the end, I'll be happy either way. This does save a ton of time in bringing in new monsters.
Certainly does.
Hrm... damn. Gotta re-do the skills import. There's more than one method they use in the XML. &gt;_&lt;
Is anyone else having a problem with the macros producing double cards?
1395144173
Alex L.
Pro
Sheet Author
James Y. said: Is anyone else having a problem with the macros producing double cards? Do you have Power Card install as well as this, as HB included it with this script.
Got skills working properly now. Adding a no powercard option now for people that don't want to use the power card script for whatever reason.
The no power card option for monsters got sidetracked... by a Character Importer for D&D 4e! Decided to go ahead after taking a look at the organization of the dnd4e character file and it's clean enough to make it easy to import. Just gotta do the grunt work of writing the script and testing it. I already have everything but powers importing for characters.
Can we have an option for an initiative macro that rolls to the tracker?
Ah, yeah... I guess I could do that. I wrote a script for myself that lets me add all my monsters to the turn order tracker with initiatives automatically rolled... mass initiative is way faster.
Also I sent you a PM in reference to this script.
All monsters using the importer use the same initiative attribute, so you could just make a campaign macro that shows up in the macro bar at the bottom. [[1d20 + @{selected|Initiative} &{tracker}]]
Major Update - Monster Importer no longer has Power Cards included. They are now two separate scripts. Major Update (Mar 23 2014 - 11:45 am edt): Added support for text only macros with no power card. Change USE_POWER_CARDS to true if you want to create monsters with pre-made power card macros. Otherwise you will get text only macros that do not require any other scripts to use. Major Update (Mar 23 2014 - 11:45 am edt): I have also removed the Power Card script from the Monster Importer. You must install the Power Card script separately to use power cards with monsters. Power Card Script - <a href="https://app.roll20.net/forum/post/673780/script-c" rel="nofollow">https://app.roll20.net/forum/post/673780/script-c</a>...
Anyone having any issues with the scripts? Monsters the break the script or have serious power import problems?
1396395519

Edited 1396396858
The only issue I ran into was that the power card update messed with the macros a bit after the monsters were imported. Which seems like a power card format thing where the --format|dnd4e may have to be added. I've tried it here: PowerString += "!power --format|dnd4e --emote|" + MonsterName.toUpperCase() + " --name|" + MPName; and it seems to work.
1396396845

Edited 1396397134
Yeah. Just adding that will fix monster macros. I totally forgot to edit the first post and add that when I updated Power Cards. It's done now.
Great scripts, by the way. They've sped up a lot about making and running a 4E game with roll20.
Awesome, glad it helps. :)
Hmm; I keep getting this error: TypeError: Cannot read property '0' of null at Sandbox.&lt;anonymous&gt; (evalmachine.&lt;anonymous&gt;:1177:78) at eval ( With and Without Power cards