var PowerCards = PowerCards || (function () {
//'use strict';
// VERSION INFO
var PowerCards_Author = "Sky and Kurt Jaegers";
var PowerCards_Version = "3.8.11";
var PowerCards_LastUpdated = "2018-02-16";
// FUNCTION DECLARATIONS
var PowerCard = PowerCard || {};
// INLINE ROLL COLORS
var INLINE_ROLL_DEFAULT = " background-color: #FFFEA2; border-color: #87850A; color: #000000;";
var INLINE_ROLL_CRIT_LOW = " background-color: #FFAAAA; border-color: #660000; color: #660000;";
var INLINE_ROLL_CRIT_HIGH = " background-color: #88CC88; border-color: #004400; color: #004400;";
var INLINE_ROLL_CRIT_BOTH = " background-color: #8FA4D4; border-color: #061539; color: #061539;";
var INLINE_ROLL_STYLE = "text-align: center; font-size: 100%; display: inline-block; font-weight: bold; height: 1em; min-width: 1.75em; margin-top: -1px; margin-bottom: 1px; padding: 0px 2px; border: 1px solid; border-radius: 3px;";
// API COMMAND HANDLER
on("chat:message", function (msg) {
if (msg.type !== "api") {
return;
}
switch (msg.content.split(" ", 1)[0].toLowerCase()) {
case "!power":
case "!powercard":
case "!powercards":
var player_obj = getObj("player", msg.playerid);
msg.who = msg.who.replace(" (GM)", "");
msg.content = msg.content.replace(/
\n/g, ' ').replace(/({{)(.*)((\}\}))/, " $2 ").replace(/\[#\[/g, "[[").replace(/\]#\]/g, "]]");
PowerCard.Process(msg, player_obj);
break;
case "!power_version":
sendChat("", "/w " + msg.who + " You are using version " + PowerCards_Version + " of PowerCards, authored by " + PowerCards_Author + ", which was last updated on: " + PowerCards_LastUpdated + ".");
break;
case "!pc_showem":
var attrs = filterObjs(function (z) {
return (z.get("characterid") == (msg.content.split(" ")[1]) && z.get("name").startsWith(msg.content.split(" ")[2]));
});
log(attrs);
break;
}
});
// LOAD POWERCARD FORMATS
on("ready", function () {
getPowerCardHandouts();
getPowerCardStatusList();
log("-=> PowerCards v" + PowerCards_Version + " <=- [" + PowerCards_LastUpdated + "]");
//log (Date.now().toString().substr(0, 10));
});
on("change:handout", function () {
getPowerCardHandouts();
getPowerCardStatusList();
});
// POWERCARD
PowerCard.Process = function (msg, player_obj) {
// USER CONFIGURATION
var ALLOW_URLS = false; // Allows players to include full urls in powercards
var ALLOW_HIDDEN_URLS = false; // Allows players to hide urls as a link instead
var CUSTOM_EMOTES = true; // Set to true to use custom emotes instead of Roll20 emotes
var SHOW_AVATAR = true; // Set to false to hide character sheet avatar in custom emotes
var SHOW_TARGET_AVATAR = true; // Set to false to hide target's avatar in custom emotes
var USE_DEFAULT_FORMAT = false; // Set to true if you want powercards to default formatting
var USE_PLAYER_COLOR = false; // Set to true to override all color formatting
// REPLACE INLINE ROLLS WITH EXPRESSIONS
if (msg.inlinerolls !== undefined) {
var Count = 0;
while (Count < msg.inlinerolls.length) {
msg.content = msg.content.replace("$[[" + Count + "]]", ("[[" + msg.inlinerolls[Count].expression + " ]]").replace(/\[\[\[/g, "[[ ["));
Count++;
}
}
// DEFINE VARIABLES
var n = (player_obj) ? msg.content.replace("%%who%%", player_obj.get("displayname")).split(/\s+--/) : msg.content.split(/\s+--/);
var template_items = [];
var PowerCard = {};
var Tag = "";
var Content = "";
var TagCount = 0;
var TagRepeat = 0;
// PLACEHOLDER VARIABLES
var Avatar = "";
var Character = "";
var Character_ID = "";
var Token = "";
var TargetAvatar = "";
var TargetToken = "";
var TargetCharacter = "";
var TargetCharacter_ID = ""
// DEFAULT FORMATTING
var Display = "";
var PlayerBGColor = (player_obj) ? player_obj.get("color") : "#FFFFFF";
var PlayerTXColor = (getBrightness(PlayerBGColor) < (255 / 2)) ? "#FFFFFF" : "#000000";
var PlayerTXShadow = (getBrightness(PlayerBGColor) < (255 / 2)) ? "#000000" : "#FFFFFF";
PowerCard.titlefont = "Contrail One";
PowerCard.titlefontvariant = "normal";
PowerCard.titlefontshadow = "-1px -1px 0 " + PlayerTXShadow + ", 1px -1px 0 " + PlayerTXShadow + ", -1px 1px 0 " + PlayerTXShadow + ", 1px 1px 0 " + PlayerTXShadow + ";";
PowerCard.titlebackground = "linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, 0));";
PowerCard.subtitlefont = "Tahoma";
PowerCard.subtitlefontvariant = "normal";
PowerCard.bodyfont = "Helvetica";
PowerCard.titlefontsize = "1.2em; line-height: 1.2em";
PowerCard.subtitlefontsize = "11px";
PowerCard.bodyfontsize = "14px";
PowerCard.txcolor = PlayerTXColor;
PowerCard.bgcolor = PlayerBGColor;
PowerCard.erowtx = "#000000";
PowerCard.erowbg = "#B6AB91"; // #B6AB91 - Default darker brown
PowerCard.orowtx = "#000000";
PowerCard.orowbg = "#CEC7B6"; // #CEC7B6 - Default light brown
PowerCard.corners = 5; // Set to 0 to remove rounded corners from PowerCards
PowerCard.border = "1px solid #000000"; // size style #color
PowerCard.boxshadow = ""; // h-distance v-distance blur spread #color
PowerCard.lineheight = "1.1em";
PowerCard.emotefont = "font-family: Georgia, serif; font-weight: bold; ";
// CREATE POWERCARD OBJECT
n.shift();
// Build a list of attributes that will be mapped to replacement variables for characters
var replace_attrs = "CN|character_name;CID|character_id;PB|pb;MSTR|strength_mod;MINT|intelligence_mod;MWIS|wisdom_mod;MCON|constitution_mod;MDEX|dexterity_mod;MCHA|charisma_mod;"
replace_attrs += "STR|strength;INT|intelligence;WIS|wisdom;CON|constitution;DEX|dexterity;CHA|charisma;"
replace_attrs += "SBSTR|strength_save_bonus;SBINT|intelligence_save_bonus;SBWIS|wisdom_save_bonus;SBCON|constitution_save_bonus;SBDEX|dexterity_save_bonus;SBCHA|charisma_save_bonus;"
replace_attrs += "SAB|spell_attack_bonus;SSDC|spell_save_dc;R|race;C|class;L|level;"
replace_attrs += "SSL1T|lvl1_slots_total;SSL1E|lvl1_slots_expended;SSL2T|lvl2_slots_total;SSL2E|lvl2_slots_expended;SSL3T|lvl3_slots_total;SSL3E|lvl3_slots_expended;"
replace_attrs += "SSL4T|lvl4_slots_total;SSL4E|lvl4_slots_expended;SSL5T|lvl5_slots_total;SSL5E|lvl5_slots_expended;SSL6T|lvl6_slots_total;SSL6E|lvl6_slots_expended;"
replace_attrs += "SSL7T|lvl7_slots_total;SSL7E|lvl7_slots_expended;SSL8T|lvl8_slots_total;SSL8E|lvl8_slots_expended;SSL9T|lvl9_slots_total;SSL9E|lvl9_slots_expended"
var replacementItems = [];
// Check for --replacement, --inlinereplace, and --replaceattrs tags tags and load appropriate replacement sets.
for (var x = n.length - 1; x >= 0; x--) {
var spliceIt = false;
if (n[x].substring(0, 11) === "replacement") {
var replacementContent = n[x].substring(n[x].indexOf("|") + 1).trim();
var replacementList = replacementContent.split(";");
replacementList.forEach(function (replacer) {
var replacementKeys = (state.PowerCard_Replacements && state.PowerCard_Replacements[replacer] !== undefined) ? state.PowerCard_Replacements[replacer].split(";") : [];
for (var q = 0; q < replacementKeys.length; q++) {
replacementItems.push(replacementKeys[q].trim());
}
})
spliceIt = true;
}
if (n[x].substring(0, 13) === "inlinereplace") {
var replacementset = n[x].substring(n[x].indexOf("|") + 1).trim();
var replacementKeys = replacementset.split(";")
for (var q = 0; q < replacementKeys.length; q++) {
replacementItems.push(replacementKeys[q].trim());
}
spliceIt = true;
}
if (n[x].substring(0, 12) === "replaceattrs") {
try {
var replacementContent = n[x].substring(n[x].indexOf("|") + 1).trim();
var replacement_prefix = replacementContent.split("|")[0];
var replacement_charid = replacementContent.split("|")[1];
replace_attrs.split(";").forEach(function (attr) {
replacementItems.push(replacement_prefix + attr.split("|")[0] + "|" + getAttrByName(replacement_charid, attr.split("|")[1]));
});
if (getAttrByName(replacement_charid, "npc") == "1") {
replacementItems.push(replacement_prefix + "AC|" + getAttrByName(replacement_charid, "npc_ac"));
} else {
replacementItems.push(replacement_prefix + "AC|" + getAttrByName(replacement_charid, "ac"));
}
replacementItems.push(replacement_prefix + "SAM|" + getAttrByName(replacement_charid, getAttrByName(replacement_charid, "spellcasting_ability").replace(/\@\{/g, "").replace(/\}/g, "").replace(/\+/g, "")));
} catch (e) {}
spliceIt = true;
}
if (n[x].substring(0, 15) === "replaceattrlist") {
try {
var replacementContent = n[x].substring(n[x].indexOf("|") + 1).trim();
var replacement_prefix = replacementContent.split("|")[0];
var replacement_charid = replacementContent.split("|")[1];
var replacement_attibute_list = replacementContent.split("|")[2];
// Replace text in the replacements attribute list to let replacements in this tag be nested. Yes, this is weird...
for (var q = 0; q < replacementItems.length; q++) {
if (replacementItems[q].includes("|")) {
replacement_attibute_list = replacement_attibute_list.replace(new RegExp(RegExp.quote("~" + replacementItems[q].split("|")[0].trim() + "$"), "g"), replacementItems[q].split("|")[1].trim());
}
}
replacement_attibute_list.split(";").forEach(function (attr) {
replacementItems.push(replacement_prefix + attr + "|" + getAttrByName(replacement_charid, attr));
});
} catch (e) {}
spliceIt = true;
}
if (n[x].startsWith("replacespell")) {
var params = n[x].substring(n[x].indexOf("|") + 1).trim().split("|");
getSpellInfo(params[0], params[1]).forEach(function (a) {
replacementItems.push(a)
});
spliceIt = true;
}
if (n[x].startsWith("replacenpcaction")) {
var params = n[x].substring(n[x].indexOf("|") + 1).trim().split("|");
getNPCAction(params[0], params[1]).forEach(function (a) {
replacementItems.push(a)
});
spliceIt = true;
}
if (n[x].startsWith("replacenpclegendaryaction")) {
var params = n[x].substring(n[x].indexOf("|") + 1).trim().split("|");
getNPCLegendaryAction(params[0], params[1]).forEach(function (a) {
replacementItems.push(a)
});
spliceIt = true;
}
if (n[x].startsWith("replacepcattack")) {
var params = n[x].substring(n[x].indexOf("|") + 1).trim().split("|");
getPCAttack(params[0], params[1]).forEach(function (a) {
replacementItems.push(a)
});
spliceIt = true;
}
if (n[x].startsWith("replacesection")) {
var params = n[x].substring(n[x].indexOf("|") + 1).trim().split("|");
getSectionAttrs(params[0], params[1], params[2], params[3], params[4]).forEach(function (a) {
replacementItems.push(a)
});
spliceIt = true;
}
if (spliceIt) {
n.splice(x, 1);
}
}
// Get a list of all --template tags and their locations in the card.
var templateTags = [];
var templateInserts = [];
for (var x = 0; x < n.length; x++) {
if (n[x].substring(0, 8) === "template") {
templateTags.push(n[x]);
templateInserts.push(x);
}
}
// Process --template tags from the bottom of the card up, so we can insert items without impacting the order
// of items/other templates that appear earlier in the card. This is esier than keeping track of how many
// lines we add and adjusting some counter/pointer.
for (var x = templateTags.length - 1; x >= 0; x--) {
var TemplateContent = templateTags[x].substring(templateTags[x].indexOf("|") + 1).trim();
var TemplateName = TemplateContent;
var TemplateParameters = undefined;
var TemplateItems = [];
if (TemplateContent.includes("|")) {
TemplateName = TemplateContent.split("|")[0];
}
// If the content of a --tempate tag contains a | character, there are likely parameters following the
// template name. These are separated by semicolons (;), so split the remainder of the content string
// into parameter values.
if (TemplateName !== TemplateContent) {
TemplateParameters = TemplateContent.split("|")[1].trim().split(";");
}
// Retrieve the appropriate Template from the known templates list and split it into tags
var PowerCard_Templates = (state.PowerCard_Templates && state.PowerCard_Templates[TemplateName] !== undefined) ? state.PowerCard_Templates[TemplateName].split("--") : [];
// Build a tag/content pair for each tag in the template and store in an array in reverse order.
// We do this so that when we put the back into the "n" array, they come out in the order they are
// listed in the template.
PowerCard_Templates.forEach(function (a) {
Tag = a.substring(0, a.indexOf("|")).trim();
Content = a.substring(a.indexOf("|") + 1).trim().replace(/\[#\[/g, "[[").replace(/\]#\]/g, "]]");
if (TemplateParameters !== undefined) {
for (var i = 0; i < TemplateParameters.length; i++) {
Tag = Tag.replace(new RegExp(RegExp.quote("~" + i + "!"), "g"), TemplateParameters[i]);
Content = Content.replace(new RegExp(RegExp.quote("~" + i + "!"), "g"), TemplateParameters[i]);
// Check the tag and the content for replacement keys. If they exist, do the replacement from the defined replacement sets.
for (var q = 0; q < replacementItems.length; q++) {
if (replacementItems[q].includes("|")) {
Content = Content.replace(new RegExp(RegExp.quote("~" + replacementItems[q].split("|")[0].trim() + "$"), "g"), replacementItems[q].split("|")[1].trim());
Tag = Tag.replace(new RegExp(RegExp.quote("~" + replacementItems[q].split("|")[0].trim() + "$"), "g"), replacementItems[q].split("|")[1].trim());
}
}
}
}
// Optional Template Tags : Putting [ and ] at the front of a tag in a template can optionally hide the tag. If the brackets are empty,
// the tag won't be added to the card. If the brackets contain something that can be evaluated as a javascript expression, the tag will
// be added if the expression evaluates to true. If the brackets contains something that can't be evalualted as a tag, but aren't empty,
// the tag will be added to the card.
// Empty brackets - zero out the tag.
if (Tag.startsWith("[]")) {
Tag = ""
};
// Brackets contain something
if (Tag.startsWith("[")) {
// Extract the contents of the brackets
var testexpr = Tag.match(/\[(.*)\]/);
if (testexpr !== null) {
// Attempt to evaluate the expression
try {
if (eval(sanitize(testexpr[1])) === false) {
Tag = "";
} else {}
} catch (e) {}
} else {
Tag = "";
}
}
// Clean up [ and ] from the tag along with the expression
Tag = Tag.replace(/\[.*\]/g, "");
if (Tag !== "" && Content !== "") {
TemplateItems.unshift(Tag + "|" + Content);
}
});
// Remove the Template Entry from the card's tag array
n.splice(templateInserts[x], 1);
// Insert the template items into the tag array at the point the template used to be.
TemplateItems.forEach(function (entry) {
n.splice(templateInserts[x], 0, entry);
});
}
// Process replacements again, this time for all of the lines in the card (not just the Template lines)
for (var x = 0; x < n.length; x++) {
for (var q = 0; q < replacementItems.length; q++) {
if (replacementItems[q].includes("|")) {
n[x] = n[x].replace(new RegExp(RegExp.quote("~" + replacementItems[q].split("|")[0].trim() + "$"), "g"), replacementItems[q].split("|")[1].trim());
}
}
}
//_.each(n, function(a) { log(a); });
// Handle each tag, one at a time
_.each(n, function (a) {
Tag = a.substring(0, a.indexOf("|")).trim();
Content = a.substring(a.indexOf("|") + 1).trim();
if (Tag === "target_list")
Content = Content.split(" | ");
// Create a 5e OGL Sheet NPC Attributes LIST
if (Tag.includes("attribute_summary") && (getObj("character", Content) !== undefined)) {
Character_ID = Content;
Tag = "!attribute_summary";
Content = "
| **Str** " + getSheetAttr(Character_ID, "strength") + "(" + (getSheetAttr(Character_ID, "strength_mod") || "0") + ") | "; Content += "**Dex** " + getSheetAttr(Character_ID, "dexterity") + "(" + (getSheetAttr(Character_ID, "dexterity_mod") || "0") + ") | "; Content += "**Con** " + getSheetAttr(Character_ID, "constitution") + "(" + (getSheetAttr(Character_ID, "constitution_mod") || "0") + ") |
| **Int** " + getSheetAttr(Character_ID, "intelligence") + "(" + (getSheetAttr(Character_ID, "intelligence_mod") || "0") + ") | "; Content += "**Wis** " + getSheetAttr(Character_ID, "wisdom") + "(" + (getSheetAttr(Character_ID, "wisdom_mod") || "0") + ") | "; Content += "**Cha** " + getSheetAttr(Character_ID, "charisma") + "(" + (getSheetAttr(Character_ID, "charisma_mod") || "0") + ") |
| **AC** " + getSheetAttr(Character_ID, "AC") + " | "; Content += outputIfNotEmpty("**Challenge Rating** ", getSheetAttr(Character_ID, "npc_challenge"), " | |
/g, "").replace(/\<\/p\>/g, "
").split("
");
notes.forEach(function (notes) {
TemplateName = notes.substring(0, notes.indexOf(":")).replace(/ /g, "").trim();
TemplateContent = notes.substring(notes.indexOf(":") + 1).trim();
TemplateContent = TemplateContent.replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&").replace(/ /g, " ").trim();
if (TemplateName !== "" && TemplateContent !== "")
PowerCard_Templates[TemplateName] = " " + TemplateContent;
});
});
}
if (handout.get("name").startsWith("PowerCard Replacements")) {
var ReplacementName = "";
var ReplacementContent = "";
handout.get("notes", function (notes) {
notes = notes.replace(/\
/g, "").replace(/\<\/p\>/g, "
").split("
");
notes.forEach(function (notes) {
ReplacementName = notes.substring(0, notes.indexOf(":")).replace(/ /g, "").trim();
ReplacementContent = notes.substring(notes.indexOf(":") + 1).trim();
ReplacementContent = ReplacementContent.replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&").replace(/ /g, "").trim();
if (ReplacementName !== "" && ReplacementContent !== "") {
PowerCard_Replacements[ReplacementName] = " " + ReplacementContent;
}
});
});
}
if (handout.get("name").startsWith("PowerCard Formats")) {
var FormatName = "";
var FormatContent = "";
handout.get("notes", function (notes) {
notes = notes.replace(/\
/g, "").replace(/\<\/p\>/g, "
").split("
");
notes.forEach(function (notes) {
FormatName = notes.substring(0, notes.indexOf(":")).replace(/<(?:.|\n)*?>/g, "").replace(/ /g, "").trim();
FormatContent = notes.substring(notes.indexOf(":") + 1).replace(/<(?:.|\n)*?>/g, "").replace(/ /g, "").trim();
if (FormatName !== "" && FormatContent !== "")
PowerCard_Formats[FormatName] = " " + FormatContent;
});
});
}
});
state.PowerCard_Templates = PowerCard_Templates;
state.PowerCard_Replacements = PowerCard_Replacements;
state.PowerCard_Formats = PowerCard_Formats;
};
// Retrieves the contents of a handout named "PowerCard Status List" and parses the notes field
// to create a table of token status markers and their meanings/descriptions.
function getPowerCardStatusList() {
var PowerCard_FormatStatusList = findObjs({
_type : "handout",
name : "PowerCard Status List"
})[0] || findObjs({
_type : "handout",
name : "PowerCards Status List"
})[0];
if (PowerCard_FormatStatusList !== undefined) {
var PowerCard_StatusList = {};
var Status;
var StatusName = "";
var StatusIcon = "";
var StatusInfo = "";
PowerCard_FormatStatusList.get("notes", function (notes) {
notes = notes.split("
");
notes.forEach(function (notes) {
Status = notes.split("|");
if (!_.isEmpty(Status)) {
StatusName = Status[0];
StatusIcon = Status[1];
StatusInfo = Status[2];
if (StatusName !== "" && StatusName !== undefined) {
PowerCard_StatusList[StatusName] = {
"icon" : StatusIcon || "",
"info" : StatusInfo || ""
}
}
}
});
state.PowerCard_StatusList = PowerCard_StatusList;
});
}
};
function getTargetInfo(content, TargetList) {
// PARSE FOR TARGET INFO REPLACEMENT CHARMS
var Token = getObj("graphic", TargetList[0]);
if (Token === undefined)
return content;
var Character = getObj("character", Token.get("represents"));
// TOKEN CHARMS
return content.replace(/%%(.*?)%%/g, function (m, charm) {
var attr;
switch (charm) {
case 'token_name':
return Token.get('name');
case 'bar1':
case 'bar2':
case 'bar3':
return Token.get(charm + '_value');
case 'bar1_max':
case 'bar2_max':
case 'bar3_max':
return Token.get(charm);
default:
attr = getAttrByName(Character.id, charm);
attr = getAttrRefValues(Character.id, attr);
return (Character && attr) || 'ERROR';
}
});
};
// Instead of throwing an error or returning undefined, return an empty string if
// the attribute doesn't exist on the character.
function getAttrOrEmptyString(characterID, attributeName) {
var o = findObjs({
type : 'attribute',
characterid : characterID,
name : attributeName
})
if (!_.isEmpty(o)) {
return getAttrByName(characterID, attributeName);
} else {
return "";
}
}
// If "testvalue" is not undefined or an empty string, will return the result of
// adding the prefix, the testvalue, and the postfix together. Otherwise, the
// value of alternative is returned.
function outputIfNotEmpty(prefix, testvalue, postfix, alternative) {
if (testvalue !== undefined && testvalue !== "") {
return prefix + testvalue + postfix;
} else {
return alternative;
}
}
// Check the "character_sheet" attribute to determine which sheet is being used.
// Based on this information, map the 5E OGL sheet attribute names to the equivalent
// attributes for that sheet. Note that we assume the sheet in use is 5E OGL
function getSheetSpecificAttrName(characterID, OGLName) {
var sheet = "5E OGL";
var attr = getAttrOrEmptyString(characterID, "character_sheet");
if (attr == "") {
return OGLName;
} else {
if (attr.substr(0, 7) == "Shaped ") {
switch (OGLName) {
case "npc_ac":
return "AC";
case "npc_challenge":
return "challenge";
case "lvl":
return "spell_slots_level_";
case "_slots_total":
return "";
case "_slots_expended":
return "_remaining";
case "npc_senses":
return "senses_string";
case "npc_immunities":
return "damage_immunities";
case "npc_acrobatics_base":
return "acrobatics";
case "npc_animal_handling_base":
return "animalhandling";
case "npc_arcana_base":
return "arcana";
case "npc_athletics_base":
return "athletics";
case "npc_deception_base":
return "deception";
case "npc_history_base":
return "history";
case "npc_insight_base":
return "insight";
case "npc_intimidation_base":
return "intimidation";
case "npc_investigation_base":
return "investigation";
case "npc_medicine_base":
return "medicine";
case "npc_nature_base":
return "nature";
case "npc_perception_base":
return "perception";
case "npc_performance_base":
return "performance";
case "npc_persuasion_base":
return "persuasion";
case "npc_religion_base":
return "religion";
case "npc_sleight_of_hand_base":
return "sleightofhand";
case "npc_stealth_base":
return "stealth";
case "npc_survival_base":
return "survival";
default:
return OGLName;
}
}
}
}
// Uses getSheetSpecificAttrName to retrieve a character sheet attribute independant of the
// sheet being used by specifying he 5E OGL name. If the attribute doesn't exist, returns an
// empty string.
function getSheetAttr(characterID, attrName) {
if (!attrName.substr(0, 10) == "repeating_") {
return getAttrOrEmptyString(characterID, getSheetSpecificAttrName(characterID, attrName));
} else {
return getAttrByName(characterID, getSheetSpecificAttrName(characterID, attrName)) || "";
}
}
function getRepeatingCount(characterID, prefix, postfix) {
var attrs = findObjs({
type : 'attribute',
characterid : characterID
});
var attrCount = 0;
attrs.forEach(function (s) {
if (s.get("name").startsWith(prefix) && s.get("name").endsWith(postfix)) {
attrCount += 1;
}
});
return attrCount;
}
function safeSendChat(speakingAs, message, callback) {
try {
sendChat(speakingAs, message, callback);
} catch (e) {
sendChat("PowerCards", JSON.stringify(e));
}
}
function sortByKey(array, key) {
return array.sort(function (a, b) {
var x = a.get(key);
var y = b.get(key);
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
function getSpellInfo(charid, spellname) {
var return_set = [];
try {
var spell_prefix = filterObjs(function (z) {
return (z.get("characterid") == charid && z.get("name").endsWith("spellname"));
})
.filter(entry => entry.get("current") == spellname)[0]
.get("name").slice(0, -9);
} catch (e) {
return (return_set)
};
return_set.push("SP-NAME|" + getAttrByName(charid, spell_prefix + "spellname"));
return_set.push("SP-LEVEL|" + (spell_prefix.charAt(16) == "c" ? 0 : spell_prefix.charAt(16)));
return_set.push("SP-SCHOOL|" + getAttrByName(charid, spell_prefix + "spellschool"));
return_set.push("SP-RANGE|" + getAttrByName(charid, spell_prefix + "spellrange"));
return_set.push("SP-DURATION|" + getAttrByName(charid, spell_prefix + "spellduration"));
return_set.push("SP-ATTACK|" + getAttrByName(charid, spell_prefix + "spellattack"));
return_set.push("SP-DAMAGE|" + getAttrByName(charid, spell_prefix + "spelldamage"));
return_set.push("SP-DAMAGETYPE|" + getAttrByName(charid, spell_prefix + "spelldamagetype"));
return_set.push("SP-DAMAGE2|" + getAttrByName(charid, spell_prefix + "spelldamage2"));
return_set.push("SP-DAMAGETYPE2|" + getAttrByName(charid, spell_prefix + "spelldamagetype2"));
return_set.push("SP-HLDIE|" + getAttrByName(charid, spell_prefix + "spellhldie"));
return_set.push("SP-HLDIETYPE|" + getAttrByName(charid, spell_prefix + "spellhldietype"));
return_set.push("SP-OUTPUT|" + getAttrByName(charid, spell_prefix + "spelloutput"));
var desc = getAttrByName(charid, spell_prefix + "spelldescription").replace(/\
/g, "").replace(/\<\/p\>/g, "").replace(/\n/g, "
")
return_set.push("SP-DESCRIPTION|" + desc);
return_set.push("SP-MORECANTRIPDAMAGE|" + (desc.includes("damage increases by") ? 1 : 0));
return_set.push("SP-ATHIGHER|" + getAttrByName(charid, spell_prefix + "spellathigherlevels"));
var hlDice = getAttrByName(charid, spell_prefix + "spellhldie");
var hlDieType = getAttrByName(charid, spell_prefix + "spellhldietype");
var hlBonus = getAttrByName(charid, spell_prefix + "spellhlbonus");
if (hlDice !== "") {
return_set.push("SP-HLDICE|" + hlDice);
return_set.push("SP-HLDIETYPE|" + hlDieType);
return_set.push("SP-HLBONUS|+" + hlBonus);
} else {
return_set.push("SP-HLDICE|0");
return_set.push("SP-HLDIETYPE|");
return_set.push("SP-HLBONUS|");
}
return_set.push("SP-ADDABILITY|" + (getAttrByName(charid, spell_prefix + "spelldmgmod") == "Yes" ? 1 : 0));
return_set.push("SP-CONCENTRATION|" + (getAttrByName(charid, spell_prefix + "spellconcentration") == "{{concentration=1}}" ? "Concentration" : " "));
return_set.push("SP-CASTINGTIME|" + getAttrByName(charid, spell_prefix + "spellcastingtime"));
return_set.push("SP-SAVE|" + getAttrByName(charid, spell_prefix + "spellsave"));
return_set.push("SP-SAVESUCCESS|" + getAttrByName(charid, spell_prefix + "spellsavesuccess"));
return_set.push("SP-HEALING|" + getAttrByName(charid, spell_prefix + "spellhealing"));
return (return_set);
}
function getNPCAction(charid, actionname) {
var return_set = [];
try {
var action_prefix = filterObjs(function (z) {
return (z.get("characterid") == charid && z.get("name").startsWith("repeating_npcaction") && z.get("name").endsWith("name"));
})
.filter(entry => entry.get("current") == actionname)[0]
.get("name").slice(0, -4);
} catch (e) {
return (return_set)
};
var actionType = (getAttrByName(charid, action_prefix + "attack_flag") == "on" ? "ATTACK" : "INFOCARD");
return_set.push("NPCA-NAME|" + actionname);
return_set.push("NPCA-TYPE|" + actionType);
if (actionType == "ATTACK") {
return_set.push("NPCA-ATYPE|" + getAttrByName(charid, action_prefix + "attack_type"));
return_set.push("NPCA-RANGE|" + getAttrByName(charid, action_prefix + "attack_range"));
return_set.push("NPCA-TOHIT|" + getAttrByName(charid, action_prefix + "attack_tohit"));
return_set.push("NPCA-TARGET|" + getAttrByName(charid, action_prefix + "attack_target"));
var baseDamage = getAttrByName(charid, action_prefix + "attack_damage");
return_set.push("NPCA-DAMAGE|" + baseDamage);
var crit = (parseInt(baseDamage.split("d")[0]) * 2).toString() + "d" + baseDamage.split("d")[1];
return_set.push("NPCA-CRIT|" + crit);
return_set.push("NPCA-DAMAGETYPE|" + getAttrByName(charid, action_prefix + "attack_damagetype"));
baseDamage = getAttrByName(charid, action_prefix + "attack_damage2");
return_set.push("NPCA-DAMAGE2|" + baseDamage);
if (baseDamage !== "") {
crit = (parseInt(baseDamage.split("d")[0]) * 2).toString() + "d" + baseDamage.split("d")[1];
return_set.push("NPCA-CRIT2|" + crit);
} else {
return_set.push("NPCA-CRIT2|");
}
return_set.push("NPCA-DAMAGETYPE2|" + getAttrByName(charid, action_prefix + "attack_damagetype2"));
}
return_set.push("NPCA-DESCRIPTION|" + getAttrByName(charid, action_prefix + "description").replace(/(?:\r\n|\r|\n)/g, "
"));
return (return_set);
}
function getNPCLegendaryAction(charid, actionname) {
var return_set = [];
try {
var action_prefix = filterObjs(function (z) {
return (z.get("characterid") == charid && z.get("name").startsWith("repeating_npcaction-l") && z.get("name").endsWith("name"));
})
.filter(entry => entry.get("current") == actionname)[0]
.get("name").slice(0, -4);
} catch (e) {
return (return_set)
};
var actionType = (getAttrByName(charid, action_prefix + "attack_flag") == "on" ? "ATTACK" : "INFOCARD");
return_set.push("NPCA-NAME|" + actionname);
return_set.push("NPCA-TYPE|" + actionType);
if (actionType == "ATTACK") {
return_set.push("NPCA-ATYPE|" + getAttrByName(charid, action_prefix + "attack_type"));
return_set.push("NPCA-RANGE|" + getAttrByName(charid, action_prefix + "attack_range"));
return_set.push("NPCA-TOHIT|" + getAttrByName(charid, action_prefix + "attack_tohit"));
return_set.push("NPCA-TARGET|" + getAttrByName(charid, action_prefix + "attack_target"));
var baseDamage = getAttrByName(charid, action_prefix + "attack_damage");
return_set.push("NPCA-DAMAGE|" + baseDamage);
var crit = (parseInt(baseDamage.split("d")[0]) * 2).toString() + "d" + baseDamage.split("d")[1];
return_set.push("NPCA-CRIT|" + crit);
return_set.push("NPCA-DAMAGETYPE|" + getAttrByName(charid, action_prefix + "attack_damagetype"));
baseDamage = getAttrByName(charid, action_prefix + "attack_damage2");
return_set.push("NPCA-DAMAGE2|" + baseDamage);
if (baseDamage !== "") {
crit = (parseInt(baseDamage.split("d")[0]) * 2).toString() + "d" + baseDamage.split("d")[1];
return_set.push("NPCA-CRIT2|" + crit);
} else {
return_set.push("NPCA-CRIT2|");
}
return_set.push("NPCA-DAMAGETYPE2|" + getAttrByName(charid, action_prefix + "attack_damagetype2"));
}
return_set.push("NPCA-DESCRIPTION|" + getAttrByName(charid, action_prefix + "description").replace(/(?:\r\n|\r|\n)/g, "
"));
return (return_set);
}
function getPCAttack(charid, attackname) {
var return_set = [];
try {
var action_prefix = filterObjs(function (z) {
return (z.get("characterid") == charid && z.get("name").startsWith("repeating_attack") && z.get("name").endsWith("atkname"));
})
.filter(entry => entry.get("current") == attackname)[0]
.get("name").slice(0, -7);
} catch (e) {
return (return_set)
};
return_set.push("PCA-ATYPE|Attack");
return_set.push("PCA-NAME|" + attackname);
return_set.push("PCA-DMGBASE|" + getAttrByName(charid, action_prefix + "dmgbase"));
return_set.push("PCA-DMGTYPE|" + getAttrByName(charid, action_prefix + "dmgtype"));
var atkattr = getAttrByName(charid, action_prefix + "atkattr_base");
var attrstem = atkattr.substring(2, 5).toUpperCase();
return_set.push("PCA-ATKATTR|" + attrstem);
return_set.push("PCA-RANGE|" + getAttrByName(charid, action_prefix + "atkrange"));
return_set.push("PCA-ATKBONUS|" + getAttrByName(charid, action_prefix + "atkbonus").replace(/\+/g, ""));
var damagetype = getAttrByName(charid, action_prefix + "atkdmgtype");
if (damagetype !== undefined) {
var magicBonus = ""
if (damagetype.indexOf("Magic Bonus") !== -1) {
magicBonus = "+" + damagetype.substr(damagetype.indexOf("Magic Bonus") - 2, 1) + " [Magic]";
damagetype = damagetype.replace(/\+\ [0-9]\ Magic Bonus/g, "");
}
var dmg1 = "none";
var dmg2 = "none";
if (damagetype.indexOf(" + ") !== -1) {
dmg1 = damagetype.trim().split(" + ")[0];
dmg2 = damagetype.trim().split(" + ")[1];
} else {
dmg1 = damagetype.trim();
}
if (dmg1 !== "none") {
var d1dice = dmg1.split(" ")[0];
var d1type = dmg1.split(" ")[1];
return_set.push("PCA-DMG1DICE|" + d1dice + magicBonus);
var d1crit = parseInt(d1dice.split("d")[0]) * 2;
return_set.push("PCA-DMG1CRIT|" + d1crit.toString() + "d" + d1dice.split("d")[1] + magicBonus);
return_set.push("PCA-DMG1TYPE|" + d1type);
} else {
return_set.push("PCA-DMG1DICE|none");
}
if (dmg2 !== "none") {
var d2dice = dmg2.split(" ")[0];
var d2type = dmg2.split(" ")[1];
return_set.push("PCA-DMG2DICE|" + d2dice + magicBonus);
var d2crit = parseInt(d2dice.split("d")[0]) * 2;
return_set.push("PCA-DMG2CRIT|" + d2crit.toString() + "d" + d2dice.split("d")[1]);
return_set.push("PCA-DMG2TYPE|" + d2type);
} else {
return_set.push("PCA-DMG2DICE|none");
}
}
var saveflag = getAttrByName(charid, action_prefix + "saveflag");
if (saveflag !== "0") {
var saveattr = getAttrByName(charid, action_prefix + "saveattr");
var saveeffect = getAttrByName(charid, action_prefix + "saveeffect");
var savedc = getAttrByName(charid, action_prefix + "savedc");
savedc = savedc.replace(/\(/g, "").replace(/\)/g, "");
var dcparts = savedc.split("+");
var dctotal = 0;
for (var x = 0; x < dcparts.length; x++) {
if (dcparts[x].indexOf("@") !== -1) {
var partAttr = dcparts[x].split("{")[1].slice(0, -1);
var thisPart = getAttrByName(charid, partAttr);
dcparts[x] = parseInt(thisPart, 10);
} else {
dcparts[x] = parseInt(dcparts[x], 10);
}
dctotal += dcparts[x];
}
return_set.push("PCA-SAVE|true");
return_set.push("PCA-SAVEATTR|" + saveattr);
return_set.push("PCA-SAVEATTRSHORT|" + saveattr.substring(0, 3).toUpperCase());
return_set.push("PCA-SAVEEFFECT|" + saveeffect);
return_set.push("PCA-SAVEDC|" + dctotal);
} else {
return_set.push("PCA-SAVE|none");
}
return_set.push("PCA-PROF|" + + getAttrByName(charid, action_prefix + "atkprofflag"));
var desc = getAttrByName(charid, action_prefix + "atk_desc");
if (desc !== undefined && desc !== "") {
return_set.push("PCA-DESC|" + getAttrByName(charid, action_prefix + "atk_desc").replace(/(?:\r\n|\r|\n)/g, "
"));
} else {
return_set.push("PCA-DESC|none");
}
return (return_set);
}
function getSectionAttrs(charid, entryname, sectionname, searchtext, replacementprefix) {
var return_set = [];
try {
var action_prefix = filterObjs(function (z) {
return (z.get("characterid") == charid && z.get("name").startsWith("repeating_" + sectionname) && z.get("name").endsWith(searchtext));
})
.filter(entry => entry.get("current") == entryname)[0]
.get("name").slice(0, -searchtext.length);
} catch (e) {
return (return_set)
};
var action_attrs = filterObjs(function (z) {
return (z.get("characterid") == charid && z.get("name").startsWith(action_prefix));
})
action_attrs.forEach(function (z) {
return_set.push(replacementprefix + z.get("name").replace(action_prefix, "") + "|" + z.get("current"));
return_set.push(replacementprefix + z.get("name").replace(action_prefix, "") + "_max|" + z.get("max"));
})
//log(return_set);
return (return_set);
}
function getSpellMacro(charid, spellname, spellattrname) {
var spellAttr = getSheetAttr(charid, "spell_" + spellname.replace(/ /g, "_") + "_macro");
if (spellAttr !== "") {
if (spellAttr.substring(0, 1) !== '#') {
return ("(~" + charid + "|" + spellAttr + ")");
} else {
return ("(!
" + spellAttr + ")");
}
} else {
return ("(~" + charid + "|" + spellattrname.replace("name", "") + ")");
}
}
function sanitize(str) {
var res;
if (str) {
res = str.replace(/\=/g, "").replace(/\;/g, "").replace(/\/g, "").replace(/\{/g, "").replace(/\}/g, "").replace(/\[/g, "").replace(/\]/g, "").replace(/\(/g, "").replace(/\)/g, "").replace(/\./g, "").replace(/\&/g, "").replace(/|>/g, "");
res = res.replace(/\-eq/g, "==").replace(/\-ne/g, "!==").replace(/\-gt/g, ">").replace(/\-lt/g, "<").replace(/\-ge/g, ">=").replace(/\-le/g, "<=").replace(/\-and/g, "&&").replace(/\-or/g, "||");
}
return res;
}
RegExp.quote = function (str) {
return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
};
const getRepeatingSectionAttrs = function (charid, prefix) {
// Input
// charid: character id
// prefix: repeating section name, e.g. 'repeating_weapons'
// Output
// repRowIds: array containing all repeating section IDs for the given prefix, ordered in the same way that the rows appear on the sheet
// repeatingAttrs: object containing all repeating attributes that exist for this section, indexed by their name
const repeatingAttrs = {},
regExp = new RegExp(`^${prefix}_(-[-A-Za-z0-9]+?|\\d+)_`);
let repOrder;
// Get attributes
findObjs({
_type : 'attribute',
_characterid : charid
}).forEach(o => {
const attrName = o.get('name');
if (attrName.search(regExp) === 0)
repeatingAttrs[attrName] = o;
else if (attrName === `_reporder_${prefix}`)
repOrder = o.get('current').split(',');
});
if (!repOrder)
repOrder = [];
// Get list of repeating row ids by prefix from repeatingAttrs
const unorderedIds = [...new Set(Object.keys(repeatingAttrs)
.map(n => n.match(regExp))
.filter(x => !!x)
.map(a => a[1]))];
const repRowIds = [...new Set(repOrder.filter(x => unorderedIds.includes(x)).concat(unorderedIds))];
return [repRowIds, repeatingAttrs];
}
return {
Process : PowerCard.Process
};
}
());