Created
January 31, 2017 03:20
-
-
Save SCPRedMage/9ac2bf758226b2002412da44bebe3ae9 to your computer and use it in GitHub Desktop.
HL-import module for Roll20 Pathfinder character sheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var HLImport = HLImport || (function() { | |
'use strict'; | |
var parseNum = function(num) | |
{ | |
if (_.isUndefined(num) || num === "") | |
return 0; | |
return (parseInt(num) || 0); | |
}, | |
buildList = function(objArray, propName) { return _.map(objArray, function (item) { return item[propName]; }).join(", "); }, | |
getSizeMod = function(size) | |
{ | |
switch(size.toLowerCase()) | |
{ | |
case "colossal": | |
return -8; | |
break; | |
case "gargantuan": | |
return -4; | |
break | |
case "huge": | |
return -2; | |
break; | |
case "large": | |
return -1; | |
break; | |
case "small": | |
return 1; | |
break; | |
case "tiny": | |
return 2; | |
break; | |
case "diminutive": | |
return 4; | |
break; | |
case "fine": | |
return 8; | |
break; | |
default: | |
return 0; | |
} | |
}, | |
// Make sure "stuff" is an array | |
arrayify = function(stuff) | |
{ | |
if (_.isUndefined(stuff)) | |
return []; | |
if (Array.isArray(stuff)) | |
return stuff; | |
return new Array(stuff); | |
}, | |
importInit = function(attrs,initObj) | |
{ | |
attrs["init-misc"] = parseNum(initObj._total)-parseNum(initObj._attrtext); | |
attrs["init-ability"] = "@{"+initObj._attrname.substr(0,3).toUpperCase()+"-mod}"; | |
attrs["init_notes"] = initObj.situationalmodifiers._text; | |
}, | |
importAbilityScores = function(attrs,attributes) | |
{ | |
attributes.forEach(function(abScore) { | |
var abName = abScore._name.substr(0,3).toUpperCase(); | |
var base = parseNum(abScore.attrvalue._base); | |
var modifier = parseNum(abScore.attrvalue._modified) - base; // Modifier is the total difference between what HL is reporting as the character's base ability score and the final modified ability score | |
attrs[abName+"-base"] = base; | |
// If the modifier is positive, assume it's an enhancement bonus; otherwise, assume it's a penalty | |
if (modifier > 0) | |
attrs[abName+"-enhance"] = modifier; | |
else | |
attrs[abName+"-penalty"] = modifier; | |
}); | |
}, | |
importSaves = function(attrs,saves) | |
{ | |
// Since the XML doesn't break this down by class, add it all to class 0 | |
var i = 0; | |
var saveNotes = saves.allsaves.situationalmodifiers._text; | |
for (i = 0; i < saves.save.length; i++) | |
{ | |
var save = saves.save[i]; | |
var abbr = save._abbr; | |
attrs["class-0-"+abbr] = parseNum(save._base); | |
attrs[abbr+"-resist"] = parseNum(save._fromresist); | |
attrs[abbr+"-misc"] = parseNum(save._save)-parseNum(save._base)-parseNum(save._fromresist)-parseNum(save._fromattr); | |
if (save.situationalmodifiers._text !== "" && saveNotes.indexOf(save.situationalmodifiers._text) === -1) | |
saveNotes = saveNotes + "\n**"+abbr+":** " + save.situationalmodifiers._text; | |
} | |
attrs["Save-notes"] = saveNotes.trim(); | |
}, | |
// Find an existing repeatable item with the same name, or generate new row ID | |
getOrMakeRowID = function(featIDList,name) | |
{ | |
var attrNames = Object.values(featIDList); | |
var rows = Object.keys(featIDList); | |
var attrMatch = _.find(attrNames, function(currentAttrName) | |
{ | |
var attrName = currentAttrName; | |
// Eliminate anything in parentheses, dice expressions, and "x#" (we use that to indicate we've taken a feat more than once) before comparing names | |
attrName = attrName.replace(/ x[0-9]+$/,"").trim(); | |
if (attrName === name) | |
{ | |
var ID = rows[_.indexOf(attrNames,currentAttrName)]; | |
if (!_.isUndefined(ID)) | |
return true; | |
} | |
return false; | |
}); | |
if (!_.isUndefined(attrMatch)) | |
return rows[_.indexOf(attrNames,attrMatch)]; | |
return generateRowID(); | |
}, | |
// Find an existing repeatable item with the same name, or generate new row ID; extra processing for items | |
getOrMakeItemRowID = function(featIDList,name) | |
{ | |
var attrNames = Object.values(featIDList); | |
var rows = Object.keys(featIDList); | |
var compareName = name.replace(/\(.*\)/,"").replace(/\+\d+/,"").toLowerCase().replace("masterwork","").trim(); | |
var attrMatch = _.find(attrNames, function(currentAttrName) | |
{ | |
var attrName = currentAttrName; | |
// Eliminate anything in parentheses, dice expressions, and "x#" (we use that to indicate we've taken a feat more than once) before comparing names | |
attrName = attrName.replace(/\(.*\)/,"").replace(/\+\d+/,"").toLowerCase().replace("masterwork","").trim(); | |
if (attrName === compareName) | |
{ | |
var ID = rows[_.indexOf(attrNames,currentAttrName)]; | |
if (!_.isUndefined(ID)) | |
return true; | |
} | |
return false; | |
}); | |
if (!_.isUndefined(attrMatch)) | |
return rows[_.indexOf(attrNames,attrMatch)]; | |
return generateRowID(); | |
}, | |
// Find an existing repeatable item with the same name and spellclass, or generate new row ID | |
getOrMakeSpellRowID = function(featIDList,name,spellclass) | |
{ | |
var attrMatch = _.find(featIDList, function(currentFeat) | |
{ | |
if (currentFeat.name === name && currentFeat.spellclass === spellclass) | |
return true; | |
return false; | |
}); | |
if (!_.isUndefined(attrMatch)) | |
return featIDList[_.indexOf(featIDList,attrMatch)]; | |
return generateRowID(); | |
}, | |
getOrMakeClassRowID = function(featIDList,name) | |
{ | |
var attrNames = Object.values(featIDList); | |
var rows = Object.keys(featIDList); | |
var attrMatch = _.find(attrNames, function(currentAttrName) | |
{ | |
var attrName = currentAttrName; | |
// Eliminate anything in parentheses, dice expressions, and "x#" (we use that to indicate we've taken a feat more than once) before comparing names | |
name = name.replace(/ x[0-9]+$/,"").replace(/\B\+\d*/g,"").replace(/\(([^\)]+)\)/g,"").replace(/\b\d+d\d+\+*-*\d*\b/g,"").trim(); | |
attrName = attrName.replace(/ x[0-9]+$/,"").replace(/\B\+\d*/g,"").replace(/\(([^\)]+)\)/g,"").replace(/\b\d+d\d+\+*-*\d*\b/g,"").trim(); | |
if (attrName === name) | |
{ | |
var ID = rows[_.indexOf(attrNames,currentAttrName)]; | |
if (!_.isUndefined(ID)) | |
return true; | |
} | |
return false; | |
}); | |
if (!_.isUndefined(attrMatch)) | |
return rows[_.indexOf(attrNames,attrMatch)]; | |
return generateRowID(); | |
}, | |
importFeats = function(attrs,feats,featIDList,resources) | |
{ | |
var repeatPrefix = "repeating_npc-spell-like-abilities"; | |
var skipList = []; | |
var featNames = _.map(feats, function(feat) { return feat._name; } ); | |
_.each(feats, function(feat) | |
{ | |
// Early exit if we already dealt with another copy of this feat | |
if (_.contains(skipList,feat._name)) | |
return; | |
// Count the number of times the feat is listed, so we can indicate that in the feat name | |
var taken = _.filter(featNames,function(featName) { return featName === feat._name; } ).length; | |
var row = getOrMakeRowID(featIDList,feat._name); | |
if (!_.isUndefined(featIDList[row])) | |
delete featIDList[row]; | |
if (taken > 1) | |
attrs[repeatPrefix+"_"+row+"_name"] = feat._name + " x" + taken; | |
else | |
attrs[repeatPrefix+"_"+row+"_name"] = feat._name; | |
attrs[repeatPrefix+"_"+row+"_description"] = feat.description; | |
attrs[repeatPrefix+"_"+row+"_rule_category"] = "feats"; | |
skipList.push(feat._name); | |
if (_.contains(Object.keys(resources),feat._name)) | |
attrs[repeatPrefix+"_"+row+"_max-calculation"] = resources[feat._name]._max; | |
}); | |
}, | |
// Hero Lab stores armor and shields identically, so so assume anything with "shield" or "klar" in the name is a shield | |
nameIsShield = function(name) | |
{ | |
if (name.toLowerCase().indexOf("shield") !== -1 || name.toLowerCase().indexOf("klar") !== -1) | |
return true; | |
return false; | |
}, | |
importItems = function(items,resources,armorPenalties,armor,weapons) | |
{ | |
var repeatPrefix = "repeating_item"; | |
getSectionIDs(repeatPrefix, function(idarray) { | |
var itemNameAttrs = _.union(_.map(idarray,function(id) { return repeatPrefix+"_"+id+"_name"; }),["shield3-acp","shield3-spell-fail"]); | |
getAttrs(itemNameAttrs, function(names) { | |
// Pull out the shield attributes before we build the ID list | |
var shieldACP = parseNum(names["shield3-acp"]); | |
var shieldASF = parseNum(names["shield3-spell-fail"]); | |
if (!_.isUndefined(names["shield3-acp"])) | |
delete names["shield3-acp"]; | |
if (!_.isUndefined(names["shield3-spell-fail"])) | |
delete names["shield3-spell-fail"]; | |
var itemIDList = _.object(_.map(names,function(name,attr) { | |
return [attr.substring(repeatPrefix.length+1,(attr.indexOf("_name"))),name]; | |
})); | |
var itemsList = []; | |
var attrs = {}; | |
var armorNames = _.map(armor, function(obj) { return obj._name; }); | |
var weaponNames = _.map(weapons, function(obj) { return obj._name; }); | |
// List of words that indicate an item is masterwork | |
var masterworkWords = ["mithral","adamantine","angelskin","darkleaf","darkwood","dragonhide","eel","fire-forged","frost-forged","greenwood","paueliel"] | |
_.each(items,function(item) | |
{ | |
var row = getOrMakeItemRowID(itemIDList,item._name); | |
if (!_.isUndefined(itemIDList[row])) | |
delete itemIDList[row]; | |
itemsList.push(item._name); | |
repeatPrefix = "repeating_item_" + row; | |
attrs[repeatPrefix+"_name"] = item._name; | |
attrs[repeatPrefix+"_weight"] = item.weight._value; | |
attrs[repeatPrefix+"_value"] = (parseFloat(item.cost._value) / parseInt(item._quantity) ); | |
attrs[repeatPrefix+"_description"] = item.description; | |
if (_.contains(Object.keys(resources),item._name) && item._quantity === "1" && resources[item._name]._max !== "1") | |
{ | |
attrs[repeatPrefix+"_qty"] = resources[item._name]._left; | |
attrs[repeatPrefix+"_qty_max"] = resources[item._name]._max; | |
} | |
else | |
attrs[repeatPrefix+"_qty"] = item._quantity; | |
if (!_.isUndefined(item.itempower)) | |
_.each(arrayify(item.itempower), function(itemPower) { itemsList.push(itemPower._name); }); | |
// check if this is a weapon | |
var weaponCompareName = item._name; | |
// If this is a shield (but not a klar), the attack name will be "Heavy/light shield bash" | |
if (item._name.toLowerCase().indexOf("shield") !== -1) | |
{ | |
var attackName; | |
if (item._name.toLowerCase().indexOf("heavy" !== -1)) | |
attackName = "heavy shield bash"; | |
else | |
attackName = "light shield bash"; | |
weaponCompareName = (_.find(weaponNames,function(name) { if (name.toLowerCase().indexOf(attackName) !== -1) return true; return false;}) || item._name); | |
} | |
if (_.contains(weaponNames, weaponCompareName)) | |
{ | |
var weaponObj = weapons[_.indexOf(weaponNames,weaponCompareName)]; | |
attrs[repeatPrefix+"_item-enhance"] = parseNum(weaponObj._name.match(/\+\d+/)); | |
if (!_.isUndefined(weaponObj._typetext)) | |
attrs[repeatPrefix+"_item-dmg-type"] = weaponObj._typetext; | |
// Check to see if item name includes any words that indicate this is a masterwork item | |
if ((weaponCompareName.toLowerCase().indexOf("masterwork") !== -1) || _.intersection(masterworkWords,item._name.toLowerCase().split(" ")).length > 0) | |
attrs[repeatPrefix+"_item-masterwork"] = 1; | |
if (!_.isUndefined(weaponObj._damage)) | |
{ | |
var weaponDice = weaponObj._damage.match(/\d+d\d+/); | |
if (weaponDice.length > 0) | |
{ | |
attrs[repeatPrefix+"_item-damage-dice-num"] = parseNum(weaponDice[0].split("d")[0]); | |
attrs[repeatPrefix+"_item-damage-die"] = parseNum(weaponDice[0].split("d")[1]); | |
} | |
} | |
if (!_.isUndefined(weaponObj._crit)) | |
{ | |
var critArray = weaponObj._crit.split("/"); | |
if (critArray.length > 1) | |
attrs[repeatPrefix+"_item-crit-target"] = parseNum(critArray[0].match(/\d+/)[0]); | |
else | |
attrs[repeatPrefix+"_item-crit-target"] = 20; | |
attrs[repeatPrefix+"_item-crit-multiplier"] = parseNum(critArray[critArray.length-1].replace(/\D/g,"")); | |
} | |
if (!_.isUndefined(weaponObj.rangedattack) && !_.isUndefined(weaponObj.rangedattack._rangeincvalue)) | |
attrs[repeatPrefix+"_item-range"] = parseNum(weaponObj.rangedattack._rangeincvalue); | |
} | |
// check if this is armor | |
// If this is a klar, the armor name will be different | |
var armorCompareName = item._name; | |
if (item._name.toLowerCase().indexOf("klar") !== -1) | |
{ | |
armorCompareName = (_.find(armorNames,function(name) { if (name.toLowerCase().indexOf("klar") !== -1) return true; return false;}) || item._name); | |
} | |
if (_.contains(armorNames, armorCompareName)) | |
{ | |
var armorObj = armor[_.indexOf(armorNames,armorCompareName)]; | |
// Item is a shield | |
if (nameIsShield(item._name)) | |
{ | |
var enhancement = parseNum(armorCompareName.match(/\+\d+/)); | |
var ACbonus = parseNum(armorObj._ac) - enhancement; | |
attrs[repeatPrefix+"_item-acbonus"] = ACbonus; | |
attrs[repeatPrefix+"_item-enhance"] = enhancement; | |
if (!_.isUndefined(armorObj._equipped) && armorObj._equipped === "yes") | |
{ | |
attrs[repeatPrefix+"_item-acp"] = shieldACP; | |
attrs[repeatPrefix+"_item-spell-fail"] = shieldASF; | |
attrs["shield3"] = item._name; | |
attrs["shield3-acbonus"] = ACbonus; | |
attrs["shield3-enhance"] = enhancement; | |
} | |
} | |
else | |
{ | |
var enhancement = parseNum(item._name.match(/\+\d+/)); | |
var ACbonus = parseNum(armorObj._ac) - enhancement; | |
attrs[repeatPrefix+"_item-acbonus"] = ACbonus; | |
attrs[repeatPrefix+"_item-enhance"] = enhancement; | |
if (!_.isUndefined(armorObj._equipped) && armorObj._equipped === "yes") | |
{ | |
attrs["armor3-acp"] = attrs[repeatPrefix+"_item-acp"] = armorPenalties.ACP - shieldACP; | |
attrs["armor3-spell-fail"] = attrs[repeatPrefix+"_item-spell-fail"] = armorPenalties.spellfail - shieldASF; | |
if (armorPenalties.maxDex == 99) | |
attrs["armor3-max-dex"] = attrs[repeatPrefix+"_item-max-dex"] = ""; | |
else | |
attrs["armor3-max-dex"] = attrs[repeatPrefix+"_item-max-dex"] = armorPenalties.maxDex; | |
attrs["armor3"] = item._name; | |
attrs["armor3-acbonus"] = ACbonus; | |
attrs["armor3-enhance"] = enhancement; | |
} | |
} | |
} | |
}); | |
setAttrs(attrs); | |
}); | |
}); | |
}, | |
importTraits = function(attrs,traits,traitIDList,resources) | |
{ | |
var repeatPrefix = "repeating_npc-spell-like-abilities"; | |
traits.forEach(function(trait) | |
{ | |
var row = getOrMakeRowID(traitIDList,trait._name); | |
if (!_.isUndefined(traitIDList[row])) | |
delete traitIDList[row]; | |
attrs[repeatPrefix+"_"+row+"_name"] = trait._name; | |
attrs[repeatPrefix+"_"+row+"_description"] = trait.description; | |
attrs[repeatPrefix+"_"+row+"_rule_category"] = "traits"; | |
if (_.contains(Object.keys(resources),trait._name)) | |
attrs[repeatPrefix+"_"+row+"_max-calculation"] = resources[trait._name]._max; | |
}); | |
}, | |
importSLAs = function(attrs,SLAs,SLAsIDList,resources) | |
{ | |
var repeatPrefix = "repeating_npc-spell-like-abilities"; | |
SLAs.forEach(function(SLA) | |
{ | |
var row = getOrMakeRowID(SLAsIDList,SLA._name); | |
if (!_.isUndefined(SLAsIDList[row])) | |
delete SLAsIDList[row]; | |
attrs[repeatPrefix+"_"+row+"_name"] = SLA._name; | |
attrs[repeatPrefix+"_"+row+"_description"] = SLA.description; | |
attrs[repeatPrefix+"_"+row+"_rule_category"] = "spell-like-abilities"; | |
attrs[repeatPrefix+"_"+row+"_ability_type"] = "Sp"; | |
if (_.contains(Object.keys(resources),SLA._name)) | |
attrs[repeatPrefix+"_"+row+"_max-calculation"] = resources[SLA._name]._max; | |
}); | |
}, | |
importFeatures = function(attrs,featureList,specials,archetypes,resources) | |
{ | |
var specNameList = _.map(specials,function(special) { return special._name;}); | |
var skipList = []; | |
_.each(specials, function(special) | |
{ | |
var name = special._name; | |
var repeatPrefix = "repeating_npc-spell-like-abilities",row,classSource = -1; | |
var cleanName = name.replace(/ x[0-9]+$/,"").replace(/\(([^\)]+)\)/g,"").trim(); | |
if (_.contains(skipList,cleanName)) | |
return; | |
var multiList = _.filter(specNameList, function(spec) { return (spec.replace(/\(([^\)]+)\)/g,"").trim() === cleanName); }); | |
if (multiList.length > 1) | |
{ | |
skipList.push(cleanName); | |
var parenList = _.map(multiList, function(item) { return item.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); }); | |
name = name.replace(/\(([^\)]+)\)/,"("+parenList.join(", ")+")"); | |
} | |
row = getOrMakeClassRowID(featureList,name); | |
repeatPrefix = "repeating_npc-spell-like-abilities_" + row; | |
if (!_.isUndefined(featureList[row])) | |
delete featureList[row]; | |
else // If we created a new row for this, set rule category | |
{ | |
// Import if it has a "specsource", assume it's a class feature | |
if (special.specsource) | |
attrs[repeatPrefix+"_rule_category"] = "class-features"; | |
else | |
attrs[repeatPrefix+"_rule_category"] = "racial-traits"; | |
} | |
classSource = getClassSource(arrayify(special.specsource),archetypes); | |
attrs[repeatPrefix+"_name"] = name; | |
attrs[repeatPrefix+"_description"] = special.description; | |
if (classSource !== -1) | |
{ | |
attrs[repeatPrefix+"_CL-basis"] = "@{class-"+classSource+"-level}"; | |
attrs[repeatPrefix+"_class-name"] = special.specsource; | |
} | |
if (_.contains(Object.keys(resources),special._name)) | |
attrs[repeatPrefix+"_max-calculation"] = resources[special._name]._max; | |
}); | |
}, | |
importClasses = function(attrs, classes) | |
{ | |
var classList = new Object(); | |
var i = 0; | |
var classObj; | |
while (i < classes.length) | |
{ | |
classObj = classes[i]; | |
// We can only handle 5 classes | |
if (i >= 5) | |
return; | |
classList[classObj._name.replace(/\(([^\)]+)\)/g,"").replace("(","").replace(")","").trim()] = classObj; | |
attrs["class-"+i+"-name"] = classObj._name; | |
attrs["class-"+i+"-level"] = classObj._level; | |
i++; | |
} | |
return classList; | |
}, | |
// Import spellclasses; presence in spellclasses node means it's a spellcaster, but some of the data is in the classes node | |
importSpellClasses = function(attrs, spellclasses,classes,abScores) | |
{ | |
var spellClassesList = new Object(); | |
var i, j, abMod = 0, currentAbMod, spellslots, spelllevel, casterlevel, concmod, spellpenmod; | |
var spellClassIndex = 0; | |
for (i = 0; i < spellclasses.length; i++) | |
{ | |
var spellClass = spellclasses[i]; | |
// Only 3 spellclasses on character sheet, so if they somehow have more... | |
if (spellClassIndex >= 3) | |
return spellClassesList; | |
var spellClassName = spellClass._name.replace(/\(([^\)]+)\)/g,"").replace("(","").replace(")","").trim(); | |
var classIndex = _.indexOf(Object.keys(classes),_.find(Object.keys(classes),function(className) | |
{ | |
if (className.toLowerCase().indexOf(spellClassName.toLowerCase()) !== -1) | |
return true; | |
return false; | |
})); | |
if (classIndex !== -1) | |
{ | |
casterlevel = parseNum(classes[spellClassName]._casterlevel); | |
attrs["spellclass-"+spellClassIndex] = classIndex; | |
attrs["spellclass-"+spellClassIndex+"-level-misc"] = casterlevel - parseNum(classes[spellClassName]._level); | |
if (!_.isUndefined(classes[spellClassName].arcanespellfailure)) | |
attrs["armor3-spell-fail"] = parseNum(classes[spellClassName].arcanespellfailure._value); | |
// Make a guess at which ability modifier is used for this class | |
if (!_.isUndefined(classes[spellClassName]._basespelldc)) | |
abMod = parseNum(classes[spellClassName]._basespelldc) - 10; | |
if (!_.isUndefined(classes[spellClassName]._basespelldc)) | |
{ | |
// Start at the fourth ability score (Intelligence), so we skip the physical abilities | |
for (j = 3; j < abScores.length; j++) | |
{ | |
if (parseNum(abScores[j].attrbonus._modified) === abMod) | |
{ | |
var attr = {} | |
attr["Concentration-"+spellClassIndex+"-ability"] = "@{"+abScores[j]._name.substr(0,3).toUpperCase()+"-mod}"; | |
setAttrs(attr); | |
break; | |
} | |
} | |
} | |
if (abMod !== 0) | |
{ | |
// Calculate misc mods to concentration | |
if (!_.isUndefined(classes[spellClassName]._concentrationcheck)) | |
{ | |
concmod = parseNum(classes[spellClassName]._concentrationcheck) - casterlevel - abMod; | |
attrs["Concentration-"+spellClassIndex+"-misc"] = concmod; | |
} | |
// Calculate misc mods to spell penetration | |
if (!_.isUndefined(classes[spellClassName].overcomespellresistance)) | |
{ | |
spellpenmod = parseNum(classes[spellClassName].overcomespellresistance) - casterlevel; | |
attrs["spellclass-"+spellClassIndex+"-SP_misc"] = spellpenmod; | |
} | |
// Populate spells / day; Hero Lab includes bonus slots, so remove those | |
if (!_.isUndefined(spellclasses[i].spelllevel)) | |
{ | |
spellclasses[i].spelllevel = arrayify(spellclasses[i].spelllevel); | |
for (j = 0; j < spellclasses[i].spelllevel.length; j++) | |
{ | |
spellslots = parseNum(spellclasses[i].spelllevel[j]._maxcasts); | |
spelllevel = parseNum(spellclasses[i].spelllevel[j]._level); | |
if (spelllevel > 0) | |
spellslots = spellslots - bonusSpellSlots(abMod,spelllevel); | |
attrs["spellclass-"+spellClassIndex+"-level-"+spelllevel+"-class"] = spellslots; | |
} | |
} | |
} | |
spellClassesList[spellClassName] = classes[Object.keys(classes)[classIndex]]; | |
spellClassIndex++; | |
} | |
} | |
return spellClassesList; | |
}, | |
importSpells = function(spells,spellclasses) | |
{ | |
console.log("Import spells"); | |
var repeatPrefix = "repeating_spells"; | |
getSectionIDs(repeatPrefix, function(idarray) { | |
var spellNameAttrs = _.union(_.map(idarray,function(id) { return repeatPrefix+"_"+id+"_name"; }),_.map(idarray,function(id) { return repeatPrefix+"_"+id+"_spellclass_number"; })); | |
getAttrs(spellNameAttrs, function(spellAttrs) { | |
var spellObjList = {}; | |
var spellKeys = Object.keys(spellAttrs); | |
_.each(spellKeys,function(spellKey) { | |
var rowID; | |
if (spellKey.indexOf("_name") !== -1) | |
{ | |
rowID = spellKey.substring(repeatPrefix.length+1,(spellKey.indexOf("_name"))); | |
if (_.isUndefined(spellObjList[rowID])) | |
spellObjList[rowID] = {}; | |
spellObjList[rowID].name = spellAttrs[spellKey]; | |
} | |
if (spellKey.indexOf("_spellclass_number") !== -1) | |
{ | |
rowID = spellKey.substring(repeatPrefix.length+1,(spellKey.indexOf("_spellclass_number"))); | |
if (_.isUndefined(spellObjList[rowID])) | |
spellObjList[rowID] = {}; | |
spellObjList[rowID].spellclass = spellAttrs[spellKey]; | |
} | |
}); | |
var spellClassesKeys = Object.keys(spellclasses); | |
var attrs = {}; | |
_.each(spells, function(spell) { | |
var rowID, spellClass, spellName, school, level; | |
// Search for a repeating spell with the same name and spellclass; if not found, make new row | |
level = parseNum(spell._level); | |
repeatPrefix = "repeating_spells_"; | |
spellClass = _.indexOf(spellClassesKeys,spell._class); | |
spellName = spell._name.replace(/\(x\d+\)/,"").trim(); | |
rowID = getOrMakeSpellRowID(spellObjList,spellName,spellClass); | |
// Update prefix with ID | |
repeatPrefix = repeatPrefix + rowID; | |
attrs[repeatPrefix+"_name"] = spellName; | |
attrs[repeatPrefix+"_spell_level"] = level; | |
attrs[repeatPrefix+"_spellclass_number"] = spellClass; | |
attrs[repeatPrefix+"_components"] = spell._componenttext.replace("Divine Focus", "DF").replace("Focus","F").replace("Material","M").replace("Verbal","V").replace("Somatic","S").replace(" or ","/"); | |
attrs[repeatPrefix+"_range"] = spell._range; | |
attrs[repeatPrefix+"_duration"] = spell._duration; | |
attrs[repeatPrefix+"_save"] = spell._save.replace(/DC \d+/,"").trim(); | |
attrs[repeatPrefix+"_cast-time"] = spell._casttime; | |
attrs[repeatPrefix+"_sr"] = spell._resist.replace("harmless","Harmless"); | |
attrs[repeatPrefix+"_DC_misc"] = parseNum(spell._dc) - parseNum(spellclasses[(spell._class !== "") ? spell._class:Object.keys(spellclasses)[0]]._basespelldc) - level; | |
if (spell._area !== "") | |
attrs[repeatPrefix+"_targets"] = spell._area; | |
else if (spell._effect !== "") | |
attrs[repeatPrefix+"_targets"] = spell._effect; | |
else | |
attrs[repeatPrefix+"_targets"] = spell._target; | |
school = spell._schooltext; | |
if (spell._subschooltext !== "") | |
school = school + " (" + spell._subschooltext + ")"; | |
if (spell._descriptortext !== "") | |
school = school + " [" + spell._descriptortext + "]"; | |
attrs[repeatPrefix+"_school"] = school; | |
attrs[repeatPrefix+"_description"] = spell.description; | |
}); | |
setAttrs(attrs); | |
}); | |
}); | |
/*var i, rowID, spellClass, spellName, school, level; | |
var spellClassesKeys = Object.keys(spellclasses); | |
for (i = 0; i < spells.length; i++) | |
{ | |
// Search for a repeating spell with the same level, name, and spellclass; if not found, make new row | |
level = parseNum(spells[i]._level); | |
repeatPrefix = "repeating_spells_"; | |
spellClass = _.indexOf(spellClassesKeys,spells[i]._class); | |
spellName = spells[i]._name.replace(/\(x\d+\)/,"").trim(); | |
rowID = getOrMakeSpellRowID(character,repeatPrefix,spellName,spellClass); | |
// Update prefix with ID | |
repeatPrefix = repeatPrefix + rowID; | |
setAttr(character,repeatPrefix+"_name",spellName); | |
setAttr(character,repeatPrefix+"_spell_level",level); | |
setAttr(character,repeatPrefix+"_spellclass_number",spellClass); | |
setAttr(character,repeatPrefix+"_components",spells[i]._componenttext.replace("Divine Focus", "DF").replace("Focus","F").replace("Material","M").replace("Verbal","V").replace("Somatic","S").replace(" or ","/")); | |
setAttr(character,repeatPrefix+"_range",spells[i]._range); | |
setAttr(character,repeatPrefix+"_duration",spells[i]._duration); | |
setAttr(character,repeatPrefix+"_save",spells[i]._save.replace(/DC \d+/,"").trim()); | |
setAttr(character,repeatPrefix+"_cast-time",spells[i]._casttime); | |
setAttr(character,repeatPrefix+"_sr",spells[i]._resist.replace("harmless","Harmless")); | |
setAttr(character,repeatPrefix+"_DC_misc",parseNum(spells[i]._dc) - parseNum(spellclasses[(spells[i]._class !== "") ? spells[i]._class:Object.keys(spellclasses)[0]]._basespelldc) - level); | |
if (spells[i]._area !== "") | |
setAttr(character,repeatPrefix+"_targets",spells[i]._area); | |
else if (spells[i]._effect !== "") | |
setAttr(character,repeatPrefix+"_targets",spells[i]._effect); | |
else | |
setAttr(character,repeatPrefix+"_targets",spells[i]._target); | |
school = spells[i]._schooltext; | |
if (spells[i]._subschooltext !== "") | |
school = school + " (" + spells[i]._subschooltext + ")"; | |
if (spells[i]._descriptortext !== "") | |
school = school + " [" + spells[i]._descriptortext + "]"; | |
setAttr(character,repeatPrefix+"_school",school); | |
setAttr(character,repeatPrefix+"_description",spells[i].description); | |
}*/ | |
}, | |
calcHitDice = function(hitdice) | |
{ | |
var dice = hitdice.match(/\d+d\d/g); | |
var numDice = 0; | |
var i = 0; | |
while (i < dice.length) | |
{ | |
numDice += parseInt(dice[i].split("d")[0]); | |
i++; | |
} | |
return numDice; | |
}, | |
buildArchetypeArray = function(classes) | |
{ | |
var archetypes = new Object(); | |
_.each(classes, function (classObj, className) { | |
if (classObj._name.indexOf("(") === -1) | |
{ | |
archetypes[className] = []; | |
return; | |
} | |
var archeString = classObj._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
var archeList = archeString.split(","); | |
archeList = _.map(archeList,function(arche) { return arche.trim(); }); | |
archetypes[className] = archeList; | |
}); | |
return archetypes; | |
}, | |
// Returns the array number of the class that grants a feature; returns -1 if we can't find the class | |
getClassSource = function(sources,archetypes) | |
{ | |
// If there's no listed source, it isn't from a class | |
if (!sources.length) | |
return -1; | |
// Grab an array of class names from the archetypes object | |
var classes = Object.keys(archetypes); | |
// Check if source is a class, first | |
var intersect = _.intersection(sources,classes); | |
if (intersect.length) | |
return classes.indexOf(intersect[0]); | |
// If not a class, check for an archetype as a source, and return the associated class | |
var className = _.find(classes, function(item) { return (_.intersection(archetypes[item],sources).length); }); | |
if (className) | |
return classes.indexOf(className); | |
return -1; | |
}, | |
bonusSpellSlots = function(abilMod,spellLevel) { return Math.max(0, Math.floor((abilMod + 4 - spellLevel) / 4)); }, | |
importSkills = function(attrs,skills,size,ACP) | |
{ | |
// Ripped from the PF character sheet JS | |
var skillSize; | |
switch (Math.abs(size)){ | |
case 0: skillSize=0;break; | |
case 1: skillSize=2;break; | |
case 2: skillSize=4;break; | |
case 4: skillSize=6;break; | |
case 8: skillSize=8;break; | |
case 16: skillSize=10;break; | |
default: skillSize=0; | |
} | |
if(size<0) {skillSize=skillSize*-1;} | |
// Clear out all existing skills data | |
_.extend(attrs, { "acrobatics-ability":"", "acrobatics-cs":"", "acrobatics-ranks":"", "acrobatics-class":"", "acrobatics-ability-mod":"", "acrobatics-racial":"", "acrobatics-feat":"", "acrobatics-item":"", "acrobatics-size":"", "acrobatics-acp":"", "acrobatics-misc":"", "acrobatics-reqtrain":"", "artistry-ability":"", "artistry-cs":"", "artistry-ranks":"", "artistry-class":"", "artistry-ability-mod":"", "artistry-racial":"", "artistry-feat":"", "artistry-item":"", "artistry-size":"", "artistry-acp":"", "artistry-misc":"", "artistry-reqtrain":"", "artistry2-ability":"", "artistry2-cs":"", "artistry2-ranks":"", "artistry2-class":"", "artistry2-ability-mod":"", "artistry2-racial":"", "artistry2-feat":"", "artistry2-item":"", "artistry2-size":"", "artistry2-acp":"", "artistry2-misc":"", "artistry2-reqtrain":"", "artistry3-ability":"", "artistry3-cs":"", "artistry3-ranks":"", "artistry3-class":"", "artistry3-ability-mod":"", "artistry3-racial":"", "artistry3-feat":"", "artistry3-item":"", "artistry3-size":"", "artistry3-acp":"", "artistry3-misc":"", "artistry3-reqtrain":"", "appraise-ability":"", "appraise-cs":"", "appraise-ranks":"", "appraise-class":"", "appraise-ability-mod":"", "appraise-racial":"", "appraise-feat":"", "appraise-item":"", "appraise-size":"", "appraise-acp":"", "appraise-misc":"", "appraise-reqtrain":"", "bluff-ability":"", "bluff-cs":"", "bluff-ranks":"", "bluff-class":"", "bluff-ability-mod":"", "bluff-racial":"", "bluff-feat":"", "bluff-item":"", "bluff-size":"", "bluff-acp":"", "bluff-misc":"", "bluff-reqtrain":"", "climb-ability":"", "climb-cs":"", "climb-ranks":"", "climb-class":"", "climb-ability-mod":"", "climb-racial":"", "climb-feat":"", "climb-item":"", "climb-size":"", "climb-acp":"", "climb-misc":"", "climb-reqtrain":"", "craft-ability":"", "craft-cs":"", "craft-ranks":"", "craft-class":"", "craft-ability-mod":"", "craft-racial":"", "craft-feat":"", "craft-item":"", "craft-size":"", "craft-acp":"", "craft-misc":"", "craft-reqtrain":"", "craft2-ability":"", "craft2-cs":"", "craft2-ranks":"", "craft2-class":"", "craft2-ability-mod":"", "craft2-racial":"", "craft2-feat":"", "craft2-item":"", "craft2-size":"", "craft2-acp":"", "craft2-misc":"", "craft2-reqtrain":"", "craft3-ability":"", "craft3-cs":"", "craft3-ranks":"", "craft3-class":"", "craft3-ability-mod":"", "craft3-racial":"", "craft3-feat":"", "craft3-item":"", "craft3-size":"", "craft3-acp":"", "craft3-misc":"", "craft3-reqtrain":"", "diplomacy-ability":"", "diplomacy-cs":"", "diplomacy-ranks":"", "diplomacy-class":"", "diplomacy-ability-mod":"", "diplomacy-racial":"", "diplomacy-feat":"", "diplomacy-item":"", "diplomacy-size":"", "diplomacy-acp":"", "diplomacy-misc":"", "diplomacy-reqtrain":"", "disable-device-ability":"", "disable-device-cs":"", "disable-device-ranks":"", "disable-device-class":"", "disable-device-ability-mod":"", "disable-device-racial":"", "disable-device-feat":"", "disable-device-item":"", "disable-device-size":"", "disable-device-acp":"", "disable-device-misc":"", "disable-device-reqtrain":"", "disguise-ability":"", "disguise-cs":"", "disguise-ranks":"", "disguise-class":"", "disguise-ability-mod":"", "disguise-racial":"", "disguise-feat":"", "disguise-item":"", "disguise-size":"", "disguise-acp":"", "disguise-misc":"", "disguise-reqtrain":"", "escape-artist-ability":"", "escape-artist-cs":"", "escape-artist-ranks":"", "escape-artist-class":"", "escape-artist-ability-mod":"", "escape-artist-racial":"", "escape-artist-feat":"", "escape-artist-item":"", "escape-artist-size":"", "escape-artist-acp":"", "escape-artist-misc":"", "escape-artist-reqtrain":"", "fly-ability":"", "fly-cs":"", "fly-ranks":"", "fly-class":"", "fly-ability-mod":"", "fly-racial":"", "fly-feat":"", "fly-item":"", "fly-size":"", "fly-acp":"", "fly-misc":"", "fly-reqtrain":"", "handle-animal-ability":"", "handle-animal-cs":"", "handle-animal-ranks":"", "handle-animal-class":"", "handle-animal-ability-mod":"", "handle-animal-racial":"", "handle-animal-feat":"", "handle-animal-item":"", "handle-animal-size":"", "handle-animal-acp":"", "handle-animal-misc":"", "handle-animal-reqtrain":"", "heal-ability":"", "heal-cs":"", "heal-ranks":"", "heal-class":"", "heal-ability-mod":"", "heal-racial":"", "heal-feat":"", "heal-item":"", "heal-size":"", "heal-acp":"", "heal-misc":"", "heal-reqtrain":"", "intimidate-ability":"", "intimidate-cs":"", "intimidate-ranks":"", "intimidate-class":"", "intimidate-ability-mod":"", "intimidate-racial":"", "intimidate-feat":"", "intimidate-item":"", "intimidate-size":"", "intimidate-acp":"", "intimidate-misc":"", "intimidate-reqtrain":"", "linguistics-ability":"", "linguistics-cs":"", "linguistics-ranks":"", "linguistics-class":"", "linguistics-ability-mod":"", "linguistics-racial":"", "linguistics-feat":"", "linguistics-item":"", "linguistics-size":"", "linguistics-acp":"", "linguistics-misc":"", "linguistics-reqtrain":"", "lore-ability":"", "lore-cs":"", "lore-ranks":"", "lore-class":"", "lore-ability-mod":"", "lore-racial":"", "lore-feat":"", "lore-item":"", "lore-size":"", "lore-acp":"", "lore-misc":"", "lore-reqtrain":"", "lore2-ability":"", "lore2-cs":"", "lore2-ranks":"", "lore2-class":"", "lore2-ability-mod":"", "lore2-racial":"", "lore2-feat":"", "lore2-item":"", "lore2-size":"", "lore2-acp":"", "lore2-misc":"", "lore2-reqtrain":"", "lore3-ability":"", "lore3-cs":"", "lore3-ranks":"", "lore3-class":"", "lore3-ability-mod":"", "lore3-racial":"", "lore3-feat":"", "lore3-item":"", "lore3-size":"", "lore3-acp":"", "lore3-misc":"", "lore3-reqtrain":"", "knowledge-arcana-ability":"", "knowledge-arcana-cs":"", "knowledge-arcana-ranks":"", "knowledge-arcana-class":"", "knowledge-arcana-ability-mod":"", "knowledge-arcana-racial":"", "knowledge-arcana-feat":"", "knowledge-arcana-item":"", "knowledge-arcana-size":"", "knowledge-arcana-acp":"", "knowledge-arcana-misc":"", "knowledge-arcana-reqtrain":"", "knowledge-dungeoneering-ability":"", "knowledge-dungeoneering-cs":"", "knowledge-dungeoneering-ranks":"", "knowledge-dungeoneering-class":"", "knowledge-dungeoneering-ability-mod":"", "knowledge-dungeoneering-racial":"", "knowledge-dungeoneering-feat":"", "knowledge-dungeoneering-item":"", "knowledge-dungeoneering-size":"", "knowledge-dungeoneering-acp":"", "knowledge-dungeoneering-misc":"", "knowledge-dungeoneering-reqtrain":"", "knowledge-engineering-ability":"", "knowledge-engineering-cs":"", "knowledge-engineering-ranks":"", "knowledge-engineering-class":"", "knowledge-engineering-ability-mod":"", "knowledge-engineering-racial":"", "knowledge-engineering-feat":"", "knowledge-engineering-item":"", "knowledge-engineering-size":"", "knowledge-engineering-acp":"", "knowledge-engineering-misc":"", "knowledge-engineering-reqtrain":"", "knowledge-geography-ability":"", "knowledge-geography-cs":"", "knowledge-geography-ranks":"", "knowledge-geography-class":"", "knowledge-geography-ability-mod":"", "knowledge-geography-racial":"", "knowledge-geography-feat":"", "knowledge-geography-item":"", "knowledge-geography-size":"", "knowledge-geography-acp":"", "knowledge-geography-misc":"", "knowledge-geography-reqtrain":"", "knowledge-history-ability":"", "knowledge-history-cs":"", "knowledge-history-ranks":"", "knowledge-history-class":"", "knowledge-history-ability-mod":"", "knowledge-history-racial":"", "knowledge-history-feat":"", "knowledge-history-item":"", "knowledge-history-size":"", "knowledge-history-acp":"", "knowledge-history-misc":"", "knowledge-history-reqtrain":"", "knowledge-local-ability":"", "knowledge-local-cs":"", "knowledge-local-ranks":"", "knowledge-local-class":"", "knowledge-local-ability-mod":"", "knowledge-local-racial":"", "knowledge-local-feat":"", "knowledge-local-item":"", "knowledge-local-size":"", "knowledge-local-acp":"", "knowledge-local-misc":"", "knowledge-local-reqtrain":"", "knowledge-nature-ability":"", "knowledge-nature-cs":"", "knowledge-nature-ranks":"", "knowledge-nature-class":"", "knowledge-nature-ability-mod":"", "knowledge-nature-racial":"", "knowledge-nature-feat":"", "knowledge-nature-item":"", "knowledge-nature-size":"", "knowledge-nature-acp":"", "knowledge-nature-misc":"", "knowledge-nature-reqtrain":"", "knowledge-nobility-ability":"", "knowledge-nobility-cs":"", "knowledge-nobility-ranks":"", "knowledge-nobility-class":"", "knowledge-nobility-ability-mod":"", "knowledge-nobility-racial":"", "knowledge-nobility-feat":"", "knowledge-nobility-item":"", "knowledge-nobility-size":"", "knowledge-nobility-acp":"", "knowledge-nobility-misc":"", "knowledge-nobility-reqtrain":"", "knowledge-planes-ability":"", "knowledge-planes-cs":"", "knowledge-planes-ranks":"", "knowledge-planes-class":"", "knowledge-planes-ability-mod":"", "knowledge-planes-racial":"", "knowledge-planes-feat":"", "knowledge-planes-item":"", "knowledge-planes-size":"", "knowledge-planes-acp":"", "knowledge-planes-misc":"", "knowledge-planes-reqtrain":"", "knowledge-religion-ability":"", "knowledge-religion-cs":"", "knowledge-religion-ranks":"", "knowledge-religion-class":"", "knowledge-religion-ability-mod":"", "knowledge-religion-racial":"", "knowledge-religion-feat":"", "knowledge-religion-item":"", "knowledge-religion-size":"", "knowledge-religion-acp":"", "knowledge-religion-misc":"", "knowledge-religion-reqtrain":"", "perception-ability":"", "perception-cs":"", "perception-ranks":"", "perception-class":"", "perception-ability-mod":"", "perception-racial":"", "perception-feat":"", "perception-item":"", "perception-size":"", "perception-acp":"", "perception-misc":"", "perception-reqtrain":"", "perform-ability":"", "perform-cs":"", "perform-ranks":"", "perform-class":"", "perform-ability-mod":"", "perform-racial":"", "perform-feat":"", "perform-item":"", "perform-size":"", "perform-acp":"", "perform-misc":"", "perform-reqtrain":"", "perform2-ability":"", "perform2-cs":"", "perform2-ranks":"", "perform2-class":"", "perform2-ability-mod":"", "perform2-racial":"", "perform2-feat":"", "perform2-item":"", "perform2-size":"", "perform2-acp":"", "perform2-misc":"", "perform2-reqtrain":"", "perform3-ability":"", "perform3-cs":"", "perform3-ranks":"", "perform3-class":"", "perform3-ability-mod":"", "perform3-racial":"", "perform3-feat":"", "perform3-item":"", "perform3-size":"", "perform3-acp":"", "perform3-misc":"", "perform3-reqtrain":"", "profession-ability":"", "profession-cs":"", "profession-ranks":"", "profession-class":"", "profession-ability-mod":"", "profession-racial":"", "profession-feat":"", "profession-item":"", "profession-size":"", "profession-acp":"", "profession-misc":"", "profession-reqtrain":"", "profession2-ability":"", "profession2-cs":"", "profession2-ranks":"", "profession2-class":"", "profession2-ability-mod":"", "profession2-racial":"", "profession2-feat":"", "profession2-item":"", "profession2-size":"", "profession2-acp":"", "profession2-misc":"", "profession2-reqtrain":"", "profession3-ability":"", "profession3-cs":"", "profession3-ranks":"", "profession3-class":"", "profession3-ability-mod":"", "profession3-racial":"", "profession3-feat":"", "profession3-item":"", "profession3-size":"", "profession3-acp":"", "profession3-misc":"", "profession3-reqtrain":"", "ride-ability":"", "ride-cs":"", "ride-ranks":"", "ride-class":"", "ride-ability-mod":"", "ride-racial":"", "ride-feat":"", "ride-item":"", "ride-size":"", "ride-acp":"", "ride-misc":"", "ride-reqtrain":"", "sense-motive-ability":"", "sense-motive-cs":"", "sense-motive-ranks":"", "sense-motive-class":"", "sense-motive-ability-mod":"", "sense-motive-racial":"", "sense-motive-feat":"", "sense-motive-item":"", "sense-motive-size":"", "sense-motive-acp":"", "sense-motive-misc":"", "sense-motive-reqtrain":"", "sleight-of-hand-ability":"", "sleight-of-hand-cs":"", "sleight-of-hand-ranks":"", "sleight-of-hand-class":"", "sleight-of-hand-ability-mod":"", "sleight-of-hand-racial":"", "sleight-of-hand-feat":"", "sleight-of-hand-item":"", "sleight-of-hand-size":"", "sleight-of-hand-acp":"", "sleight-of-hand-misc":"", "sleight-of-hand-reqtrain":"", "spellcraft-ability":"", "spellcraft-cs":"", "spellcraft-ranks":"", "spellcraft-class":"", "spellcraft-ability-mod":"", "spellcraft-racial":"", "spellcraft-feat":"", "spellcraft-item":"", "spellcraft-size":"", "spellcraft-acp":"", "spellcraft-misc":"", "spellcraft-reqtrain":"", "stealth-ability":"", " stealth-cs":"", "stealth-ranks":"", "stealth-class":"", "stealth-ability-mod":"", "stealth-racial":"", "stealth-feat":"", "stealth-item":"", "stealth-size":"", "stealth-acp":"", "stealth-misc":"", "stealth-reqtrain":"", "survival-ability":"", "survival-cs":"", "survival-ranks":"", "survival-class":"", "survival-ability-mod":"", "survival-racial":"", "survival-feat":"", "survival-item":"", "survival-size":"", "survival-acp":"", "survival-misc":"", "survival-reqtrain":"", "swim-ability":"", "swim-cs":"", "swim-ranks":"", "swim-class":"", "swim-ability-mod":"", "swim-racial":"", "swim-feat":"", "swim-item":"", "swim-size":"", "swim-acp":"", "swim-misc":"", "swim-reqtrain":"", "use-magic-device-ability":"", "use-magic-device-cs":"", "use-magic-device-ranks":"", "use-magic-device-class":"", "use-magic-device-ability-mod":"", "use-magic-device-racial":"", "use-magic-device-feat":"", "use-magic-device-item":"", "use-magic-device-size":"", "use-magic-device-acp":"", "use-magic-device-misc":"", "use-magic-device-reqtrain":"", "misc-skill-0-ability":"", "misc-skill-0-cs":"", "misc-skill-0-ranks":"", "misc-skill-0-class":"", "misc-skill-0-ability-mod":"", "misc-skill-0-racial":"", "misc-skill-0-feat":"", "misc-skill-0-item":"", "misc-skill-0-size":"", "misc-skill-0-acp":"", "misc-skill-0-misc":"", "misc-skill-0-reqtrain":"", "misc-skill-1-ability":"", "misc-skill-1-cs":"", "misc-skill-1-ranks":"", "misc-skill-1-class":"", "misc-skill-1-ability-mod":"", "misc-skill-1-racial":"", "misc-skill-1-feat":"", "misc-skill-1-item":"", "misc-skill-1-size":"", "misc-skill-1-acp":"", "misc-skill-1-misc":"", "misc-skill-1-reqtrain":"", "misc-skill-2-ability":"", "misc-skill-2-cs":"", "misc-skill-2-ranks":"", "misc-skill-2-class":"", "misc-skill-2-ability-mod":"", "misc-skill-2-racial":"", "misc-skill-2-feat":"", "misc-skill-2-item":"", "misc-skill-2-size":"", "misc-skill-2-acp":"", "misc-skill-2-misc":"", "misc-skill-2-reqtrain":"", "misc-skill-3-ability":"", "misc-skill-3-cs":"", "misc-skill-3-ranks":"", "misc-skill-3-class":"", "misc-skill-3-ability-mod":"", "misc-skill-3-racial":"", "misc-skill-3-feat":"", "misc-skill-3-item":"", "misc-skill-3-size":"", "misc-skill-3-acp":"", "misc-skill-3-misc":"", "misc-skill-3-reqtrain":"", "misc-skill-4-ability":"", "misc-skill-4-cs":"", "misc-skill-4-ranks":"", "misc-skill-4-class":"", "misc-skill-4-ability-mod":"", "misc-skill-4-racial":"", "misc-skill-4-feat":"", "misc-skill-4-item":"", "misc-skill-4-size":"", "misc-skill-4-acp":"", "misc-skill-4-misc":"", "misc-skill-4-reqtrain":"", "misc-skill-5-ability":"", "misc-skill-5-cs":"", "misc-skill-5-ranks":"", "misc-skill-5-class":"", "misc-skill-5-ability-mod":"", "misc-skill-5-racial":"", "misc-skill-5-feat":"", "misc-skill-5-item":"", "misc-skill-5-size":"", "misc-skill-5-acp":"", "misc-skill-5-misc":"", "misc-skill-5-reqtrain":"", "craft-name":"", "craft2-name":"", "craft3-name":"", "lore-name":"", "perform-name":"", "perform2-name":"", "perform3-name":"", "profession-name":"", "profession2-name":"", "profession3-name":"", "misc-skill-0-name":"", "misc-skill-1-name":"", "misc-skill-2-name":"", "misc-skill-3-name":"", "misc-skill-4-name":"", "misc-skill-5-name":"" }); | |
// Keep track of which of these skills we're on | |
var craft = 1; | |
var perform = 1; | |
var profession = 1; | |
var artistry = 1; | |
var lore = 1; | |
var misc = 0; | |
var i = 0; | |
var skill; | |
var skillMisc; | |
var skillAttrPrefix; | |
for (i = 0; i < skills.length; i++) | |
{ | |
/*if (_.isUndefined(skill._name)) | |
{ | |
continue; | |
}*/ | |
skill = skills[i]; | |
console.log(skill._name); | |
// Figure out where we're putting this skill on the character sheet | |
if (skill._name.indexOf("Craft") !== -1) | |
{ | |
if (craft === 1) | |
{ | |
skillAttrPrefix = "craft"; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs["craft-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
craft++; | |
} | |
else if (craft <= 3) | |
{ | |
skillAttrPrefix = "craft" + craft; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs["craft"+craft+"-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
craft++; | |
} | |
else | |
{ | |
if (misc <= 5) | |
{ | |
skillAttrPrefix = "misc-skill-" + misc; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs[skillAttrPrefix+"-name"] = skill._name; | |
misc++; | |
} | |
else | |
console.log("Ran out of misc skills for " + skill._name + "!"); | |
} | |
} | |
else if (skill._name.indexOf("Perform") !== -1) | |
{ | |
if (perform === 1) | |
{ | |
skillAttrPrefix = "perform"; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs["perform-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
perform++; | |
} | |
else if (perform <= 3) | |
{ | |
skillAttrPrefix = "perform" + perform; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs["perform"+perform+"-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
perform++; | |
} | |
else | |
{ | |
if (misc <= 5) | |
{ | |
skillAttrPrefix = "misc-skill-" + misc; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs[skillAttrPrefix+"-name"] = skill._name; | |
misc++; | |
} | |
else | |
console.log("Ran out of misc skills for " + skill._name + "!"); | |
} | |
} | |
else if (skill._name.indexOf("Profession") !== -1) | |
{ | |
if (profession === 1) | |
{ | |
skillAttrPrefix = "profession"; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs["profession-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
profession++; | |
} | |
else if (profession <= 3) | |
{ | |
skillAttrPrefix = "profession" + profession; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs["profession"+profession+"-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
profession++; | |
} | |
else | |
{ | |
if (misc <= 5) | |
{ | |
skillAttrPrefix = "misc-skill-" + misc; | |
if (skill._name.match(/\(([^\)]+)\)/) !== null) | |
attrs[skillAttrPrefix+"-name"] = skill._name; | |
misc++; | |
} | |
else | |
console.log("Ran out of misc skills for " + skill._name + "!"); | |
} | |
} | |
else if (skill._name.indexOf("Knowledge") !== -1) | |
{ | |
switch(skill._name.match(/\(([^\)]+)\)/g)[0]) | |
{ | |
case "(arcana)": | |
case "(dungeoneering)": | |
case "(engineering)": | |
case "(geography)": | |
case "(history)": | |
case "(local)": | |
case "(nature)": | |
case "(nobility)": | |
case "(planes)": | |
case "(religion)": | |
skillAttrPrefix = skill._name.toLowerCase().replace(/\s/g,"-").replace("(","").replace(")",""); | |
break; | |
default: | |
skillAttrPrefix = "misc-skill-" + misc; | |
attrs[skillAttrPrefix+"-name"] = skill._name; | |
misc++; | |
} | |
} | |
else if (skill._name.indexOf("Artistry") !== -1) | |
{ | |
if (artistry === 1) | |
{ | |
skillAttrPrefix = "artistry"; | |
attrs["artistry-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
artistry++; | |
} | |
else if (artistry <= 3) | |
{ | |
skillAttrPrefix = "artistry" + artistry; | |
attrs["artistry"+artistry+"-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
artistry++; | |
} | |
else | |
{ | |
if (misc <= 5) | |
{ | |
skillAttrPrefix = "misc-skill-" + misc; | |
attrs[skillAttrPrefix+"-name"] = skill._name; | |
misc++; | |
} | |
else | |
console.log("Ran out of misc skills for " + skill._name + "!"); | |
} | |
} | |
else if (skill._name.indexOf("Lore") !== -1) | |
{ | |
if (lore === 1) | |
{ | |
skillAttrPrefix = "lore"; | |
attrs["lore-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
lore++; | |
} | |
else if (lore <= 3) | |
{ | |
skillAttrPrefix = "lore" + lore; | |
attrs["lore"+lore+"-name"] = skill._name.match(/\(([^\)]+)\)/)[0].replace("(","").replace(")",""); | |
lore++; | |
} | |
else | |
{ | |
if (misc <= 5) | |
{ | |
skillAttrPrefix = "misc-skill-" + misc; | |
attrs[skillAttrPrefix+"-name"] = skill._name; | |
misc++; | |
} | |
else | |
console.log("Ran out of misc skills for " + skill._name + "!"); | |
} | |
} | |
else | |
skillAttrPrefix = skill._name.toLowerCase().replace(/\s/g,"-").replace("(","").replace(")","").replace("-hand","-Hand").replace("e-device","e-Device").replace("-artist","-Artist").replace("-animal","-Animal"); | |
attrs[skillAttrPrefix+"-ranks"] = parseNum(skill._ranks); | |
attrs[skillAttrPrefix+"-ability"] = "@{"+skill._attrname+"-mod}"; | |
if (skill._classskill === "yes") attrs[skillAttrPrefix+"-cs"] = 3; | |
skillMisc = parseNum(skill._value) - parseNum(skill._ranks)- parseNum(skill._attrbonus); | |
if (parseNum(skill._ranks) != 0 && skill._classskill === "yes") | |
skillMisc -= 3; | |
if (skill._armorcheck === "yes") | |
skillMisc -= ACP; | |
if (skill._name === "Fly") | |
skillMisc -= skillSize; | |
if (skill._name === "Stealth") | |
skillMisc -= (2 * skillSize); | |
attrs[skillAttrPrefix+"-misc"] = skillMisc; | |
if (skill._trainedonly === "yes") attrs[skillAttrPrefix+"-ReqTrain"] = 1; | |
// Add situation modifiers to the macro | |
if (!_.isUndefined(skill.situationalmodifiers.situationalmodifier)) | |
{ | |
var macro = "@{PC-whisper} &{template:pf_generic} {{color=@{rolltemplate_color}}} {{header_image=@{header_image-pf_generic-skill}}} @{toggle_rounded_flag} {{character_name=@{character_name}}} {{character_id=@{character_id}}} {{subtitle}} {{name="+skill._name+"}} {{Check=[[ @{skill-query} + [[ @{"+skillAttrPrefix+"} ]] ]]}}"; | |
skill.situationalmodifiers.situationalmodifier = arrayify(skill.situationalmodifiers.situationalmodifier); | |
var j = 0; | |
while (j < skill.situationalmodifiers.situationalmodifier.length) | |
{ | |
macro = macro + " {{" + skill.situationalmodifiers.situationalmodifier[j]._source + "=" + skill.situationalmodifiers.situationalmodifier[j]._text+"}}" | |
j++; | |
} | |
attrs[skillAttrPrefix+"-macro"] = macro; | |
} | |
} | |
}, | |
// Import ACP and Max Dex; these aren't included under items, but the final values are listed in penalties | |
importPenalties = function(attrs,penalties) | |
{ | |
var ACP = 0; | |
var i = 0; | |
while (i < penalties.length) | |
{ | |
if (penalties[i]._name === "Armor Check Penalty") | |
{ | |
ACP = parseNum(penalties[i]._value); | |
attrs["armor3-acp"] = ACP; | |
} | |
else if (penalties[i]._name === "Max Dex Bonus") | |
attrs["armor3-max-dex"] = Math.min(99, parseNum(penalties[i]._value)); // Hero Lab uses 1000 for Max Dex when player doesn't have one; cap it at 99 to match sheet default | |
i++; | |
} | |
return ACP; | |
}, | |
importAC = function(attrs,acObj) | |
{ | |
attrs["AC-natural"] = parseNum(acObj._fromnatural); | |
attrs["AC-deflect"] = parseNum(acObj._fromdeflect); | |
attrs["AC-dodge"] = parseNum(acObj._fromdodge); | |
// Are we replacing Dex to AC with something else? | |
if (acObj._fromdexterity === "") | |
{ | |
if (acObj._fromcharisma !== "") | |
{ | |
attrs["AC-ability"] = "( ((@{CHA-mod} + [[ @{max-dex-source} ]]) - abs(@{CHA-mod} - [[ @{max-dex-source} ]])) / 2 )"; | |
attrs["AC-misc"] = parseNum(acObj._ac) - 10 - parseNum(acObj._fromarmor) - parseNum(acObj._fromshield) - parseNum(acObj._fromcharisma) - parseNum(acObj._fromsize) - parseNum(acObj._fromnatural) - parseNum(acObj._fromdeflect) - parseNum(acObj._fromdodge); | |
} | |
else if (acObj._fromwisdom !== "") | |
{ | |
attrs["AC-ability"] = "( ((@{WIS-mod} + [[ @{max-dex-source} ]]) - abs(@{WIS-mod} - [[ @{max-dex-source} ]])) / 2 )"; | |
attrs["AC-misc"] = parseNum(acObj._ac) - 10 - parseNum(acObj._fromarmor) - parseNum(acObj._fromshield) - parseNum(acObj._fromwisdom) - parseNum(acObj._fromsize) - parseNum(acObj._fromnatural) - parseNum(acObj._fromdeflect) - parseNum(acObj._fromdodge); | |
} | |
else | |
attrs["AC-misc"] = parseNum(acObj._ac) - 10 - parseNum(acObj._fromarmor) - parseNum(acObj._fromshield) - parseNum(acObj._fromdexterity) - parseNum(acObj._fromsize) - parseNum(acObj._fromnatural) - parseNum(acObj._fromdeflect) - parseNum(acObj._fromdodge); | |
} | |
}, | |
importCharacter = function(characterObj) | |
{ | |
var attrs = {}; | |
importAbilityScores(attrs,characterObj.attributes.attribute); | |
importSaves(attrs,characterObj.saves); | |
var classes, spellClasses, archetypes = {}; | |
// Class objects won't exist for creatures w/o class levels, such as animals | |
if (!_.isUndefined(characterObj.classes.class)) | |
{ | |
// Class will be an array if multiclassed, but a single object if single-classed; make it an array, just to be safe | |
characterObj.classes.class = arrayify(characterObj.classes.class); | |
classes = importClasses(attrs, characterObj.classes.class); | |
// If any of the character's classes is a spellcaster, it'll be listed here, too | |
if (!_.isUndefined(characterObj.spellclasses.spellclass)) | |
{ | |
characterObj.spellclasses.spellclass = arrayify(characterObj.spellclasses.spellclass); | |
spellClasses = importSpellClasses(attrs, characterObj.spellclasses.spellclass,classes,characterObj.attributes.attribute); | |
// Well, it's a spellcaster, so let's import those spells, too! | |
if (!_.isUndefined(characterObj.spellsknown.spell)) | |
{ | |
characterObj.spellsknown.spell = arrayify(characterObj.spellsknown.spell); | |
importSpells(characterObj.spellsknown.spell,spellClasses); | |
} | |
if (!_.isUndefined(characterObj.spellbook.spell)) | |
{ | |
characterObj.spellbook.spell = arrayify(characterObj.spellbook.spell); | |
importSpells(characterObj.spellbook.spell,spellClasses); | |
} | |
if (!_.isUndefined(characterObj.spellsmemorized.spell)) | |
{ | |
characterObj.spellsmemorized.spell = arrayify(characterObj.spellsmemorized.spell); | |
importSpells(characterObj.spellsmemorized.spell,spellClasses); | |
} | |
} | |
// Need to keep track of what archetypes the character has, since class feature source could be an archetype | |
archetypes = buildArchetypeArray(classes); | |
} | |
importAC(attrs,characterObj.armorclass); | |
characterObj.penalties.penalty = arrayify(characterObj.penalties.penalty); | |
var ACP = importPenalties(attrs,characterObj.penalties.penalty); | |
// Build an object we can pass to the item importing, so we can attach this to the inventory item | |
var armorPenalties = {}; | |
armorPenalties.ACP = parseNum(attrs["armor3-acp"]); | |
armorPenalties.maxDex = parseNum(attrs["armor3-max-dex"]); | |
armorPenalties.spellfail = parseNum(attrs["armor3-spell-fail"]); | |
// We might change these values if we're using a shield, so don't set them outside of item import | |
if (!_.isUndefined(attrs["armor3-acp"])) | |
delete attrs["armor3-acp"]; | |
if (!_.isUndefined(attrs["armor3-spell-fail"])) | |
delete attrs["armor3-spell-fail"]; | |
var armor = _.reject(arrayify(characterObj.defenses.armor || {}),function(item) { return _.isUndefined(item._name); }); | |
var weapons = _.reject(arrayify(characterObj.melee.weapon || {}).concat(arrayify(characterObj.ranged.weapon || {})),function(item) { return _.isUndefined(item._name); }); | |
// "Tracked Resources" is a list of uses, either a quantity of items, charges, or uses per day | |
var resources = _.object(_.map(characterObj.trackedresources.trackedresource, function (resource) { return [resource._name,resource];})); | |
// Make an array of items, both magic and mundane | |
var items = _.reject(arrayify(characterObj.magicitems.item || {}).concat(arrayify(characterObj.gear.item || {})),function(item) { return _.isUndefined(item._name); }); | |
// "Specials" could include items, so we need to filter them out | |
var itemNames = _.map(items, function(obj) { return obj._name; }); | |
var specials = _.reject(arrayify(characterObj.attack.special).concat(arrayify(characterObj.defenses.special),arrayify(characterObj.otherspecials.special),arrayify(characterObj.spelllike.special)), function(obj) { return _.contains(itemNames, obj._name); }); | |
importItems(items,resources,armorPenalties,armor,weapons); | |
getSectionIDs("repeating_npc-spell-like-abilities", function(idarray) { | |
var abilityNameAttrs = _.union(_.map(idarray,function(id) { return "repeating_npc-spell-like-abilities_"+id+"_name"; }),_.map(idarray,function(id) { return "repeating_npc-spell-like-abilities_"+id+"_rule_category"; })); | |
getAttrs(abilityNameAttrs, function(abilityAttrs) { | |
var abilityObjList = {}; | |
var abilityKeys = Object.keys(abilityAttrs); | |
var asyncAttrs = {}; | |
_.each(abilityKeys,function(abilityKey) { | |
var rowID; | |
if (abilityKey.indexOf("_name") !== -1) | |
{ | |
rowID = abilityKey.substring("repeating_npc-spell-like-abilities_".length,(abilityKey.indexOf("_name"))); | |
if (_.isUndefined(abilityObjList[rowID])) | |
abilityObjList[rowID] = {}; | |
abilityObjList[rowID].name = abilityAttrs[abilityKey]; | |
} | |
if (abilityKey.indexOf("_rule_category") !== -1) | |
{ | |
rowID = abilityKey.substring("repeating_npc-spell-like-abilities_".length,(abilityKey.indexOf("_rule_category"))); | |
if (_.isUndefined(abilityObjList[rowID])) | |
abilityObjList[rowID] = {}; | |
abilityObjList[rowID].rulecategory= abilityAttrs[abilityKey]; | |
} | |
}); | |
_.each(Object.keys(abilityObjList),function(abilityKey) { abilityObjList[abilityKey].id = abilityKey; }); | |
if (!_.isUndefined(characterObj.feats.feat)) | |
{ | |
var featsArray = _.filter(abilityObjList,_.matcher({rulecategory:"feats"})); | |
var featsList = {}; | |
_.each(featsArray, function(obj){ featsList[obj.id] = obj.name; }); | |
characterObj.feats.feat = arrayify(characterObj.feats.feat); | |
importFeats(asyncAttrs, characterObj.feats.feat, featsList, resources); | |
} | |
if (!_.isUndefined(characterObj.traits.trait)) | |
{ | |
var traitsArray = _.filter(abilityObjList,_.matcher({rulecategory:"traits"})); | |
var traitsList = {}; | |
_.each(traitsArray, function(obj){ traitsList[obj.id] = obj.name; }); | |
characterObj.traits.trait = arrayify(characterObj.traits.trait); | |
importTraits(asyncAttrs, characterObj.traits.trait, traitsList, resources); | |
} | |
if (!_.isUndefined(characterObj.spelllike.special)) | |
{ | |
var SLAsArray = _.filter(abilityObjList,_.matcher({rulecategory:"spell-like-abilities"})); | |
var SLAsList = {}; | |
_.each(SLAsArray, function(obj){ SLAsList[obj.id] = obj.name; }); | |
characterObj.spelllike.special = arrayify(characterObj.spelllike.special); | |
importSLAs(asyncAttrs, characterObj.spelllike.special, SLAsList, resources); | |
} | |
var featuresArray = _.filter(abilityObjList, function (obj) { if (obj.rulecategory === "traits" || obj.rulecategory === "feats") return false; return true; }); | |
var featuresList = {}; | |
_.each(featuresArray, function(obj){ featuresList[obj.id] = obj.name; }); | |
importFeatures(asyncAttrs, featuresList, specials, archetypes, resources); | |
setAttrs(asyncAttrs); | |
}); | |
}); | |
attrs["experience"] = parseFloat(characterObj.xp._total); | |
attrs["class-0-bab"] = parseNum(characterObj.attack._baseattack); | |
// Set max hp; remove Con mod from hp first, since the sheet will add that in | |
// Since the XML doesn't break this down by class, add it all to class 0 | |
var level = calcHitDice(characterObj.health._hitdice); | |
attrs["class-0-hp"] = (parseNum(characterObj.health._hitpoints) - (level * parseNum(characterObj.attributes.attribute[2].attrbonus._modified))); | |
importInit(attrs,characterObj.initiative); | |
var racialHD = level - parseNum(characterObj.classes._level); | |
if (racialHD > 0) | |
attrs["npc-hd-num"] = racialHD; | |
var size = getSizeMod(characterObj.size._name); | |
attrs["size"] = size; | |
attrs["default_char_size"] = size; | |
characterObj.skills.skill = arrayify(characterObj.skills.skill); | |
importSkills(attrs,characterObj.skills.skill,size,ACP); | |
if (!_.isUndefined(characterObj.senses.special)) | |
{ | |
characterObj.senses.special = arrayify(characterObj.senses.special); | |
attrs["vision"] = buildList(characterObj.senses.special, "_shortname"); | |
} | |
if (!_.isUndefined(characterObj.damagereduction.special)) | |
{ | |
characterObj.damagereduction.special = arrayify(characterObj.damagereduction.special); | |
attrs["DR"] = buildList(characterObj.damagereduction.special, "_shortname"); | |
} | |
if (!_.isUndefined(characterObj.resistances.special)) | |
{ | |
characterObj.resistances.special = arrayify(characterObj.resistances.special); | |
attrs["resistances"] = buildList(characterObj.resistances.special, "_shortname"); | |
} | |
if (!_.isUndefined(characterObj.immunities.special)) | |
{ | |
characterObj.immunities.special = arrayify(characterObj.immunities.special); | |
attrs["immunities"] = buildList(characterObj.immunities.special, "_shortname"); | |
} | |
if (!_.isUndefined(characterObj.weaknesses.special)) | |
{ | |
characterObj.weaknesses.special = arrayify(characterObj.weaknesses.special); | |
attrs["weaknesses"] = buildList(characterObj.weaknesses.special, "_shortname"); | |
} | |
if (!_.isUndefined(characterObj.languages.language)) | |
{ | |
characterObj.languages.language = arrayify(characterObj.languages.language); | |
attrs["languages"] = buildList(characterObj.languages.language, "_name"); | |
} | |
attrs["character_name"] = characterObj._name; | |
attrs["player-name"] = characterObj._playername; | |
attrs["deity"] = characterObj.deity._name; | |
attrs["race"] = characterObj.race._racetext.substr(0,1).toUpperCase()+characterObj.race._racetext.substr(1,1000); | |
attrs["alignment"] = characterObj.alignment._name; | |
attrs["gender"] = characterObj.personal._gender; | |
attrs["age"] = characterObj.personal._age; | |
attrs["height"] = characterObj.personal.charheight._text; | |
attrs["weight"] = characterObj.personal.charweight._text; | |
attrs["hair"] = characterObj.personal._hair; | |
attrs["eyes"] = characterObj.personal._eyes; | |
attrs["skin"] = characterObj.personal._skin; | |
//attrs["npc-cr"] = characterObj.challengerating._text.replace("CR ",""); | |
//attrs["npc-xp"] = characterObj.xpaward._value; | |
if (!_.isUndefined(characterObj.favoredclasses.favoredclass)) | |
{ | |
characterObj.favoredclasses.favoredclass = arrayify(characterObj.favoredclasses.favoredclass); | |
attrs["class-favored"] = buildList(characterObj.favoredclasses.favoredclass, "_name"); | |
} | |
setAttrs(attrs); | |
}, | |
registerEventHandlers = function() { | |
on("change:herolab_import", function(eventInfo) { | |
getAttrs(["herolab_import"], function(values) { | |
var xmlObj; | |
if (_.isUndefined(values.herolab_import)) | |
return; | |
try { | |
xmlObj = JSON.parse(values.herolab_import); | |
if (_.isArray(xmlObj.document.public.character)) | |
importCharacter(xmlObj.document.public.character[0]); | |
else | |
importCharacter(xmlObj.document.public.character); | |
setAttrs({herolab_import:""},{silent: true}); | |
} | |
catch(err) {console.log(err);setAttrs({herolab_import: err.message},{silent: true});} | |
}); | |
}); | |
}; | |
registerEventHandlers(); | |
console.log(PFLog.l + ' HLImport module loaded ' + PFLog.r, PFLog.bg); | |
PFLog.modulecount++; | |
return { | |
importCharacter: importCharacter | |
}; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment