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] Power Cards

October 19 (10 years ago)

James the DM said:

HoneyBadger, or any other big brain code monkeys that sees the Matrix.

A while back you had posted about having the "monsters status cards" show up in the chat, have you or anyone else came up with away to do this? All I see in the matrix is green lines of code, but it looks like HB's power card script could be modified to display the monster status in chat. I have a DDI account and could export the ".monster files" and have for many of the monsters I use, but it"s very mouse click based and slow. I all so have all the DDI power cards and creature cards in html format and love to have an importer for the format if it possible?

Jim

You could certainly convert the monster information into powercard format, but that's not something I have any interest in doing. It would take a lot of work.
October 19 (10 years ago)
I'm using the bio and detail sections of my character sheet to whisper the DM/GM all the info for creatures, but as Honey Badger is stating it is a lot of work. In total it is just the relevant information. To the point you shouldn't have to access the Monster Manual.
October 19 (10 years ago)

Edited October 19 (10 years ago)
I put all the monster info into my rollable tables as the name, set up the power card to randomly pick one, whisper it to me. I have my random encounter and all my info I need right there. Similar to what Wes did above with his random effects/wild surges.

Traps and Magic Item/Treasure determination are next.
October 20 (10 years ago)

Bryan K. said:

I'm using the bio and detail sections of my character sheet to whisper the DM/GM all the info for creatures, but as Honey Badger is stating it is a lot of work. In total it is just the relevant information. To the point you shouldn't have to access the Monster Manual.

I am not a script writer, not in any sense of the word... I almost have the monster importer scraping the .monster file for the link to the Compendium, but it is one step forward two back at this point.

Hope to have somthing soon.
October 20 (10 years ago)

OldSchoolChris said:

I put all the monster info into my rollable tables as the name, set up the power card to randomly pick one, whisper it to me. I have my random encounter and all my info I need right there. Similar to what Wes did above with his random effects/wild surges.

Traps and Magic Item/Treasure determination are next.

Very cool.
No matter what I script, none of my cards seem to show up

Here is some sample code:

!power --emote|@{selected|token_name} rams his dagger home --name|Short Sword --leftsub|Melee --rightsub|Main Hand --attack|1d20+10 --defense|AC --damage|1d4+9 --dmgtype|piercing

Even this doesn't show up. Can anyone tell me why?
October 21 (10 years ago)
You have to be in a campaign where the GM is a mentor and has installed this api script.
--attack|[[1d20r>18+?{Ranged|2}]] rerolls, but doesn't highlight, any ideas? Sorry, but I'm still new to the whole macro scripting
October 21 (10 years ago)
You need to add [HR18] in front of the roll you're wanting to highlight. For example... --attack|[[ [HR18] 1d20r>18+?{Ranged|2}]]
October 23 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
Based on that other thread discussion....

You could change this:
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 == "!power") {
		PowerCardScript.Process(msg);
	}
});

To this:
on("chat:message", function (msg) {
	// Exit if not an api command
	if (msg.type != "api") return;

	var command = msg.content.split(" ", 1);
	if(command == "!power") {

		// Get the API Chat Command
		var pMsg = _.clone(msg);
		pMsg.who = pMsg.who.replace(" (GM)", "");
		pMsg.content = pMsg.content.replace("(GM) ", "");

		PowerCardScript.Process(pMsg);
	}
});
October 25 (10 years ago)
vÍnce
Pro
Sheet Author
I know we can insert url's with hyperlinked text now, which sadly --whisper seems to ignore... Is it possible to create a link within a poswercard to open a handout. I tried using single brackets around my handout name [handout] but it just printed the text. Would --whisper also "mess" with this ability? Thanks
October 25 (10 years ago)
Nope. Handout links inside the chat window do not work.
October 25 (10 years ago)
vÍnce
Pro
Sheet Author
Thanks HB. I'm currently just pasting the handouts "ugly" URL and that works with w/out --whisper

One other question, I noticed that if I have a long block of text with carriage returns (paragraphs), anything after the first return is shown outside of the card... Other than making a custom tag per paragraph is there another method of showing the text with carriage returns? Thanks
October 25 (10 years ago)
Yeah. Use ^^ to create a carriage return.
October 25 (10 years ago)
vÍnce
Pro
Sheet Author
Thank you and thanks for your powercard script. Introducing my players to it for the first time tonight.
October 26 (10 years ago)
I see that rerolls can be done by adding an r? after the die roll like this: --damage|[[ 2d10r1 + 9 ]], but is there a way to set the value to a fixed number if you roll a number that qualifies to be rerolled? Here's why:

Avenging Resolution
Heroic Tier
Prerequisite: Avenger, oath of enmity power
Benefit: When you attack a target of your oath of enmity with a weapon attack, treat all rolls of a 1 or 2 on the damage dice as though the result were a 3 on the die.

Thanks!
October 26 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
You can setup a rollable table for each die type and roll those. I actually have a script that does this: https://app.roll20.net/forum/post/950142/script-we...

So, the idea is you'd setup a rollable table with the numbers 3,3,3,4,5,6 for a 1d6 with a minimum of 3.

For a single die, you can just use something like [[{d6,3}kh1]], but that doesn't work for multiple dice.
October 29 (10 years ago)
Falcon
Pro
Sheet Author
HB - Been using your PowerCards for everything. I ran into a weird error in the latest version. It won't let me use the --^0. Well I mean it works but when I use --^0attack or --^0damage it comes up as -undefined. I can use --^0 on custom tags though. For example test this:

!power --whisper| --emote|@{selected|token_name} fires his Longbow at @{target|token_name} --charid|@{selected|character_id} --name|@{selected|token_name} --leftsub|Longbow --rightsub|Range Attack --Target|@{target|token_name} --^Location|[[1t[Hit-Location]]] --^0attack|[[[HR 20] 1D20+@{RANGE BNS} + ?{Att Mods|0}]] vs. AC [[@{target|AC}]] --defense|[[@{target|AC}]] --^1Attack Modifier|[[?{Att Mods}]] --^0damage|[[1D8+@{DMG BNS}+ ?{Dmg Mods|0}]] of Piercing Damage --Critical|^* **20, x3** --^Damage Modifier|[[?{Dmg Mods}]] --Powers Applied|None
October 29 (10 years ago)
Falcon
Pro
Sheet Author
Also - what does --defense do? Is there any attack vs defense checking within the script??? I notice that --defense doesn't show anything in my above script.
October 29 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
--defense is intended for listing which of the 4 defenses the attack targets. It's a DnD 4th Edition thing. Would be one of: Def, Ref, Will, Fort
October 29 (10 years ago)
Falcon
Pro
Sheet Author
Got it. Thanks Aaron. Too bad. Because It would be awesome to have an attack (skill) roll and then a defense (target #) checking. if it rolls the number or higher then the --damage| is displayed (or even other custom tags) otherwise it doesn't show it. That would lower the size of the power scripts in the message box substantially.

You didn't hit? Then it's a 2 line Power Card. If you Hit - well here's the full script.

October 29 (10 years ago)
Falcon
Pro
Sheet Author
You could probably do that with a special tag. So -- is the standard tag identifier. For success you can have a --& or whatever to signify that those only show up if the success number is reached. Just one simple if/then could really make a difference.
October 29 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
For the ^0attack problem, it's because when he's storing the lookup (Line 157) for the attack line, it has the ^0 on the front:
{"format":"","^0attack":"$[[0]]","leftsub":"","rightsub":""}
but when he's looking it up (Lines 270, 271, 277, 281,293 and 303) he has stripped it off and so he's looking for the key 'attack', not '^0attack' and not finding it.

For the other commands, he has pre fetched it into the content local variable (Line 248) and so has the correct data when he's building the line later (Line 313 and 317).


Here's a patched version with the attack.
(Note: This does not fix it for multi attacks, since the matching is based on the first 6 characters of the tag (Line 91). Same goes for damage (line 97), multiroll (Line 103), and emote (Line 109).)

// VARIABLE & FUNCTION DECLARATIONS -- DO NOT ALTER!!
var PowerCardScript = PowerCardScript || {};
var getBrightness = getBrightness || {};
var hexDec = hexDec || {};




// USER CONFIGUATION
    var POWERCARD_DEFAULT_FORMAT = "";
    var POWERCARD_CUSTOM_EMOTE = false;
    var POWERCARD_USE_PLAYER_COLOR = false;
    var POWERCARD_SHOW_ATTACK_DAMAGE_TAGS = true;
    var POWERCARD_SHOW_ATTACK_DAMAGE_INFO = true;
    var POWERCARD_INLINE_ROLL_NO_HIGHLIGHT = false;
    var POWERCARD_INLINE_ROLL_COLOR_OVERRIDE = true;
    
    // ALLOW URLS IN POWERCARDS
    var ALLOW_URLS = false;
    var ALLOW_HIDDEN_URLS = false;
    
    // DEFINE SIZE & COLOR OF POWERCARD BORDER AND IF ROUNDED
    var POWERCARD_BORDER_SIZE = 1;
    var POWERCARD_BORDER_COLOR = "#000000";
    var POWERCARD_ROUNDED_CORNERS = true;
    
    // DEFINE IF CORNER OF INLINE ROLLS ARE ROUNDED
    var POWERCARD_ROUNDED_INLINE_ROLLS = true;
    
    // DEFINE COLORS FOR STANDARD INLINE ROLLS
    var POWERCARD_INLINE_BASE_BORDER = "#87850A";
    var POWERCARD_INLINE_BASE_BACKGROUND = "#FFFEA2";
    var POWERCARD_INLINE_BASE_TEXT_COLOR = "#000000";
    
    // DEFINE COLORS FOR MAXIMUM VALUE ROLLS
    var POWERCARD_INLINE_CRIT_BORDER = "#004400";
    var POWERCARD_INLINE_CRIT_BACKGROUND = "#88CC88";
    var POWERCARD_INLINE_CRIT_TEXT_COLOR = "#004400";


    // DEFINE COLOR FOR MINIMUM VALUE ROLLS
    var POWERCARD_INLINE_FAIL_BORDER = "#660000";
    var POWERCARD_INLINE_FAIL_BACKGROUND = "#FFAAAA";
    var POWERCARD_INLINE_FAIL_TEXT_COLOR = "#660000";
    
    // DEFINE COLORS FOR A ROLL WITH BOTH MINIMUM AND MAXIMUM VALUES IN THE ROLL
    var POWERCARD_INLINE_BOTH_BORDER = "#061539";
    var POWERCARD_INLINE_BOTH_BACKGROUND = "#8FA4D4";
    var POWERCARD_INLINE_BOTH_TEXT_COLOR = "#061539";


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 == "!power") {
		PowerCardScript.Process(msg);
	}
});


PowerCardScript.Process = function(msg){
	// DEFINE VARIABLES
	var n = msg.content.split(" --");
	var PowerCard = {};
	var DisplayCard = "";
	var NumberOfAttacks = 1;
	var NumberOfDmgRolls = 1;
	var NumberOfRolls = 1;
	var Tag = "";
	var Content = "";
	var Character = "";


    // MORE USER CONFIGURATION
	// DEFINE COLORS FOR ODD/EVEN ROWS
	var POWERCARD_ODD_ROW_BGCOLOR = "#CEC7B6"; // #CEC7B6 - Default light brown
	var POWERCARD_ODD_ROW_TXCOLOR = "#000000";
	var POWERCARD_EVEN_ROW_BGCOLOR = "#B6AB91"; // #B6AB91 - Default darker brown
	var POWERCARD_EVEN_ROW_TXCOLOR = "#000000";
	
	// SHOW AVATAR/IMAGE IN CUSTOM EMOTES
	var POWERCARD_SHOW_AVATAR = true;
	
	// SET THE DEFAULT FORMAT... Override with --format| in macro
	PowerCard.format = POWERCARD_DEFAULT_FORMAT;
	
	// CREATE POWERCARD OBJECT ARRAY
	n.shift();
	n.forEach(function(token){
		Tag = token.substring(0, token.indexOf("|"));
		Content = token.substring(token.indexOf("|") + 1);
		if (Tag.substring(0, 6).toLowerCase() === "attack") {
			var attacks = parseInt(Tag.substring(6));
			if(attacks && attacks >= NumberOfAttacks) {
				NumberOfAttacks = attacks;
				Tag = "attack";
			}
		} else if (Tag.substring(0, 6).toLowerCase() === "damage") {
			var dmgs = parseInt(Tag.substring(6));
			if(dmgs && dmgs >= NumberOfDmgRolls) {
				NumberOfDmgRolls = dmgs;
				Tag = "damage";
			}
		} else if (Tag.substring(0, 9).toLowerCase() === "multiroll") {
			var mrolls = parseInt(Tag.substring(9));
			if(mrolls && mrolls >= NumberOfRolls) {
				NumberOfRolls = mrolls;
				Tag = "multiroll";
			}
		} else if (Tag.substring(0,5).toLowerCase() === "emote" && Content.charAt(0) === "!") {
			POWERCARD_SHOW_AVATAR = false;
			Content = Content.replace("!", "");
		}
		
		// PARSE FOR INLINE FORMATTING
		var f;
		// LINE BREAK
		if (Content.indexOf("^^") != -1) Content = Content.replace(/\^\^/g, "<br><br>");
		// INDENT FIRST LINE
		if (Content.indexOf("^*") != -1) Content = Content.replace(/\^\*/g, "<span style='margin-left: 1em;'></span>");
		// BOLD
		if (Content.indexOf("**") != -1) {
			// Do stuff...
			f = 1;
			while (Content.indexOf("**") != -1) {
				Content = (f % 2 == 1) ? Content.replace("**", "<b>") : Content.replace("**", "</b>");
				f++;
			}
		}
		// ITALICS
		if (Content.indexOf("__") != -1) {
			// Do stuff...
			f = 1;
			while (Content.indexOf("__") != -1) {
				Content = (f % 2 == 1) ? Content.replace("__", "<i>") : Content.replace("__", "</i>");
				f++;
			}
		}
        // URL
        if (Content.indexOf("@@") != -1 && ALLOW_URLS) {
            if (ALLOW_HIDDEN_URLS) {
                f = 1;
                while (Content.indexOf("@@") != -1) {
                    Content = (f % 2 == 1) ? Content.replace("@@", "<a href='") : Content.replace("@@", "</a>");
                    if (f % 2 == 1) {
                        Content = Content.replace("@@", "<a href='");
                        Content = Content.replace("||", "' style='color:#000; text-decoration: underline;'>");
                    }
                    f++;
                }
			} else {
                while (Content.indexOf("@@") != -1) {
                    Content = (f % 2 == 1) ? Content.replace("@@", "<a>") : Content.replace("@@", "</a>");
                    f++;
                }
			}
        }
		PowerCard[Tag] = Content;
	});
	
	// ERROR CATCH FOR EMPTY EMOTE
	if (PowerCard.emote === "") PowerCard.emote = '" "';
	
	// CREATE CUSTOM EMOTE
	if (PowerCard.charid !== undefined && PowerCard.emote !== undefined && POWERCARD_CUSTOM_EMOTE) {
		Character = getObj("character", PowerCard.charid);
		if (POWERCARD_SHOW_AVATAR) {
			PowerCard.emote = "<div style='display: table-cell; width: 50px;'><img src='" + Character.get("avatar") + "' style='height: 50px; width: 50px;'></img></div><div style='display:table-cell; width: calc(100%-50px); vertical-align: middle; font-size: 12px; font-style: italic; text-align: center;'>" + PowerCard.emote + "</div>";
		} else {
			PowerCard.emote = "<div style='display:block; width: 100%; vertical-align: middle; font-size: 12px; font-style: italic; text-align: center;'>" + PowerCard.emote + "</div>";
		}
	}
	
	// CREATE WHISPER TARGET
	var WhisperTarget = "";
	if (PowerCard.whisper !== undefined) {
		WhisperTarget = (PowerCard.whisper.toLowerCase() == "self") ? msg.who : "gm";
	}
	
	// CREATE TITLE STYLE
	var TitleStyle = " font-family: Georgia; font-size: medium; font-weight: normal; letter-spacing: 0.25px; text-align: center; vertical-align: middle; padding: 2px 0px; margin: 0px 0px 0px -10px; border: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + ";";
	
	// ROUNDED CORNERS ON TOP OF POWER CARD
	TitleStyle += (POWERCARD_ROUNDED_CORNERS) ? " border-radius: 10px 10px 0px 0px;" : "";
	
	// LIST OF PRE-SET TITLE TEXT & BACKGROUND COLORS FOR D&D 4E
	var AtWill = " color: #FFF; background-color: #040;";
	var Encounter = " color: #FFF; background-color: #400;";
	var Daily = " color: #FFF; background-color: #444;";
	var Item = " color: #FFF; background-color: #e58900;";
	var Recharge = " color: #FFF; background-color: #004;";
    
    // SET PowerCard.bgcolor TO PLAYER'S COLOR IF .bgcolor IS EQUAL TO PLAYER
    // THIS OVERRIDES TXCOLOR
    if (PowerCard.bgcolor == "player") {
        PowerCard.bgcolor = getObj("player", msg.playerid).get("color");
        PowerCard.txcolor = (getBrightness(PowerCard.bgcolor) < (255/2)) ? "#FFF" : "#000";
    }
	
	// CHECK FOR PRESET TITLE COLORS
	if (!POWERCARD_USE_PLAYER_COLOR) {
		if (PowerCard.usage !== undefined && PowerCard.txcolor === undefined && PowerCard.bgcolor === undefined) {
			// PRESET TITLE COLORS
			TitleStyle += AtWill;
			if (PowerCard.usage.toLowerCase().indexOf("encounter") != -1) TitleStyle += Encounter;
			if (PowerCard.usage.toLowerCase().indexOf("daily") != -1) TitleStyle += Daily;
			if (PowerCard.usage.toLowerCase().indexOf("item") != -1) TitleStyle += Item;
			if (PowerCard.usage.toLowerCase().indexOf("recharge") != -1) TitleStyle += Recharge;
		} else {
			// CUSTOM TITLECARD TEXT & BACKGROUND COLORS
			TitleStyle += (PowerCard.txcolor !== undefined) ? " color: " + PowerCard.txcolor + ";" : " color: #FFF;";
			TitleStyle += (PowerCard.bgcolor !== undefined) ? " background-color: " + PowerCard.bgcolor + ";" : " background-color: #040;";
		}
	} else {
		// SET TITLE BGCOLOR TO PLAYER COLOR --- OVERRIDES ALL OTHER COLOR OPTIONS ---
		var PlayerBGColor = getObj("player", msg.playerid).get("color");
		var PlayerTXColor = (getBrightness(PlayerBGColor) < (255/2)) ? "#FFF" : "#000";
		TitleStyle += " color: " + PlayerTXColor + "; background-color: " + PlayerBGColor + ";";
	}
    	
	// DEFINE .leftsub and .rightsub
	if (PowerCard.leftsub === undefined) PowerCard.leftsub = (PowerCard.usage !== undefined) ? PowerCard.usage : "";
	if (PowerCard.rightsub === undefined) PowerCard.rightsub = (PowerCard.action !== undefined) ? PowerCard.action : "";
	var PowerCardDiamond = (PowerCard.leftsub === "" || PowerCard.rightsub === "") ? "" : " ♦ ";
	
	// BEGIN DISPLAYCARD CREATION
	PowerCard.title = PowerCard.title ? PowerCard.title.split("|").join("&" + "#013;") : PowerCard.title;
	DisplayCard += "<div style='" + TitleStyle + "' title='" + PowerCard.title + "'>" + PowerCard.name;
	DisplayCard += (PowerCard.leftsub !== "" || PowerCard.rightsub !== "") ? "<br><span style='font-family: Tahoma; font-size: small; font-weight: normal;'>" + PowerCard.leftsub + PowerCardDiamond + PowerCard.rightsub + "</span></div>" : "</div>";
	
	// ROW STYLE VARIABLES
	if (PowerCard.orowbg !== undefined) POWERCARD_ODD_ROW_BGCOLOR = PowerCard.orowbg;
	if (PowerCard.orowtx !== undefined) POWERCARD_ODD_ROW_TXCOLOR = PowerCard.orowtx;
	if (PowerCard.erowbg !== undefined) POWERCARD_EVEN_ROW_BGCOLOR = PowerCard.erowbg;
	if (PowerCard.erowtx !== undefined) POWERCARD_EVEN_ROW_TXCOLOR = PowerCard.erowtx;
	var OddRow = " background-color: " + POWERCARD_ODD_ROW_BGCOLOR + "; color: " + POWERCARD_ODD_ROW_TXCOLOR + ";";
	var EvenRow = " background-color: " + POWERCARD_EVEN_ROW_BGCOLOR + "; color: " + POWERCARD_EVEN_ROW_TXCOLOR + ";";
	var RowStyle = " margin: 0px 0px 0px -10px; padding: 5px; border-left: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-right: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-radius: 0px;";
	var RowBackground = OddRow;
	var RowNumber = 1;
	var Indent = 0;
	var KeyCount = 0;
	
	// KEY LOOP
	var Keys = Object.keys(PowerCard);
	var ReservedTags = "attack, damage, multiroll, text";
	var IgnoredTags = "format, emote, name, usage, action, defense, txcolor, bgcolor, leftsub, rightsub, ddn, desc, crit, title, whisper, orowbg, orowtx, erowbg, erowtx, charid, playercolor";
	Keys.forEach(function(Tag){
		Content = PowerCard[Tag];
		if (Tag.charAt(0) === "^") {
			Indent = (parseInt(Tag.charAt(1)) >= 0) ? Tag.charAt(1) : 1;
			Tag = (parseInt(Tag.charAt(1)) >= 0) ? Tag.substring(2) : Tag.substring(1);
			// Reset indent to 0 if ^0 is used... (Thanks to Rob J. on Roll20)
			if (Indent === "0") {
				RowStyle = " margin: 0px 0px 0px -10px; padding: 5px; border-left: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-right: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-radius: 0px;";
			} else {
				RowStyle = " margin: 0px 0px 0px -10px; padding: 5px; border-left: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-right: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-radius: 0px; padding-left: " + (Indent * 1.5) + "em;";
			}
		}
		
		// CHECK FOR RESERVED & IGNORED TAGS
		if (ReservedTags.indexOf(Tag) != -1) {
			// ATTACK ROLLS
			if (Tag.toLowerCase() == "attack") {
				for(var AttackCount = 0; AttackCount < NumberOfAttacks; AttackCount++){
					RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
					RowNumber += 1;
					switch (PowerCard.format) {
						case "dnd4e": {
							if (AttackCount === 0) {
								Content = Content.replace("]]", "]] vs " + PowerCard.defense + " ");
								DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + Content + "</div>";
							} else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + "$[a" + AttackCount + "] vs " + PowerCard.defense + " </div>";
							break;
						}
						case "dnd5e":
						case "ddn": {
							DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + Content + " vs Armor Class</div>";
						break;
						}
						default: {
							if (AttackCount === 0) DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + Content + "</div>";
							else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + "$[a" + AttackCount + "]</div>";
						}
					}
				}
			}
			
			// DAMAGE ROLLS
			if (Tag.toLowerCase() == "damage") {
				for (var DamageCount = 0; DamageCount < NumberOfDmgRolls; DamageCount++) {
					RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
					RowNumber += 1;
					if (DamageCount === 0) DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Hit:</b> " : "") + Content + "</div>";
					else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>$[d" + DamageCount + "]</div>";
				}
			}
			
			// MULTIROLLS
			if (Tag.toLowerCase() == "multiroll") {
				for (var MultiRoll = 0; MultiRoll < NumberOfRolls; MultiRoll++) {
					RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
					RowNumber += 1;
					if (MultiRoll === 0) DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + Content + "</div>";
					else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>$[m" + MultiRoll + "]</div>";
				}
			}
		} else if (IgnoredTags.indexOf(Tag.toLowerCase()) != -1) {
			// Do nothing
		} else {
			if (Tag.toLowerCase().substring(0,4) == "text") {
				RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
				RowNumber += 1;
				DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + Content + "</div>";
			} else {
				RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
				RowNumber += 1;
				DisplayCard += "<div style='" + RowStyle + RowBackground + "'><b>" + Tag + ":</b> " + Content + "</div>";
			}
		}
		KeyCount++;
	});
	
	// ADD ROUNDED CORNERS & BORDER TO BOTTOM OF POWER CARD
	if (POWERCARD_ROUNDED_CORNERS && KeyCount == (Keys.length)) DisplayCard = DisplayCard.replace(/border-radius: 0px;(?!.*border-radius: 0px;)/g, "border-radius: 0px 0px 10px 10px; border-bottom: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + ";");
	if (!POWERCARD_ROUNDED_CORNERS && POWERCARD_BORDER_SIZE) DisplayCard = DisplayCard.replace(/border-radius: 0px;(?!.*border-radius: 0px;)/g, "border-bottom: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + ";");
	
	// INLINE ROLLS REPLACEMENT
	if (msg.inlinerolls !== undefined) {
		// Process inline rolls...
		for (var i = 0; i < msg.inlinerolls.length; i++){
			var inlinerollValue = buildinline(msg.inlinerolls[i], PowerCard.crit);
			DisplayCard = DisplayCard.replace("$[[" + i + "]]", inlinerollValue);
		}
		
		// Process multirolls...
		var content = PowerCard.attack;
		var idx = content ? content.substring(content.indexOf("$[[") + 3, content.indexOf("]]")) : false;
		var exp = idx && msg.inlinerolls[idx] ? msg.inlinerolls[idx].expression : undefined;
		var attExp = buildExpression(NumberOfAttacks, "a", exp);
		
		content = PowerCard.damage;
		idx = content ? content.substring(content.indexOf("$[[") + 3, content.indexOf("]]")) : false;
		exp = idx && msg.inlinerolls[idx] ? msg.inlinerolls[idx].expression : undefined;
		var dmgExp = buildExpression(NumberOfDmgRolls, "d", exp);
		
		content = PowerCard.multiroll;
		idx = content ? content.substring(content.indexOf("$[[") + 3, content.indexOf("]]")) : false;
		exp = idx && msg.inlinerolls[idx] ? msg.inlinerolls[idx].expression : undefined;
		var mltExp = buildExpression(NumberOfRolls, "m", exp);
		
		// Process multiroll results...
		var rollExp = attExp + ";" + dmgExp + ";" + mltExp;
		sendChat("", rollExp, function(m){
			var i;
			var inlinerollValue;
			for (i = 1; i < NumberOfAttacks; i++){
				inlinerollValue = buildinline(m[0].inlinerolls[i], PowerCard.crit);
				DisplayCard = DisplayCard.replace("$[a" + i + "]", inlinerollValue);
			}
			var dIndex = 1;
			for (i = NumberOfAttacks; i < NumberOfAttacks + NumberOfDmgRolls-1 ; i++){
				inlinerollValue = buildinline(m[0].inlinerolls[i], PowerCard.crit);
				DisplayCard = DisplayCard.replace("$[d" + dIndex++ + "]", inlinerollValue);
			}
			var mIndex = 1;
			for (i = NumberOfAttacks + NumberOfDmgRolls-1; i < NumberOfAttacks+NumberOfDmgRolls+NumberOfRolls-2 ; i++){
				inlinerollValue = buildinline(m[0].inlinerolls[i], PowerCard.crit);
				DisplayCard = DisplayCard.replace("$[m" + mIndex++ + "]", inlinerollValue);
			}
			// SEND OUTPUT TO CHAT
			if (PowerCard.whisper !== "no" && PowerCard.whisper !== undefined) {
				if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
					sendChat("", "/desc ");
					sendChat("", "/direct " + PowerCard.emote);
				} else if (PowerCard.emote !== undefined) {
					sendChat(msg.who, "/emas " + PowerCard.emote);
				}
				sendChat("Power Card Script", "/w " + WhisperTarget + " " + DisplayCard);
			} else {
				if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
					sendChat("", "/desc ");
					sendChat("", "/direct " + PowerCard.emote);
				} else if (PowerCard.emote !== undefined) {
					sendChat(msg.who, "/emas " + PowerCard.emote);
				}
				sendChat("", "/direct " + DisplayCard);
			}
		});
	} else {
		// NO INLINE ROLLS
		if (PowerCard.whisper !== "no" && PowerCard.whisper !== undefined) {
			if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
				sendChat("", "/desc ");
				sendChat("", "/direct " + PowerCard.emote);
			} else if (PowerCard.emote !== undefined) {
				sendChat(msg.who, "/emas " + PowerCard.emote);
			}
			sendChat("Power Card Script", "/w " + WhisperTarget + " " + DisplayCard);
		} else {
			if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
				sendChat("", "/desc ");
				sendChat("", "/direct " + PowerCard.emote);
			} else if (PowerCard.emote !== undefined) {
				sendChat(msg.who, "/emas " + PowerCard.emote);
			}
			sendChat("", "/direct " + DisplayCard);
		}
	}
};


function buildinline(inlineroll){
	var InlineBorderRadius = (POWERCARD_ROUNDED_INLINE_ROLLS) ? 5 : 0;
	var InlineColorOverride = "";
	var values = [];
	var critRoll = false;
	var failRoll = false;
	var critCheck = false;
	var failCheck = false;
	var highRoll = false;
	var lowRoll = false;
	var noHighlight = false;
	
	inlineroll.results.rolls.forEach(function(roll){
		var result = processRoll(roll, critRoll, failRoll, highRoll, lowRoll, noHighlight);
		if (result.value.toString().indexOf("critsuccess") != -1) critCheck = true;
		if (result.value.toString().indexOf("critfail") != -1) failCheck = true;
		values.push(result.value);
		critRoll = result.critRoll;
		failRoll = result.failRoll;
		highRoll = result.highRoll;
		lowRoll = result.lowRoll;
		noHighlight = result.noHighlight;
	});
	
	// Overrides the default coloring of the inline rolls...
	if (POWERCARD_INLINE_ROLL_COLOR_OVERRIDE) {
		if (critCheck && failCheck) {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_BOTH_BACKGROUND + "; border-color: " + POWERCARD_INLINE_BOTH_BORDER + "; color: " + POWERCARD_INLINE_BOTH_TEXT_COLOR + ";";
		} else if (critCheck && !failCheck) {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_CRIT_BACKGROUND + "; border-color: " + POWERCARD_INLINE_CRIT_BORDER + "; color: " + POWERCARD_INLINE_CRIT_TEXT_COLOR + ";";
		} else if (!critCheck && failCheck) {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_FAIL_BACKGROUND + "; border-color: " + POWERCARD_INLINE_FAIL_BORDER + "; color: " + POWERCARD_INLINE_FAIL_TEXT_COLOR + ";";
		} else {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_BASE_BACKGROUND + "; border-color: " + POWERCARD_INLINE_BASE_BORDER + "; color: " + POWERCARD_INLINE_BASE_TEXT_COLOR + ";";
		}
	}
	
	// Temporary kludge to get table result...
	if (inlineroll.results.rolls[0].table !== undefined) inlineroll.results.total = inlineroll.results.rolls[0].results[0].tableItem.name;
	
	var rollOut = '<span style="text-align: center; vertical-align: text-middle; display: inline-block; min-width: 1.75em; border-radius: ' + InlineBorderRadius + 'px; padding: 0px 2px; ' + InlineColorOverride + '" title="Rolling ' + inlineroll.expression + ' = ' + values.join("");
	rollOut += '" class="a inlinerollresult showtip tipsy-n';
	rollOut += (critCheck && failCheck ? ' importantroll' : (critCheck ? ' fullcrit' : (failCheck ? ' fullfail' : ''))) + '">' + inlineroll.results.total + '</span>';
	return rollOut;
}


function buildExpression(numRolls, tag, expression){
	var rolls = [];
	for (var i = 1; i < numRolls; i++){
		rolls.push("[[" + expression +"]]");
	}
	return tag + ":" + rolls.join(" ");
}


function processRoll(roll, critRoll, failRoll, highRoll, lowRoll, noHighlight){
	if (roll.type === "C") {
		return {value: " " + roll.text + " "};
	} else if (roll.type === "L") {
		if (roll.text.indexOf("HR") != -1) highRoll = parseInt(roll.text.substring(2));
		else highRoll = false;
		if (roll.text.indexOf("LR") != -1) lowRoll = parseInt(roll.text.substring(2));
		else lowRoll = false;
		if (roll.text.indexOf("NH") != -1) {
			// Blocks highlight on an individual roll...
			noHighlight = true;
		}
		// Remove inline tags to reduce clutter...
		roll.text = roll.text.replace(/HR(\d+)/g, "");
		roll.text = roll.text.replace(/LR(\d+)/g, "");
		roll.text = roll.text.replace(/NH/g, "");
		if (roll.text !== "") roll.text = " [" + roll.text + "] ";
		return {value: roll.text, highRoll:highRoll, lowRoll:lowRoll, noHighlight:noHighlight};
	} else if (roll.type === "M") {
		roll.expr = roll.expr.toString().replace(/\+/g, " + ");
		return {value: roll.expr};
	} else if (roll.type === "R") {
		var rollValues = [];
		roll.results.forEach(function(result){
			if (result.tableItem !== undefined) {
				rollValues.push(result.tableItem.name);
			} else {
				// Reset critRoll and failRoll for next roll...
				critRoll = false;
				failRoll = false;
				// Turn off highlighting if true...
				if (POWERCARD_INLINE_ROLL_NO_HIGHLIGHT || noHighlight) {
					critRoll = false;
					failRoll = false;
				} else {
					if (highRoll !== false && result.v >= highRoll || result.v === roll.sides) critRoll = true;
					else if (lowRoll !== false && result.v <= lowRoll || result.v === 1) failRoll = true;
				}
				result.v = "<span class='basicdiceroll" + (critRoll ? ' critsuccess' : (failRoll ? ' critfail' : '')) + "'>" + result.v + "</span>";
				rollValues.push(result.v);
			}
		});
		return {value: "(" + rollValues.join(" + ") + ")", critRoll:critRoll, failRoll:failRoll, highRoll:highRoll, lowRoll:lowRoll, noHighlight:noHighlight};
	} else if (roll.type === "G") {
		var grollVal = [];
		roll.rolls.forEach(function(groll){
			groll.forEach(function(groll2){
var result = processRoll(groll2, highRoll, lowRoll, noHighlight);
				grollVal.push(result.value);
critRoll = critRoll || result.critRoll;
				failRoll = failRoll || result.failRoll;
				highRoll = highRoll || result.highRoll;
				lowRoll = lowRoll || result.lowRoll;
				noHighlight = noHighlight || result.noHighlight;
				});
				});
			return {value: "{" + grollVal.join(" ") + "}", critRoll:critRoll, failRoll:failRoll, highRoll:highRoll, lowRoll:lowRoll, noHighlight:noHighlight};
		}
		}
	

function getBrightness(hex) {
hex = hex.replace('#', '');
var c_r = hexDec(hex.substr(0, 2));
	var c_g = hexDec(hex.substr(2, 2));
	var c_b = hexDec(hex.substr(4, 2));
	return ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
	}
	



function hexDec(hex_string) {
hex_string = (hex_string + '').replace(/[^a-f0-9]/gi, '');
return parseInt(hex_string, 16);
	}
	

October 29 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter

Black Falcon said:

You could probably do that with a special tag. So -- is the standard tag identifier. For success you can have a --& or whatever to signify that those only show up if the success number is reached. Just one simple if/then could really make a difference.

You could, but HB is pretty anti-automation.
October 30 (10 years ago)

The Aaron said:

You can setup a rollable table for each die type and roll those. I actually have a script that does this: https://app.roll20.net/forum/post/950142/script-we...

So, the idea is you'd setup a rollable table with the numbers 3,3,3,4,5,6 for a 1d6 with a minimum of 3.

For a single die, you can just use something like [[{d6,3}kh1]], but that doesn't work for multiple dice.

Aaron, I installed your script for weighted dice and created the tables, and they roll manually in the chat window like you described (/r 2t[d4min3]). Nice!

So, is there a way I can substitute that roll in a power card macro? For example, if I had --damage|[[ 2d4 +2 ]] and wanted to use the 2t[d4min3] roll in place of the 2d4? When I do a straight substitution, I get 3, even though the mouseover of the result shows (3 + 3) + 2. How do you use rollable tables for inline rolls? Or is that even possible?

Thank you!
October 30 (10 years ago)
Falcon
Pro
Sheet Author

The Aaron said:

Black Falcon said:

You could probably do that with a special tag. So -- is the standard tag identifier. For success you can have a --& or whatever to signify that those only show up if the success number is reached. Just one simple if/then could really make a difference.

You could, but HB is pretty anti-automation.

I can understand the desire for anti-automation - we don't want to do too much and make it a video game. But I think finding ways to shorten the output would be something of desire. My powercards are already running the full size of the chat window.

And thanks for the update. I don't use the multi-attack option so don't need to worry about it.
October 30 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter

Scott B. said:

The Aaron said:

You can setup a rollable table for each die type and roll those. I actually have a script that does this: https://app.roll20.net/forum/post/950142/script-we...

So, the idea is you'd setup a rollable table with the numbers 3,3,3,4,5,6 for a 1d6 with a minimum of 3.

For a single die, you can just use something like [[{d6,3}kh1]], but that doesn't work for multiple dice.

Aaron, I installed your script for weighted dice and created the tables, and they roll manually in the chat window like you described (/r 2t[d4min3]). Nice!

So, is there a way I can substitute that roll in a power card macro? For example, if I had --damage|[[ 2d4 +2 ]] and wanted to use the 2t[d4min3] roll in place of the 2d4? When I do a straight substitution, I get 3, even though the mouseover of the result shows (3 + 3) + 2. How do you use rollable tables for inline rolls? Or is that even possible?

Thank you!

Hmm... Good question. I hadn't even considered the inline roll on a powercard might be different. I'll have to try it tomorrow and see... Sorry about that.
October 30 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
Ok. I took a look at this. There's a line near the bottom that will take the first text label for a table item an use that instead of the die roll. I modified it in this version to work with tables regardless of if they are numeric or not:

So, for this call:
!power --name|test  --dice|[[2d6]] --Tables with Text|[[ 3t[Druid] ]] --Numeric Tables|[[ 2t[d6min4]+2t[d8min5] ]]


Tables with a numeric value as well as a text value get summed:


It's a little rough, and it might make more sense to Group Rolls from the same table together, or from the same inline roll. Discuss. =D

Modified source code:
// VARIABLE & FUNCTION DECLARATIONS -- DO NOT ALTER!!
var PowerCardScript = PowerCardScript || {};
var getBrightness = getBrightness || {};
var hexDec = hexDec || {};




// USER CONFIGUATION
    var POWERCARD_DEFAULT_FORMAT = "";
    var POWERCARD_CUSTOM_EMOTE = false;
    var POWERCARD_USE_PLAYER_COLOR = false;
    var POWERCARD_SHOW_ATTACK_DAMAGE_TAGS = true;
    var POWERCARD_SHOW_ATTACK_DAMAGE_INFO = true;
    var POWERCARD_INLINE_ROLL_NO_HIGHLIGHT = false;
    var POWERCARD_INLINE_ROLL_COLOR_OVERRIDE = true;
    
    // ALLOW URLS IN POWERCARDS
    var ALLOW_URLS = false;
    var ALLOW_HIDDEN_URLS = false;
    
    // DEFINE SIZE & COLOR OF POWERCARD BORDER AND IF ROUNDED
    var POWERCARD_BORDER_SIZE = 1;
    var POWERCARD_BORDER_COLOR = "#000000";
    var POWERCARD_ROUNDED_CORNERS = true;
    
    // DEFINE IF CORNER OF INLINE ROLLS ARE ROUNDED
    var POWERCARD_ROUNDED_INLINE_ROLLS = true;
    
    // DEFINE COLORS FOR STANDARD INLINE ROLLS
    var POWERCARD_INLINE_BASE_BORDER = "#87850A";
    var POWERCARD_INLINE_BASE_BACKGROUND = "#FFFEA2";
    var POWERCARD_INLINE_BASE_TEXT_COLOR = "#000000";
    
    // DEFINE COLORS FOR MAXIMUM VALUE ROLLS
    var POWERCARD_INLINE_CRIT_BORDER = "#004400";
    var POWERCARD_INLINE_CRIT_BACKGROUND = "#88CC88";
    var POWERCARD_INLINE_CRIT_TEXT_COLOR = "#004400";


    // DEFINE COLOR FOR MINIMUM VALUE ROLLS
    var POWERCARD_INLINE_FAIL_BORDER = "#660000";
    var POWERCARD_INLINE_FAIL_BACKGROUND = "#FFAAAA";
    var POWERCARD_INLINE_FAIL_TEXT_COLOR = "#660000";
    
    // DEFINE COLORS FOR A ROLL WITH BOTH MINIMUM AND MAXIMUM VALUES IN THE ROLL
    var POWERCARD_INLINE_BOTH_BORDER = "#061539";
    var POWERCARD_INLINE_BOTH_BACKGROUND = "#8FA4D4";
    var POWERCARD_INLINE_BOTH_TEXT_COLOR = "#061539";


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 == "!power") {
		PowerCardScript.Process(msg);
	}
});


PowerCardScript.Process = function(msg){
	// DEFINE VARIABLES
	var n = msg.content.split(" --");
	var PowerCard = {};
	var DisplayCard = "";
	var NumberOfAttacks = 1;
	var NumberOfDmgRolls = 1;
	var NumberOfRolls = 1;
	var Tag = "";
	var Content = "";
	var Character = "";


    // MORE USER CONFIGURATION
	// DEFINE COLORS FOR ODD/EVEN ROWS
	var POWERCARD_ODD_ROW_BGCOLOR = "#CEC7B6"; // #CEC7B6 - Default light brown
	var POWERCARD_ODD_ROW_TXCOLOR = "#000000";
	var POWERCARD_EVEN_ROW_BGCOLOR = "#B6AB91"; // #B6AB91 - Default darker brown
	var POWERCARD_EVEN_ROW_TXCOLOR = "#000000";
	
	// SHOW AVATAR/IMAGE IN CUSTOM EMOTES
	var POWERCARD_SHOW_AVATAR = true;
	
	// SET THE DEFAULT FORMAT... Override with --format| in macro
	PowerCard.format = POWERCARD_DEFAULT_FORMAT;
	
	// CREATE POWERCARD OBJECT ARRAY
	n.shift();
	n.forEach(function(token){
		Tag = token.substring(0, token.indexOf("|"));
		Content = token.substring(token.indexOf("|") + 1);
		if (Tag.substring(0, 6).toLowerCase() === "attack") {
			var attacks = parseInt(Tag.substring(6));
			if(attacks && attacks >= NumberOfAttacks) {
				NumberOfAttacks = attacks;
				Tag = "attack";
			}
		} else if (Tag.substring(0, 6).toLowerCase() === "damage") {
			var dmgs = parseInt(Tag.substring(6));
			if(dmgs && dmgs >= NumberOfDmgRolls) {
				NumberOfDmgRolls = dmgs;
				Tag = "damage";
			}
		} else if (Tag.substring(0, 9).toLowerCase() === "multiroll") {
			var mrolls = parseInt(Tag.substring(9));
			if(mrolls && mrolls >= NumberOfRolls) {
				NumberOfRolls = mrolls;
				Tag = "multiroll";
			}
		} else if (Tag.substring(0,5).toLowerCase() === "emote" && Content.charAt(0) === "!") {
			POWERCARD_SHOW_AVATAR = false;
			Content = Content.replace("!", "");
		}
		
		// PARSE FOR INLINE FORMATTING
		var f;
		// LINE BREAK
		if (Content.indexOf("^^") != -1) Content = Content.replace(/\^\^/g, "<br><br>");
		// INDENT FIRST LINE
		if (Content.indexOf("^*") != -1) Content = Content.replace(/\^\*/g, "<span style='margin-left: 1em;'></span>");
		// BOLD
		if (Content.indexOf("**") != -1) {
			// Do stuff...
			f = 1;
			while (Content.indexOf("**") != -1) {
				Content = (f % 2 == 1) ? Content.replace("**", "<b>") : Content.replace("**", "</b>");
				f++;
			}
		}
		// ITALICS
		if (Content.indexOf("__") != -1) {
			// Do stuff...
			f = 1;
			while (Content.indexOf("__") != -1) {
				Content = (f % 2 == 1) ? Content.replace("__", "<i>") : Content.replace("__", "</i>");
				f++;
			}
		}
        // URL
        if (Content.indexOf("@@") != -1 && ALLOW_URLS) {
            if (ALLOW_HIDDEN_URLS) {
                f = 1;
                while (Content.indexOf("@@") != -1) {
                    Content = (f % 2 == 1) ? Content.replace("@@", "<a href='") : Content.replace("@@", "</a>");
                    if (f % 2 == 1) {
                        Content = Content.replace("@@", "<a href='");
                        Content = Content.replace("||", "' style='color:#000; text-decoration: underline;'>");
                    }
                    f++;
                }
			} else {
                while (Content.indexOf("@@") != -1) {
                    Content = (f % 2 == 1) ? Content.replace("@@", "<a>") : Content.replace("@@", "</a>");
                    f++;
                }
			}
        }
		PowerCard[Tag] = Content;
	});
	
	// ERROR CATCH FOR EMPTY EMOTE
	if (PowerCard.emote === "") PowerCard.emote = '" "';
	
	// CREATE CUSTOM EMOTE
	if (PowerCard.charid !== undefined && PowerCard.emote !== undefined && POWERCARD_CUSTOM_EMOTE) {
		Character = getObj("character", PowerCard.charid);
		if (POWERCARD_SHOW_AVATAR) {
			PowerCard.emote = "<div style='display: table-cell; width: 50px;'><img src='" + Character.get("avatar") + "' style='height: 50px; width: 50px;'></img></div><div style='display:table-cell; width: calc(100%-50px); vertical-align: middle; font-size: 12px; font-style: italic; text-align: center;'>" + PowerCard.emote + "</div>";
		} else {
			PowerCard.emote = "<div style='display:block; width: 100%; vertical-align: middle; font-size: 12px; font-style: italic; text-align: center;'>" + PowerCard.emote + "</div>";
		}
	}
	
	// CREATE WHISPER TARGET
	var WhisperTarget = "";
	if (PowerCard.whisper !== undefined) {
		WhisperTarget = (PowerCard.whisper.toLowerCase() == "self") ? msg.who : "gm";
	}
	
	// CREATE TITLE STYLE
	var TitleStyle = " font-family: Georgia; font-size: medium; font-weight: normal; letter-spacing: 0.25px; text-align: center; vertical-align: middle; padding: 2px 0px; margin: 0px 0px 0px -10px; border: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + ";";
	
	// ROUNDED CORNERS ON TOP OF POWER CARD
	TitleStyle += (POWERCARD_ROUNDED_CORNERS) ? " border-radius: 10px 10px 0px 0px;" : "";
	
	// LIST OF PRE-SET TITLE TEXT & BACKGROUND COLORS FOR D&D 4E
	var AtWill = " color: #FFF; background-color: #040;";
	var Encounter = " color: #FFF; background-color: #400;";
	var Daily = " color: #FFF; background-color: #444;";
	var Item = " color: #FFF; background-color: #e58900;";
	var Recharge = " color: #FFF; background-color: #004;";
    
    // SET PowerCard.bgcolor TO PLAYER'S COLOR IF .bgcolor IS EQUAL TO PLAYER
    // THIS OVERRIDES TXCOLOR
    if (PowerCard.bgcolor == "player") {
        PowerCard.bgcolor = getObj("player", msg.playerid).get("color");
        PowerCard.txcolor = (getBrightness(PowerCard.bgcolor) < (255/2)) ? "#FFF" : "#000";
    }
	
	// CHECK FOR PRESET TITLE COLORS
	if (!POWERCARD_USE_PLAYER_COLOR) {
		if (PowerCard.usage !== undefined && PowerCard.txcolor === undefined && PowerCard.bgcolor === undefined) {
			// PRESET TITLE COLORS
			TitleStyle += AtWill;
			if (PowerCard.usage.toLowerCase().indexOf("encounter") != -1) TitleStyle += Encounter;
			if (PowerCard.usage.toLowerCase().indexOf("daily") != -1) TitleStyle += Daily;
			if (PowerCard.usage.toLowerCase().indexOf("item") != -1) TitleStyle += Item;
			if (PowerCard.usage.toLowerCase().indexOf("recharge") != -1) TitleStyle += Recharge;
		} else {
			// CUSTOM TITLECARD TEXT & BACKGROUND COLORS
			TitleStyle += (PowerCard.txcolor !== undefined) ? " color: " + PowerCard.txcolor + ";" : " color: #FFF;";
			TitleStyle += (PowerCard.bgcolor !== undefined) ? " background-color: " + PowerCard.bgcolor + ";" : " background-color: #040;";
		}
	} else {
		// SET TITLE BGCOLOR TO PLAYER COLOR --- OVERRIDES ALL OTHER COLOR OPTIONS ---
		var PlayerBGColor = getObj("player", msg.playerid).get("color");
		var PlayerTXColor = (getBrightness(PlayerBGColor) < (255/2)) ? "#FFF" : "#000";
		TitleStyle += " color: " + PlayerTXColor + "; background-color: " + PlayerBGColor + ";";
	}
    	
	// DEFINE .leftsub and .rightsub
	if (PowerCard.leftsub === undefined) PowerCard.leftsub = (PowerCard.usage !== undefined) ? PowerCard.usage : "";
	if (PowerCard.rightsub === undefined) PowerCard.rightsub = (PowerCard.action !== undefined) ? PowerCard.action : "";
	var PowerCardDiamond = (PowerCard.leftsub === "" || PowerCard.rightsub === "") ? "" : " ♦ ";
	
	// BEGIN DISPLAYCARD CREATION
	PowerCard.title = PowerCard.title ? PowerCard.title.split("|").join("&" + "#013;") : PowerCard.title;
	DisplayCard += "<div style='" + TitleStyle + "' title='" + PowerCard.title + "'>" + PowerCard.name;
	DisplayCard += (PowerCard.leftsub !== "" || PowerCard.rightsub !== "") ? "<br><span style='font-family: Tahoma; font-size: small; font-weight: normal;'>" + PowerCard.leftsub + PowerCardDiamond + PowerCard.rightsub + "</span></div>" : "</div>";
	
	// ROW STYLE VARIABLES
	if (PowerCard.orowbg !== undefined) POWERCARD_ODD_ROW_BGCOLOR = PowerCard.orowbg;
	if (PowerCard.orowtx !== undefined) POWERCARD_ODD_ROW_TXCOLOR = PowerCard.orowtx;
	if (PowerCard.erowbg !== undefined) POWERCARD_EVEN_ROW_BGCOLOR = PowerCard.erowbg;
	if (PowerCard.erowtx !== undefined) POWERCARD_EVEN_ROW_TXCOLOR = PowerCard.erowtx;
	var OddRow = " background-color: " + POWERCARD_ODD_ROW_BGCOLOR + "; color: " + POWERCARD_ODD_ROW_TXCOLOR + ";";
	var EvenRow = " background-color: " + POWERCARD_EVEN_ROW_BGCOLOR + "; color: " + POWERCARD_EVEN_ROW_TXCOLOR + ";";
	var RowStyle = " margin: 0px 0px 0px -10px; padding: 5px; border-left: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-right: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-radius: 0px;";
	var RowBackground = OddRow;
	var RowNumber = 1;
	var Indent = 0;
	var KeyCount = 0;
	
	// KEY LOOP
	var Keys = Object.keys(PowerCard);
	var ReservedTags = "attack, damage, multiroll, text";
	var IgnoredTags = "format, emote, name, usage, action, defense, txcolor, bgcolor, leftsub, rightsub, ddn, desc, crit, title, whisper, orowbg, orowtx, erowbg, erowtx, charid, playercolor";
	Keys.forEach(function(Tag){
		Content = PowerCard[Tag];
		if (Tag.charAt(0) === "^") {
			Indent = (parseInt(Tag.charAt(1)) >= 0) ? Tag.charAt(1) : 1;
			Tag = (parseInt(Tag.charAt(1)) >= 0) ? Tag.substring(2) : Tag.substring(1);
			// Reset indent to 0 if ^0 is used... (Thanks to Rob J. on Roll20)
			if (Indent === "0") {
				RowStyle = " margin: 0px 0px 0px -10px; padding: 5px; border-left: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-right: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-radius: 0px;";
			} else {
				RowStyle = " margin: 0px 0px 0px -10px; padding: 5px; border-left: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-right: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + "; border-radius: 0px; padding-left: " + (Indent * 1.5) + "em;";
			}
		}
		
		// CHECK FOR RESERVED & IGNORED TAGS
		if (ReservedTags.indexOf(Tag) != -1) {
			// ATTACK ROLLS
			if (Tag.toLowerCase() == "attack") {
				for(var AttackCount = 0; AttackCount < NumberOfAttacks; AttackCount++){
					RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
					RowNumber += 1;
					switch (PowerCard.format) {
						case "dnd4e": {
							if (AttackCount === 0) {
								PowerCard[Tag] = PowerCard[Tag].replace("]]", "]] vs " + PowerCard.defense + " ");
								DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + PowerCard[Tag] + "</div>";
							} else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + "$[a" + AttackCount + "] vs " + PowerCard.defense + " </div>";
							break;
						}
						case "dnd5e":
						case "ddn": {
							DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + PowerCard[Tag] + " vs Armor Class</div>";
						break;
						}
						default: {
							if (AttackCount === 0) DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + PowerCard[Tag] + "</div>";
							else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Attack:</b> " : "") + "$[a" + AttackCount + "]</div>";
						}
					}
				}
			}
			
			// DAMAGE ROLLS
			if (Tag.toLowerCase() == "damage") {
				for (var DamageCount = 0; DamageCount < NumberOfDmgRolls; DamageCount++) {
					RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
					RowNumber += 1;
					if (DamageCount === 0) DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + (POWERCARD_SHOW_ATTACK_DAMAGE_TAGS ? "<b>Hit:</b> " : "") + PowerCard[Tag] + "</div>";
					else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>$[d" + DamageCount + "]</div>";
				}
			}
			
			// MULTIROLLS
			if (Tag.toLowerCase() == "multiroll") {
				for (var MultiRoll = 0; MultiRoll < NumberOfRolls; MultiRoll++) {
					RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
					RowNumber += 1;
					if (MultiRoll === 0) DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + PowerCard[Tag] + "</div>";
					else DisplayCard += "<div style='" + RowStyle + RowBackground + "'>$[m" + MultiRoll + "]</div>";
				}
			}
		} else if (IgnoredTags.indexOf(Tag.toLowerCase()) != -1) {
			// Do nothing
		} else {
			if (Tag.toLowerCase().substring(0,4) == "text") {
				RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
				RowNumber += 1;
				DisplayCard += "<div style='" + RowStyle + RowBackground + "'>" + Content + "</div>";
			} else {
				RowBackground = (RowNumber % 2 == 1) ? OddRow : EvenRow;
				RowNumber += 1;
				DisplayCard += "<div style='" + RowStyle + RowBackground + "'><b>" + Tag + ":</b> " + Content + "</div>";
			}
		}
		KeyCount++;
	});
	
	// ADD ROUNDED CORNERS & BORDER TO BOTTOM OF POWER CARD
	if (POWERCARD_ROUNDED_CORNERS && KeyCount == (Keys.length)) DisplayCard = DisplayCard.replace(/border-radius: 0px;(?!.*border-radius: 0px;)/g, "border-radius: 0px 0px 10px 10px; border-bottom: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + ";");
	if (!POWERCARD_ROUNDED_CORNERS && POWERCARD_BORDER_SIZE) DisplayCard = DisplayCard.replace(/border-radius: 0px;(?!.*border-radius: 0px;)/g, "border-bottom: " + POWERCARD_BORDER_SIZE + "px solid " + POWERCARD_BORDER_COLOR + ";");
	
	// INLINE ROLLS REPLACEMENT
	if (msg.inlinerolls !== undefined) {
		// Process inline rolls...
		for (var i = 0; i < msg.inlinerolls.length; i++){
			var inlinerollValue = buildinline(msg.inlinerolls[i], PowerCard.crit);
			DisplayCard = DisplayCard.replace("$[[" + i + "]]", inlinerollValue);
		}
		
		// Process multirolls...
		var content = PowerCard.attack;
		var idx = content ? content.substring(content.indexOf("$[[") + 3, content.indexOf("]]")) : false;
		var exp = idx && msg.inlinerolls[idx] ? msg.inlinerolls[idx].expression : undefined;
		var attExp = buildExpression(NumberOfAttacks, "a", exp);
		
		content = PowerCard.damage;
		idx = content ? content.substring(content.indexOf("$[[") + 3, content.indexOf("]]")) : false;
		exp = idx && msg.inlinerolls[idx] ? msg.inlinerolls[idx].expression : undefined;
		var dmgExp = buildExpression(NumberOfDmgRolls, "d", exp);
		
		content = PowerCard.multiroll;
		idx = content ? content.substring(content.indexOf("$[[") + 3, content.indexOf("]]")) : false;
		exp = idx && msg.inlinerolls[idx] ? msg.inlinerolls[idx].expression : undefined;
		var mltExp = buildExpression(NumberOfRolls, "m", exp);
		
		// Process multiroll results...
		var rollExp = attExp + ";" + dmgExp + ";" + mltExp;
		sendChat("", rollExp, function(m){
			var i;
			var inlinerollValue;
			for (i = 1; i < NumberOfAttacks; i++){
				inlinerollValue = buildinline(m[0].inlinerolls[i], PowerCard.crit);
				DisplayCard = DisplayCard.replace("$[a" + i + "]", inlinerollValue);
			}
			var dIndex = 1;
			for (i = NumberOfAttacks; i < NumberOfAttacks + NumberOfDmgRolls-1 ; i++){
				inlinerollValue = buildinline(m[0].inlinerolls[i], PowerCard.crit);
				DisplayCard = DisplayCard.replace("$[d" + dIndex++ + "]", inlinerollValue);
			}
			var mIndex = 1;
			for (i = NumberOfAttacks + NumberOfDmgRolls-1; i < NumberOfAttacks+NumberOfDmgRolls+NumberOfRolls-2 ; i++){
				inlinerollValue = buildinline(m[0].inlinerolls[i], PowerCard.crit);
				DisplayCard = DisplayCard.replace("$[m" + mIndex++ + "]", inlinerollValue);
			}
			// SEND OUTPUT TO CHAT
			if (PowerCard.whisper !== "no" && PowerCard.whisper !== undefined) {
				if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
					sendChat("", "/desc ");
					sendChat("", "/direct " + PowerCard.emote);
				} else if (PowerCard.emote !== undefined) {
					sendChat(msg.who, "/emas " + PowerCard.emote);
				}
				sendChat("Power Card Script", "/w " + WhisperTarget + " " + DisplayCard);
			} else {
				if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
					sendChat("", "/desc ");
					sendChat("", "/direct " + PowerCard.emote);
				} else if (PowerCard.emote !== undefined) {
					sendChat(msg.who, "/emas " + PowerCard.emote);
				}
				sendChat("", "/direct " + DisplayCard);
			}
		});
	} else {
		// NO INLINE ROLLS
		if (PowerCard.whisper !== "no" && PowerCard.whisper !== undefined) {
			if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
				sendChat("", "/desc ");
				sendChat("", "/direct " + PowerCard.emote);
			} else if (PowerCard.emote !== undefined) {
				sendChat(msg.who, "/emas " + PowerCard.emote);
			}
			sendChat("Power Card Script", "/w " + WhisperTarget + " " + DisplayCard);
		} else {
			if (PowerCard.emote !== undefined && PowerCard.charid !== undefined && POWERCARD_CUSTOM_EMOTE) {
				sendChat("", "/desc ");
				sendChat("", "/direct " + PowerCard.emote);
			} else if (PowerCard.emote !== undefined) {
				sendChat(msg.who, "/emas " + PowerCard.emote);
			}
			sendChat("", "/direct " + DisplayCard);
		}
	}
};


function buildinline(inlineroll){
	var InlineBorderRadius = (POWERCARD_ROUNDED_INLINE_ROLLS) ? 5 : 0;
	var InlineColorOverride = "";
	var values = [];
	var critRoll = false;
	var failRoll = false;
	var critCheck = false;
	var failCheck = false;
	var highRoll = false;
	var lowRoll = false;
	var noHighlight = false;
	
	inlineroll.results.rolls.forEach(function(roll){
		var result = processRoll(roll, critRoll, failRoll, highRoll, lowRoll, noHighlight);
		if (result.value.toString().indexOf("critsuccess") != -1) critCheck = true;
		if (result.value.toString().indexOf("critfail") != -1) failCheck = true;
		values.push(result.value);
		critRoll = result.critRoll;
		failRoll = result.failRoll;
		highRoll = result.highRoll;
		lowRoll = result.lowRoll;
		noHighlight = result.noHighlight;
	});
	
	// Overrides the default coloring of the inline rolls...
	if (POWERCARD_INLINE_ROLL_COLOR_OVERRIDE) {
		if (critCheck && failCheck) {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_BOTH_BACKGROUND + "; border-color: " + POWERCARD_INLINE_BOTH_BORDER + "; color: " + POWERCARD_INLINE_BOTH_TEXT_COLOR + ";";
		} else if (critCheck && !failCheck) {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_CRIT_BACKGROUND + "; border-color: " + POWERCARD_INLINE_CRIT_BORDER + "; color: " + POWERCARD_INLINE_CRIT_TEXT_COLOR + ";";
		} else if (!critCheck && failCheck) {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_FAIL_BACKGROUND + "; border-color: " + POWERCARD_INLINE_FAIL_BORDER + "; color: " + POWERCARD_INLINE_FAIL_TEXT_COLOR + ";";
		} else {
			InlineColorOverride = " background-color: " + POWERCARD_INLINE_BASE_BACKGROUND + "; border-color: " + POWERCARD_INLINE_BASE_BORDER + "; color: " + POWERCARD_INLINE_BASE_TEXT_COLOR + ";";
		}
	}
	
	// Temporary kludge to get table result...
	inlineroll.results.tableentries=_.chain(inlineroll.results.rolls)
		.filter(function(r){
			return _.has(r,'table');
		})
		.reduce(function(memo,r){
			_.each(r.results,function(i) {
                i=i.tableItem;
				if( ! /^[+\-]?(0|[1-9][0-9]*)([.]+[0-9]*)?([eE][+\-]?[0-9]+)?$/.test(i.name)) {
                    
					memo.push({
						name: i.name,
						weight: i.weight,
						table: r.table
						});
				}
			});
			return memo;
		},[])
		.value();
	
	var rollOut = '<span style="text-align: center; vertical-align: text-middle; display: inline-block; min-width: 1.75em; border-radius: ' + InlineBorderRadius + 'px; padding: 0px 2px; ' + InlineColorOverride + '" title="Rolling ' + inlineroll.expression + ' = ' + values.join("");
	rollOut += '" class="a inlinerollresult showtip tipsy-n';
	rollOut += (critCheck && failCheck ? ' importantroll' : (critCheck ? ' fullcrit' : (failCheck ? ' fullfail' : ''))) + '">' + inlineroll.results.total + '</span>';
	rollOut= (inlineroll.results.total === 0 && inlineroll.results.tableentries.length) ? '' : rollOut;
	rollOut+=_.map(inlineroll.results.tableentries,function(l){
		return '<span style="text-align: center; vertical-align: text-middle; display: inline-block; min-width: 1.75em; border-radius: ' + InlineBorderRadius + 'px; padding: 0px 2px; ' + InlineColorOverride + '" title="Table: '+l.table+' '+'Weight: '+l.weight+'" class="a inlinerollresult showtip tipsy-n">'+l.name+'</span>';
	}).join('');
	return rollOut;
}


function buildExpression(numRolls, tag, expression){
	var rolls = [];
	for (var i = 1; i < numRolls; i++){
		rolls.push("[[" + expression +"]]");
	}
	return tag + ":" + rolls.join(" ");
}


function processRoll(roll, critRoll, failRoll, highRoll, lowRoll, noHighlight){
	if (roll.type === "C") {
		return {value: " " + roll.text + " "};
	} else if (roll.type === "L") {
		if (roll.text.indexOf("HR") != -1) highRoll = parseInt(roll.text.substring(2));
		else highRoll = false;
		if (roll.text.indexOf("LR") != -1) lowRoll = parseInt(roll.text.substring(2));
		else lowRoll = false;
		if (roll.text.indexOf("NH") != -1) {
			// Blocks highlight on an individual roll...
			noHighlight = true;
		}
		// Remove inline tags to reduce clutter...
		roll.text = roll.text.replace(/HR(\d+)/g, "");
		roll.text = roll.text.replace(/LR(\d+)/g, "");
		roll.text = roll.text.replace(/NH/g, "");
		if (roll.text !== "") roll.text = " [" + roll.text + "] ";
		return {value: roll.text, highRoll:highRoll, lowRoll:lowRoll, noHighlight:noHighlight};
	} else if (roll.type === "M") {
		roll.expr = roll.expr.toString().replace(/\+/g, " + ");
		return {value: roll.expr};
	} else if (roll.type === "R") {
		var rollValues = [];
		roll.results.forEach(function(result){
			if (result.tableItem !== undefined) {
rollValues.push(result.tableItem.name);
				} else {
			// Reset critRoll and failRoll for next roll...
				critRoll = false;
				failRoll = false;
				// Turn off highlighting if true...
				if (POWERCARD_INLINE_ROLL_NO_HIGHLIGHT || noHighlight) {
				critRoll = false;
					failRoll = false;
					} else {
				if (highRoll !== false && result.v >= highRoll || result.v === roll.sides) critRoll = true;
					else if (lowRoll !== false && result.v <= lowRoll || result.v === 1) failRoll = true;
					}
				result.v = "<span class='basicdiceroll" + (critRoll ? ' critsuccess' : (failRoll ? ' critfail' : '')) + "'>" + result.v + "</span>";
				rollValues.push(result.v);
				}
			});
		return {value: "(" + rollValues.join(" + ") + ")", critRoll:critRoll, failRoll:failRoll, highRoll:highRoll, lowRoll:lowRoll, noHighlight:noHighlight};
		} else if (roll.type === "G") {
	var grollVal = [];
		roll.rolls.forEach(function(groll){
		groll.forEach(function(groll2){
			var result = processRoll(groll2, highRoll, lowRoll, noHighlight);
				grollVal.push(result.value);
critRoll = critRoll || result.critRoll;
				failRoll = failRoll || result.failRoll;
				highRoll = highRoll || result.highRoll;
				lowRoll = lowRoll || result.lowRoll;
				noHighlight = noHighlight || result.noHighlight;
				});
				});
			return {value: "{" + grollVal.join(" ") + "}", critRoll:critRoll, failRoll:failRoll, highRoll:highRoll, lowRoll:lowRoll, noHighlight:noHighlight};
		}
		}
	

function getBrightness(hex) {
hex = hex.replace('#', '');
var c_r = hexDec(hex.substr(0, 2));
	var c_g = hexDec(hex.substr(2, 2));
	var c_b = hexDec(hex.substr(4, 2));
	return ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
	}
	



function hexDec(hex_string) {
hex_string = (hex_string + '').replace(/[^a-f0-9]/gi, '');
return parseInt(hex_string, 16);
	}
	

November 01 (10 years ago)
So Aaron, the inline rolls with rollable tables now work perfectly. However, the script no longer highlights high or low rolls (especially high rolls), even if I add [HR10] to a roll like this: --damage|[[ [HR10] 2t[d10min3] +5 ]]

In addition, the --charid|@{selected|character_id} now no longer puts the picture of the selected token in the chat window next to the emote text on any of the powercards. Did you possibly edit an older copy of HB's script, or was your change something that could have affected those other features?

Thanks,
Scott
November 01 (10 years ago)
vÍnce
Pro
Sheet Author
Can a player whisper a powercard to another player and if so, how? Also, is there a way to show the sender's name to the recipient without the sender having to select their own token and inserting @{selected|token_name} or equivalent in the powercard? For example when using /w the sender's name is shown to the recipient.

BTW I learned that you can use an attribute as a custom tag within a powercard, ex. --@{selected|token_name}| This is new to me and a pleasant discovery.
November 01 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter

Scott B. said:

So Aaron, the inline rolls with rollable tables now work perfectly. However, the script no longer highlights high or low rolls (especially high rolls), even if I add [HR10] to a roll like this: --damage|[[ [HR10] 2t[d10min3] +5 ]]

In addition, the --charid|@{selected|character_id} now no longer puts the picture of the selected token in the chat window next to the emote text on any of the powercards. Did you possibly edit an older copy of HB's script, or was your change something that could have affected those other features?

Thanks,
Scott

Hmm. I'll have to check. It's hard editing other people's scripts. =D
November 02 (10 years ago)
Custom emotes stopped working for you Scott, because the config variable var POWERCARD_CUSTOM_EMOTE is set to false in the edit Aaron posted.
November 02 (10 years ago)
The highlighted rolls using [HR#] stopped working because the numbers are generated in a function and the necessary variables aren't being passed to it (I think). Gotta go get ready for work. Might have time to work on this tomorrow.
November 03 (10 years ago)
Oh, right! Good catch, thank you. That was an easy fix. And also thank you for looking into the highlighted rolls when you get the chance. I saw a lot of your effort earlier in this thread on those rolls.
November 03 (10 years ago)
Yeah, not gonna get to this today or this week. Picked up some overtime tonight and have board game night at a friends house tomorrow night.
Hello, I am the user of a free account. How do I use these for me and my players? Do I need to be a subscriber?
November 05 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
Hi Raul, You need to be a Mentor Level Subscriber to get access to the API, then you can install this and other scripts and make use of them.
November 05 (10 years ago)
vÍnce
Pro
Sheet Author
Can a player whisper a powercard to another player and if so, how? Also, is there a way to show the sender's name to the recipient without the sender having to select their own token and inserting @{selected|token_name} or equivalent in the powercard? For example when using /w the sender's name is shown to the recipient.

Bump
November 05 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
Those are not currently features of the script but would be possible via the API.
November 05 (10 years ago)
vÍnce
Pro
Sheet Author
Roger that. Thanks Aaron.
Are multirolls broken?

http://puu.sh/cEHgG/73446d6f3e.png
November 06 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
He's using the --attack#| syntax.
November 06 (10 years ago)

Edited November 06 (10 years ago)
vÍnce
Pro
Sheet Author

The Aaron said:

He's using the --attack#| syntax.
I'm too eager to help, but not knowledgeable enough to do so. :-)

November 06 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
=D no worries! I missed it the first time and had to go back and look. =D
November 06 (10 years ago)

Edited November 06 (10 years ago)
It's basically the same thing as --attack?{Number of Attacks|1}|[[1d20]] it just doesn't have the roll prompt.

Edit:

I did --attack3|[[d20]] instead of --attack?{Number of Attacks|1}|[[1d20]] to simplify the macro, to eliminate as many potential reasons for the problem as I could.
November 06 (10 years ago)

Vince said:

Can a player whisper a powercard to another player and if so, how? Also, is there a way to show the sender's name to the recipient without the sender having to select their own token and inserting @{selected|token_name} or equivalent in the powercard? For example when using /w the sender's name is shown to the recipient.

Bump

Been busy with work... sorry. I haven't implemented whispers to anyone other than the GM or yourself at the moment. I will at some point.
November 06 (10 years ago)

Edited November 06 (10 years ago)

Jarret B. said:

Are multirolls broken?

http://puu.sh/cEHgG/73446d6f3e.png

Not as far as I know... !power --name|Multiroll Test --attack3|[[1d20]] produced three attack rolls for me.

Edit - Found the culprit... --format|dnd5e or setting var POWERCARD_DEFAULT_FORMAT = ""; to dnd5e is not working with multi-rolls. I think I did this /skipped adding it intentionally since multiattacks in D&D 5e roll damage for each attack. I should probably re-write the whole multi-roll stuff anyway and get rid of the autoformats. They're just confusing and make things more difficult.
November 06 (10 years ago)
I am trying to get Josh's "Conditions and Status Tracker" to work with HoneyBadger's "Power Cards" each script works alone but not together.

anyone have any thoughts on how to get them to work together, or maybe another Conditions and Status Tracker that will work with "Power Cards"

Thanks for making roll20 even better!

Jim
November 06 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
I'd be happy to jump in and see if I can figure out the issue if you wanna PM me a join link.