mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-09-08 16:12:33 +00:00
2506 lines
79 KiB
JavaScript
2506 lines
79 KiB
JavaScript
var currentVersion = '2.0.11';
|
|
|
|
String.prototype.capitalize = function () {
|
|
return this.replace(/\w\S*/g, function (txt) {
|
|
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
|
});
|
|
};
|
|
String.prototype.firstThree = function () {
|
|
return this.substring(0, 3);
|
|
};
|
|
function getIntValue(value, defaultValue) {
|
|
if (!defaultValue) {
|
|
defaultValue = 0;
|
|
}
|
|
return parseInt(value, 10) || defaultValue;
|
|
}
|
|
function getFloatValue(value, defaultValue) {
|
|
if (!defaultValue) {
|
|
defaultValue = 0;
|
|
}
|
|
return parseFloat(value) || defaultValue;
|
|
}
|
|
function getAbilityName(varName) {
|
|
if (!varName) {
|
|
return 'strength';
|
|
}
|
|
return getAbilityModName(varName).replace('_mod', '');
|
|
}
|
|
function getAbilityModName(varName) {
|
|
if (!varName) {
|
|
return 'strength_mod';
|
|
}
|
|
return varName.replace(/\W/g, '');
|
|
}
|
|
function getAbilityValue(v, varName, defaultAbility) {
|
|
if (!varName) {
|
|
if (defaultAbility) {
|
|
return getIntValue(v[defaultAbility]);
|
|
}
|
|
} else if (exists(varName)) {
|
|
varName = getAbilityModName(varName);
|
|
return getIntValue(v[varName], 0);
|
|
}
|
|
return 0;
|
|
}
|
|
function getAbilityShortName(varName, capital) {
|
|
if (!varName) {
|
|
return 'Str';
|
|
}
|
|
varName = getAbilityModName(varName);
|
|
if (capital) {
|
|
varName = varName.capitalize();
|
|
}
|
|
return varName.firstThree();
|
|
}
|
|
function exists(value) {
|
|
if (!value || value === '' || value === '0' || value === 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function getRowId(leadingString, eventInfo) {
|
|
var re = new RegExp(leadingString + '_([a-zA-Z0-9\-]*)_.*');
|
|
return eventInfo.sourceAttribute.replace(re, '$1');
|
|
}
|
|
function getRepeatingField(leadingString, eventInfo) {
|
|
var re = new RegExp(leadingString + '_[a-zA-Z0-9\-]*_(.*)');
|
|
|
|
return eventInfo.sourceAttribute.replace(re, '$1');
|
|
}
|
|
function isEmpty(obj) {
|
|
for (var prop in obj) {
|
|
if (obj.hasOwnProperty(prop)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function setFinalAttrs(v, finalSetAttrs) {
|
|
if (!isEmpty(finalSetAttrs)) {
|
|
for (var key in finalSetAttrs) {
|
|
if (finalSetAttrs.hasOwnProperty(key)) {
|
|
if (v[key] === finalSetAttrs[key]) {
|
|
delete finalSetAttrs[key];
|
|
}
|
|
}
|
|
}
|
|
setAttrs(finalSetAttrs);
|
|
}
|
|
}
|
|
function fromVOrFinalSetAttrs(v, finalSetAttrs, value) {
|
|
if (exists(finalSetAttrs[value])) {
|
|
return finalSetAttrs[value];
|
|
}
|
|
return v[value];
|
|
}
|
|
function parseAttackComponents(v, repeatingString, finalSetAttrs, options) {
|
|
var parsed = v[repeatingString + 'parsed'];
|
|
|
|
if (!exists(parsed)) {
|
|
parsed = finalSetAttrs[repeatingString + 'parsed'];
|
|
}
|
|
|
|
if (!exists(parsed) || parsed.indexOf(options.parseName) === -1) {
|
|
var aTriggerFieldExists = false;
|
|
|
|
for (var i = 0; i < options.triggerFields.length; i++) {
|
|
if (exists(v[repeatingString + options.triggerFields[i]])) {
|
|
aTriggerFieldExists = true;
|
|
}
|
|
}
|
|
|
|
if (aTriggerFieldExists && !exists(v[repeatingString + options.toggleField])) {
|
|
finalSetAttrs[repeatingString + options.toggleField] = options.toggleFieldSetTo;
|
|
}
|
|
if (options.attackAbility && !exists(v[repeatingString + 'attack_ability']) && v[repeatingString + 'attack_ability'] !== '0') {
|
|
finalSetAttrs[repeatingString + 'attack_ability'] = v.default_ability;
|
|
}
|
|
|
|
if (options.addCastingModifier) {
|
|
if (exists(v[repeatingString + 'damage']) && !exists(v[repeatingString + 'damage_ability']) && v[repeatingString + 'damage_ability'] !== '0') {
|
|
finalSetAttrs[repeatingString + 'damage_ability'] = v.default_ability;
|
|
}
|
|
if (exists(v[repeatingString + 'heal']) && !exists(v[repeatingString + 'heal_ability']) && v[repeatingString + 'heal_ability'] !== '0') {
|
|
finalSetAttrs[repeatingString + 'heal_ability'] = v.default_ability;
|
|
}
|
|
}
|
|
|
|
if (!exists(finalSetAttrs[repeatingString + 'parsed'])) {
|
|
finalSetAttrs[repeatingString + 'parsed'] = '';
|
|
}
|
|
finalSetAttrs[repeatingString + 'parsed'] += ' ' + options.parseName;
|
|
}
|
|
}
|
|
function hasUpperCase(string) {
|
|
return (/[A-Z]/.test(string));
|
|
}
|
|
function emptyIfUndefined(value) {
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
return value;
|
|
}
|
|
function ordinalSpellLevel(level) {
|
|
if (level === 0) {
|
|
return 'Cantrip';
|
|
} else {
|
|
switch (level % 10) {
|
|
case 1:
|
|
return level + 'st-level';
|
|
case 2:
|
|
return level + 'nd-level';
|
|
case 3:
|
|
return level + 'rd-level';
|
|
default:
|
|
return level + 'th-level';
|
|
}
|
|
}
|
|
}
|
|
function versionCompare(v1, v2, options) {
|
|
var lexicographical = options && options.lexicographical,
|
|
zeroExtend = options && options.zeroExtend,
|
|
v1parts = v1.split('.'),
|
|
v2parts = v2.split('.');
|
|
|
|
function isValidPart(x) {
|
|
return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
|
|
}
|
|
|
|
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
|
|
return NaN;
|
|
}
|
|
|
|
if (zeroExtend) {
|
|
while (v1parts.length < v2parts.length) v1parts.push('0');
|
|
while (v2parts.length < v1parts.length) v2parts.push('0');
|
|
}
|
|
|
|
if (!lexicographical) {
|
|
v1parts = v1parts.map(Number);
|
|
v2parts = v2parts.map(Number);
|
|
}
|
|
|
|
for (var i = 0; i < v1parts.length; ++i) {
|
|
if (v2parts.length == i) {
|
|
return 1;
|
|
}
|
|
|
|
if (v1parts[i] == v2parts[i]) {
|
|
continue;
|
|
}
|
|
else if (v1parts[i] > v2parts[i]) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (v1parts.length != v2parts.length) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
function getAbilityMod (score) {
|
|
return Math.floor((getIntValue(score) - 10) / 2);
|
|
}
|
|
|
|
var ABILITIES = ['strength', 'dexterity', 'constitution', 'intelligence', 'wisdom', 'charisma'];
|
|
var ADD = ' + ';
|
|
var SUBTRACT = ' - ';
|
|
var SPACE = ' ';
|
|
|
|
on('change:cp change:sp change:ep change:gp change:pp', function () {
|
|
getAttrs(['cp', 'copper_per_gold', 'sp', 'silver_per_gold', 'ep', 'electrum_per_gold', 'gp', 'pp', 'platinum_per_gold'], function (v) {
|
|
var copperPieces = getFloatValue(v.cp);
|
|
var silverPieces = getFloatValue(v.sp);
|
|
var electrumPieces = getFloatValue(v.ep);
|
|
var goldPieces = getFloatValue(v.gp);
|
|
var platinumPieces = getFloatValue(v.pp);
|
|
var copperPerGold = getFloatValue(v.copper_per_gold, 100);
|
|
var silverPerGold = getFloatValue(v.silver_per_gold, 10);
|
|
var electrumPerGold = getFloatValue(v.electrum_per_gold, 2);
|
|
var platinumPerGold = getFloatValue(v.platinum_per_gold, 10);
|
|
var totalGold = (copperPieces / copperPerGold) + (silverPieces / silverPerGold) + (electrumPieces / electrumPerGold) + goldPieces + (platinumPieces * platinumPerGold);
|
|
var coinWeight = (copperPieces + silverPieces + electrumPieces + goldPieces + platinumPieces) / 50;
|
|
setAttrs({
|
|
total_gp: totalGold.toFixed(2),
|
|
weight_coinage: coinWeight
|
|
});
|
|
});
|
|
});
|
|
|
|
function updateAbilityModifier (ability) {
|
|
var collectionArray = [ability, ability + '_bonus', ability + '_mod', ability + '_check_mod', 'global_ability_bonus', 'strength_mod', 'dexterity_mod'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var abilityScore = getIntValue(v[ability]);
|
|
var abilityBonus = getIntValue(v[ability + '_bonus']);
|
|
var globalAbilityBonus = getIntValue(v.global_ability_bonus);
|
|
var abilityMod = getAbilityMod((abilityScore + abilityBonus + globalAbilityBonus));
|
|
|
|
var abilityCheckFormula = abilityMod + '[' + ability.firstThree() + ' mod with bonus]';
|
|
abilityCheckFormula += ADD + '@{jack_of_all_trades_toggle}[jack of all trades]';
|
|
abilityCheckFormula += ADD + '(@{global_check_bonus})[global check bonus]';
|
|
|
|
var abilityModWithSign = abilityMod;
|
|
if (abilityMod >= 0) {
|
|
abilityModWithSign = '+' + abilityMod;
|
|
}
|
|
|
|
finalSetAttrs[ability + '_mod'] = abilityMod;
|
|
finalSetAttrs[ability + '_mod_with_sign'] = abilityModWithSign;
|
|
finalSetAttrs[ability + '_check_mod'] = abilityCheckFormula;
|
|
|
|
if (ability === 'strength') {
|
|
finalSetAttrs.finesse_mod = Math.max(abilityMod, getIntValue(v.dexterity_mod));
|
|
finalSetAttrs.carrying_capacity = abilityScore * 15;
|
|
finalSetAttrs.max_push_drag_lift = abilityScore * 30;
|
|
finalSetAttrs.encumbered = abilityScore * 5;
|
|
finalSetAttrs.heavily_encumbered = abilityScore * 10;
|
|
} else if (ability === 'dexterity') {
|
|
finalSetAttrs.finesse_mod = Math.max(abilityMod, getIntValue(v.strength_mod));
|
|
}
|
|
|
|
console.log('updateAbilityModifier', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
}
|
|
on('change:strength change:strength_bonus change:strength_check_mod change:global_ability_bonus', function () {
|
|
updateAbilityModifier('strength');
|
|
});
|
|
on('change:dexterity change:dexterity_bonus change:dexterity_check_mod change:global_ability_bonus', function () {
|
|
updateAbilityModifier('dexterity');
|
|
});
|
|
on('change:constitution change:constitution_bonus change:constitution_check_mod change:global_ability_bonus', function () {
|
|
updateAbilityModifier('constitution');
|
|
});
|
|
on('change:intelligence change:intelligence_bonus change:intelligence_check_mod change:global_ability_bonus', function () {
|
|
updateAbilityModifier('intelligence');
|
|
});
|
|
on('change:wisdom change:wisdom_bonus change:wisdom_check_mod change:global_ability_bonus', function () {
|
|
updateAbilityModifier('wisdom');
|
|
});
|
|
on('change:charisma change:charisma_bonus change:charisma_check_mod change:global_ability_bonus', function () {
|
|
updateAbilityModifier('charisma');
|
|
});
|
|
on('change:dexterity_mod', function () {
|
|
updateArmor();
|
|
});
|
|
|
|
function updateLevels () {
|
|
var repeatingItem = 'repeating_class';
|
|
var collectionArray = [];
|
|
var finalSetAttrs = {};
|
|
|
|
var defaultClassDetails = {
|
|
barbarian: {
|
|
hd: 'd12'
|
|
},
|
|
bard: {
|
|
hd: 'd8',
|
|
spellcasting: 'full'
|
|
},
|
|
cleric: {
|
|
hd: 'd8',
|
|
spellcasting: 'full'
|
|
},
|
|
druid: {
|
|
hd: 'd8',
|
|
spellcasting: 'full'
|
|
},
|
|
fighter: {
|
|
hd: 'd10'
|
|
},
|
|
monk: {
|
|
hd: 'd8'
|
|
},
|
|
paladin: {
|
|
hd: 'd10',
|
|
spellcasting: 'half'
|
|
},
|
|
ranger: {
|
|
hd: 'd10',
|
|
spellcasting: 'half'
|
|
},
|
|
rogue: {
|
|
hd: 'd8'
|
|
},
|
|
sorcerer: {
|
|
hd: 'd6',
|
|
spellcasting: 'full'
|
|
},
|
|
warlock: {
|
|
hd: 'd8',
|
|
spellcasting: 'warlock'
|
|
},
|
|
wizard: {
|
|
hd: 'd6',
|
|
spellcasting: 'full'
|
|
}
|
|
};
|
|
|
|
var hd = {
|
|
d20: 0,
|
|
d12: 0,
|
|
d10: 0,
|
|
d8: 0,
|
|
d6: 0,
|
|
d4: 0,
|
|
d2: 0,
|
|
d0: 0
|
|
};
|
|
|
|
var spellcasting = {
|
|
full: 0,
|
|
half: 0,
|
|
third: 0,
|
|
warlock: 0
|
|
};
|
|
var totalLevel = 0;
|
|
var levelArray = [];
|
|
var sorcererLevels = 0;
|
|
var classesWithSpellcasting = 0;
|
|
var xpTable = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000, 305000, 355000, 385000, 405000, 435000, 465000, 495000, 525000, 555000, 585000, 605000, 635000, 665000];
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'level');
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'custom_name');
|
|
collectionArray.push(repeatingString + 'hd');
|
|
collectionArray.push(repeatingString + 'spellcasting');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
var className = v[repeatingString + 'name'];
|
|
if (!exists(className)) {
|
|
className = 'barbarian';
|
|
}
|
|
if (className === 'custom') {
|
|
finalSetAttrs[repeatingString + 'custom_class_toggle'] = 'on';
|
|
var customName = v[repeatingString + 'custom_name'];
|
|
if (exists(customName)) {
|
|
className = customName;
|
|
} else {
|
|
className = 'custom';
|
|
}
|
|
} else {
|
|
finalSetAttrs[repeatingString + 'custom_class_toggle'] = 0;
|
|
}
|
|
|
|
var classLevel = getIntValue(v[repeatingString + 'level']);
|
|
totalLevel += classLevel;
|
|
levelArray.push(className.capitalize() + ' ' + classLevel);
|
|
|
|
var classHd = v[repeatingString + 'hd'];
|
|
if (!exists(classHd)) {
|
|
if (defaultClassDetails.hasOwnProperty(className)) {
|
|
classHd = defaultClassDetails[className].hd;
|
|
finalSetAttrs[repeatingString + 'hd'] = classHd;
|
|
} else {
|
|
classHd = 'd12';
|
|
}
|
|
}
|
|
hd[classHd] += classLevel;
|
|
|
|
var classSpellcasting = v[repeatingString + 'spellcasting'];
|
|
if (!exists(classSpellcasting)) {
|
|
if (defaultClassDetails.hasOwnProperty(className)) {
|
|
classSpellcasting = defaultClassDetails[className].spellcasting;
|
|
finalSetAttrs[repeatingString + 'spellcasting'] = classSpellcasting;
|
|
}
|
|
}
|
|
if (exists(classSpellcasting)) {
|
|
classesWithSpellcasting += 1;
|
|
spellcasting[classSpellcasting] += classLevel;
|
|
}
|
|
|
|
if (className === 'sorcerer' || className === 'sorcerer') {
|
|
sorcererLevels += classLevel;
|
|
}
|
|
}
|
|
|
|
for (var key in hd) {
|
|
if (hd.hasOwnProperty(key)) {
|
|
if (hd[key] !== 0) {
|
|
finalSetAttrs['hd_' + key + '_max'] = hd[key];
|
|
finalSetAttrs['hd_' + key + '_toggle'] = 1;
|
|
} else {
|
|
finalSetAttrs['hd_' + key + '_max'] = 0;
|
|
finalSetAttrs['hd_' + key + '_toggle'] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
var casterLevel = 0;
|
|
casterLevel += spellcasting.full;
|
|
casterLevel += Math.floor(spellcasting.half / 2);
|
|
casterLevel += Math.floor(spellcasting.third / 3);
|
|
finalSetAttrs.caster_level = casterLevel;
|
|
|
|
if (classesWithSpellcasting > 1 || spellcasting.full) {
|
|
console.log('first if');
|
|
finalSetAttrs.caster_type = 'full';
|
|
} else if (spellcasting.half) {
|
|
console.log('half');
|
|
finalSetAttrs.caster_type = 'half';
|
|
} else if (spellcasting.third) {
|
|
console.log('third');
|
|
finalSetAttrs.caster_type = 'third';
|
|
} else {
|
|
console.log('else');
|
|
finalSetAttrs.caster_type = 'full';
|
|
}
|
|
|
|
console.log('finalSetAttrs.caster_type', finalSetAttrs.caster_type);
|
|
|
|
finalSetAttrs.level = totalLevel;
|
|
finalSetAttrs.class_and_level = levelArray.join(' ');
|
|
|
|
var xpForNextLevel = 0;
|
|
if (!totalLevel) {
|
|
totalLevel = 0;
|
|
}
|
|
if (totalLevel > 30) {
|
|
xpForNextLevel = xpTable[30];
|
|
} else {
|
|
xpForNextLevel = xpTable[totalLevel];
|
|
}
|
|
finalSetAttrs.xp_next_level = xpForNextLevel;
|
|
|
|
if (sorcererLevels > 0) {
|
|
finalSetAttrs.has_sorcerer_levels = 'on';
|
|
if (sorcererLevels === 1) {
|
|
finalSetAttrs.sorcery_points_max = 0;
|
|
} else {
|
|
finalSetAttrs.sorcery_points_max = sorcererLevels;
|
|
}
|
|
} else {
|
|
finalSetAttrs.has_sorcerer_levels = 0;
|
|
}
|
|
if (spellcasting.warlock > 0) {
|
|
finalSetAttrs.has_warlock_levels = 'on';
|
|
|
|
if (spellcasting.warlock === 1) {
|
|
finalSetAttrs.warlock_spell_slots_max = 1;
|
|
} else if (spellcasting.warlock >= 2 && spellcasting.warlock < 11) {
|
|
finalSetAttrs.warlock_spell_slots_max = 2;
|
|
} else if (spellcasting.warlock >= 11 && spellcasting.warlock < 17) {
|
|
finalSetAttrs.warlock_spell_slots_max = 3;
|
|
} else {
|
|
finalSetAttrs.warlock_spell_slots_max = 4;
|
|
}
|
|
} else {
|
|
finalSetAttrs.has_warlock_levels = 0;
|
|
}
|
|
|
|
console.log('updateLevels', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
}
|
|
|
|
on('change:repeating_class remove:repeating_class', function () {
|
|
console.log('repeating_class');
|
|
updateLevels();
|
|
});
|
|
|
|
var updateSpellSlots = function () {
|
|
var collectionArray = ['level', 'caster_level', 'caster_type'];
|
|
var finalSetAttrs = {};
|
|
|
|
var spellSlots = {
|
|
1: 0,
|
|
2: 0,
|
|
3: 0,
|
|
4: 0,
|
|
5: 0,
|
|
6: 0,
|
|
7: 0,
|
|
8: 0,
|
|
9: 0
|
|
};
|
|
|
|
for (var i = 1; i <= 9; i++) {
|
|
var repeatingString = 'spell_slots_l' + i + '_';
|
|
collectionArray.push(repeatingString + 'calc');
|
|
collectionArray.push(repeatingString + 'bonus');
|
|
collectionArray.push(repeatingString + 'max');
|
|
}
|
|
getAttrs(collectionArray, function (v) {
|
|
var casterLevel = getIntValue(v.level);
|
|
var casterType = v.caster_type;
|
|
if (!exists(casterType)) {
|
|
casterType = 'full';
|
|
}
|
|
|
|
if (casterType === 'full') {
|
|
casterLevel = getIntValue(v.caster_level);
|
|
|
|
if (casterLevel >= 3) {
|
|
spellSlots['1'] = 4;
|
|
} else if (casterLevel === 2) {
|
|
spellSlots['1'] = 3;
|
|
} else if (casterLevel === 1) {
|
|
spellSlots['1'] = 2;
|
|
}
|
|
if (casterLevel >= 4) {
|
|
spellSlots['2'] = 3;
|
|
} else if (casterLevel === 3) {
|
|
spellSlots['2'] = 2;
|
|
}
|
|
if (casterLevel >= 6) {
|
|
spellSlots['3'] = 3;
|
|
} else if (casterLevel === 5) {
|
|
spellSlots['3'] = 2;
|
|
}
|
|
if (casterLevel >= 9) {
|
|
spellSlots['4'] = 3;
|
|
} else if (casterLevel === 8) {
|
|
spellSlots['4'] = 2;
|
|
} else if (casterLevel === 7) {
|
|
spellSlots['4'] = 1;
|
|
}
|
|
if (casterLevel >= 18) {
|
|
spellSlots['5'] = 3;
|
|
} else if (casterLevel >= 10) {
|
|
spellSlots['5'] = 2;
|
|
} else if (casterLevel === 9) {
|
|
spellSlots['5'] = 1;
|
|
}
|
|
if (casterLevel >= 19) {
|
|
spellSlots['6'] = 2;
|
|
} else if (casterLevel >= 11) {
|
|
spellSlots['6'] = 1;
|
|
}
|
|
if (casterLevel >= 20) {
|
|
spellSlots['7'] = 2;
|
|
} else if (casterLevel >= 13) {
|
|
spellSlots['7'] = 1;
|
|
}
|
|
if (casterLevel >= 15) {
|
|
spellSlots['8'] = 1;
|
|
}
|
|
if (casterLevel >= 17) {
|
|
spellSlots['9'] = 1;
|
|
}
|
|
}
|
|
|
|
if (casterType === 'half') {
|
|
if (casterLevel >= 5) {
|
|
spellSlots['1'] = 4;
|
|
} else if (casterLevel >= 3) {
|
|
spellSlots['1'] = 3;
|
|
} else if (casterLevel === 2) {
|
|
spellSlots['1'] = 2;
|
|
}
|
|
if (casterLevel >= 7) {
|
|
spellSlots['2'] = 3;
|
|
} else if (casterLevel >= 5) {
|
|
spellSlots['2'] = 2;
|
|
}
|
|
if (casterLevel >= 11) {
|
|
spellSlots['3'] = 3;
|
|
} else if (casterLevel >= 9) {
|
|
spellSlots['3'] = 2;
|
|
}
|
|
if (casterLevel >= 17) {
|
|
spellSlots['4'] = 3;
|
|
} else if (casterLevel >= 15) {
|
|
spellSlots['4'] = 2;
|
|
} else if (casterLevel >= 13) {
|
|
spellSlots['4'] = 1;
|
|
}
|
|
if (casterLevel >= 19) {
|
|
spellSlots['5'] = 2;
|
|
} else if (casterLevel >= 17) {
|
|
spellSlots['5'] = 1;
|
|
}
|
|
}
|
|
|
|
if (casterType === 'third') {
|
|
console.log('third casterLevel', casterLevel);
|
|
if (casterLevel >= 7) {
|
|
spellSlots['1'] = 4;
|
|
} else if (casterLevel >= 4) {
|
|
spellSlots['1'] = 3;
|
|
} else if (casterLevel === 3) {
|
|
spellSlots['1'] = 2;
|
|
}
|
|
if (casterLevel >= 10) {
|
|
spellSlots['2'] = 3;
|
|
} else if (casterLevel >= 7) {
|
|
spellSlots['2'] = 2;
|
|
}
|
|
if (casterLevel >= 16) {
|
|
spellSlots['3'] = 3;
|
|
} else if (casterLevel >= 13) {
|
|
spellSlots['3'] = 2;
|
|
}
|
|
if (casterLevel >= 19) {
|
|
spellSlots['4'] = 1;
|
|
}
|
|
}
|
|
|
|
for (var i = 1; i <= 9; i++) {
|
|
var slotCalc = spellSlots[i];
|
|
finalSetAttrs['spell_slots_l' + i + '_calc'] = slotCalc;
|
|
|
|
var slotBonus = getIntValue(v['spell_slots_l' + i + '_bonus']);
|
|
var spellSlotMax = slotCalc + slotBonus;
|
|
finalSetAttrs['spell_slots_l' + i + '_max'] = spellSlotMax;
|
|
|
|
if (spellSlotMax > 0) {
|
|
finalSetAttrs['spell_slots_l' + i + '_toggle'] = 'on';
|
|
} else {
|
|
finalSetAttrs['spell_slots_l' + i + '_toggle'] = 0;
|
|
}
|
|
}
|
|
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:caster_level change:spell_slots_l1_bonus change:spell_slots_l2_bonus change:spell_slots_l3_bonus change:spell_slots_l4_bonus change:spell_slots_l5_bonus change:spell_slots_l6_bonus change:spell_slots_l7_bonus change:spell_slots_l8_bonus change:spell_slots_l9_bonus', function () {
|
|
updateSpellSlots();
|
|
});
|
|
|
|
function getPB (level, challenge) {
|
|
var pb = 2;
|
|
|
|
level = getIntValue(level);
|
|
if (challenge < 1) {
|
|
challenge = 1;
|
|
} else {
|
|
getIntValue(challenge);
|
|
}
|
|
var levelOrChallenge = Math.max(level, challenge);
|
|
|
|
if (exists(levelOrChallenge)) {
|
|
pb += Math.floor(Math.abs((levelOrChallenge - 1) / 4));
|
|
}
|
|
return pb;
|
|
}
|
|
var updatePb = function () {
|
|
var collectionArray = ['level', 'challenge'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var pb = getPB(v.level, v.challenge);
|
|
|
|
finalSetAttrs.pb = pb;
|
|
finalSetAttrs.exp = pb * 2;
|
|
finalSetAttrs.h_PB = pb / 2;
|
|
|
|
console.log('updatePb', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:level', function () {
|
|
updatePb();
|
|
});
|
|
|
|
var sumRepeating = function (options, sumItems) {
|
|
var repeatingItem = 'repeating_' + options.collection;
|
|
var collectionArray = [];
|
|
var finalSetAttrs = {};
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + options.toggle);
|
|
if (options.qty) {
|
|
collectionArray.push(repeatingString + options.qty);
|
|
}
|
|
|
|
for (var x = 0; x < sumItems.length; x++) {
|
|
finalSetAttrs[sumItems[x].totalField] = 0;
|
|
if (sumItems[x].totalFieldSecondary) {
|
|
finalSetAttrs[sumItems[x].totalFieldSecondary] = 0;
|
|
}
|
|
collectionArray.push(repeatingString + sumItems[x].fieldToAdd);
|
|
if (sumItems[x].bonus) {
|
|
collectionArray.push(repeatingString + sumItems[x].bonus);
|
|
}
|
|
if (sumItems[x].armorType) {
|
|
collectionArray.push(repeatingString + sumItems[x].armorType);
|
|
}
|
|
if (sumItems[x].addOnAfterQty) {
|
|
collectionArray.push(repeatingString + sumItems[x].addOnAfterQty);
|
|
}
|
|
}
|
|
}
|
|
if (options.getExtraFields) {
|
|
collectionArray = collectionArray.concat(options.getExtraFields);
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var dexMod = 0;
|
|
if (options.collection === 'armor') {
|
|
dexMod = getIntValue(v.dexterity_mod);
|
|
}
|
|
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
var qty = getIntValue(v[repeatingString + options.qty], 1);
|
|
|
|
for (var x = 0; x < sumItems.length; x++) {
|
|
var sumItem = sumItems[x];
|
|
var fieldToAdd = getFloatValue(v[repeatingString + sumItem.fieldToAdd]);
|
|
if (sumItem.bonus) {
|
|
fieldToAdd += getFloatValue(v[repeatingString + sumItem.bonus]);
|
|
}
|
|
if (sumItem.armorType) {
|
|
if (!v[repeatingString + sumItem.armorType] || v[repeatingString + sumItem.armorType] === 'Light Armor') {
|
|
fieldToAdd += Math.min(5, dexMod);
|
|
} else if (v[repeatingString + sumItem.armorType] === 'Medium Armor') {
|
|
var mediumArmorDexMod = getIntValue(v.medium_armor_max_dex, 2);
|
|
fieldToAdd += Math.min(mediumArmorDexMod, dexMod);
|
|
} else if (v[repeatingString + sumItem.armorType] === 'Armor + Dex') {
|
|
fieldToAdd += dexMod;
|
|
}
|
|
}
|
|
|
|
var itemTotal = Math.round(qty * fieldToAdd * 100) / 100;
|
|
|
|
if (sumItem.addOnAfterQty) {
|
|
itemTotal += getFloatValue(v[repeatingString + sumItem.addOnAfterQty]);
|
|
}
|
|
|
|
if (sumItem.itemTotal) {
|
|
finalSetAttrs[repeatingString + sumItem.itemTotal] = itemTotal;
|
|
}
|
|
|
|
var toggle = v[repeatingString + options.toggle];
|
|
if (toggle !== 0 && toggle !== '0') {
|
|
var addToPrimary = true;
|
|
var addToSecondary = false;
|
|
|
|
if (sumItem.armorType) {
|
|
if (v[repeatingString + sumItem.armorType] === 'Shield' || v[repeatingString + sumItem.armorType] === 'AC') {
|
|
addToSecondary = true;
|
|
} else if (v[repeatingString + sumItem.armorType] === 'Unarmored') {
|
|
addToPrimary = false;
|
|
addToSecondary = true;
|
|
}
|
|
}
|
|
|
|
if (addToPrimary) {
|
|
finalSetAttrs[sumItem.totalField] += itemTotal;
|
|
}
|
|
if (addToSecondary) {
|
|
finalSetAttrs[sumItem.totalFieldSecondary] += itemTotal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (var y = 0; y < sumItems.length; y++) {
|
|
var item = sumItems[y];
|
|
if (item.totalField && !exists(finalSetAttrs[item.totalField])) {
|
|
finalSetAttrs[item.totalField] = 0;
|
|
}
|
|
if (item.totalFieldSecondary && !exists(finalSetAttrs[item.totalFieldSecondary])) {
|
|
finalSetAttrs[item.totalFieldSecondary] = 0;
|
|
}
|
|
}
|
|
|
|
if (options.collection === 'armor') {
|
|
finalSetAttrs.ac_unarmored_calc += 10 + dexMod + getAbilityValue(v, v.ac_unarmored_ability);
|
|
|
|
finalSetAttrs.ac = Math.max(finalSetAttrs.ac_armored_calc, finalSetAttrs.ac_unarmored_calc);
|
|
}
|
|
|
|
console.log('sumRepeating', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
var updateArmor = function (rowId) {
|
|
var repeatingItem = 'repeating_armor';
|
|
var collectionArray = [];
|
|
var finalSetAttrs = {};
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
if (rowId) {
|
|
ids = [];
|
|
ids.push(rowId);
|
|
}
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'modifiers');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
if (!exists(v[repeatingString + 'parsed']) || v[repeatingString + 'parsed'].indexOf('acBonus') === -1) {
|
|
var armorModifiers = v[repeatingString + 'modifiers'];
|
|
if (exists(armorModifiers)) {
|
|
var acBonus = armorModifiers.replace(/^\D+/g, '');
|
|
|
|
finalSetAttrs[repeatingString + 'ac_bonus'] = acBonus;
|
|
}
|
|
if (!exists(finalSetAttrs[repeatingString + 'parsed'])) {
|
|
finalSetAttrs[repeatingString + 'parsed'] = '';
|
|
}
|
|
finalSetAttrs[repeatingString + 'parsed'] += ' acBonus';
|
|
}
|
|
}
|
|
|
|
console.log('updateArmor', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
|
|
var options = {
|
|
collection: 'armor',
|
|
getExtraFields: ['medium_armor_max_dex', 'dexterity_mod', 'ac_unarmored_ability'],
|
|
toggle: 'worn'
|
|
};
|
|
for (var i = 0; i < ABILITIES.length; i++) {
|
|
options.getExtraFields.push(ABILITIES[i] + '_mod');
|
|
}
|
|
var sumItems = [
|
|
{
|
|
fieldToAdd: 'weight',
|
|
totalField: 'weight_armor'
|
|
},
|
|
{
|
|
fieldToAdd: 'ac_base',
|
|
bonus: 'ac_bonus',
|
|
armorType: 'type',
|
|
itemTotal: 'ac_total',
|
|
totalField: 'ac_armored_calc',
|
|
totalFieldSecondary: 'ac_unarmored_calc'
|
|
}
|
|
];
|
|
sumRepeating(options, sumItems);
|
|
};
|
|
on('change:repeating_armor', function (eventInfo) {
|
|
var changedField = getRepeatingField('repeating_armor', eventInfo);
|
|
if (changedField !== 'ac_total') {
|
|
var rowId = getRowId('repeating_armor', eventInfo);
|
|
updateArmor(rowId);
|
|
}
|
|
});
|
|
on('change:medium_armor_max_dex change:ac_unarmored_ability remove:repeating_armor', function () {
|
|
updateArmor();
|
|
});
|
|
|
|
var updateEquipment = function (rowId) {
|
|
var repeatingItem = 'repeating_equipment';
|
|
var collectionArray = [];
|
|
var finalSetAttrs = {};
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
if (rowId) {
|
|
ids = [];
|
|
ids.push(rowId);
|
|
}
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'content');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
if (!exists(v[repeatingString + 'parsed']) || v[repeatingString + 'parsed'].indexOf('content') === -1) {
|
|
var content = v[repeatingString + 'content'];
|
|
if (exists(content)) {
|
|
content = content.replace(/\s(\d+d\d+\s(?:\+|\-)\s\d+)\s/g, ' [[$1]] ')
|
|
.replace(/\s(\d+d\d+)\s/g, ' [[$1]] ')
|
|
.replace(/\s(\d+)\s/g, ' [[$1]] ');
|
|
|
|
finalSetAttrs[repeatingString + 'content'] = content;
|
|
}
|
|
if (!exists(finalSetAttrs[repeatingString + 'parsed'])) {
|
|
finalSetAttrs[repeatingString + 'parsed'] = '';
|
|
}
|
|
finalSetAttrs[repeatingString + 'parsed'] += ' content';
|
|
}
|
|
}
|
|
|
|
console.log('updateEquipment', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
on('change:repeating_equipment', function (eventInfo) {
|
|
var rowId = getRowId('repeating_equipment', eventInfo);
|
|
updateEquipment(rowId);
|
|
});
|
|
on('change:repeating_equipment:carried change:repeating_equipment:weight_total remove:repeating_equipment', function () {
|
|
var options = {
|
|
collection: 'equipment',
|
|
toggle: 'carried',
|
|
qty: 'qty'
|
|
};
|
|
var sumItems = [
|
|
{
|
|
fieldToAdd: 'weight',
|
|
itemTotal: 'weight_total',
|
|
totalField: 'weight_equipment'
|
|
}
|
|
];
|
|
sumRepeating(options, sumItems);
|
|
});
|
|
|
|
on('change:pb', function () {
|
|
console.log('pb changed');
|
|
updateSkill();
|
|
updateAttack();
|
|
updateSpell();
|
|
updateJackOfAllTrades();
|
|
});
|
|
|
|
var updateJackOfAllTrades = function () {
|
|
var collectionArray = ['pb'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
finalSetAttrs.jack_of_all_trades = Math.floor(getIntValue(v.pb) / 2);
|
|
|
|
console.log('updateJackOfAllTrades', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
on('change:jack_of_all_trades_toggle', function () {
|
|
updateJackOfAllTrades();
|
|
});
|
|
|
|
var updateInitiative = function () {
|
|
var collectionArray = ['dexterity_mod', 'dexterity_check_bonus', 'initiative_bonus', 'jack_of_all_trades_toggle', 'jack_of_all_trades', 'global_check_bonus'];
|
|
var finalSetAttrs = {};
|
|
|
|
finalSetAttrs.initiative = 0;
|
|
finalSetAttrs.initiative_formula = '';
|
|
getAttrs(collectionArray, function (v) {
|
|
|
|
var dexMod = getIntValue(v.dexterity_mod);
|
|
if (exists(dexMod)) {
|
|
finalSetAttrs.initiative += dexMod;
|
|
}
|
|
finalSetAttrs.initiative_formula += dexMod + '[dex]';
|
|
|
|
var dexCheckBonus = getIntValue(v.dexterity_check_bonus);
|
|
if (exists(dexCheckBonus)) {
|
|
finalSetAttrs.initiative += dexCheckBonus;
|
|
finalSetAttrs.initiative_formula += dexCheckBonus + '[dex check bonus]';
|
|
}
|
|
|
|
var initiativeBonus = getIntValue(v.initiative_bonus);
|
|
if (exists(initiativeBonus)) {
|
|
finalSetAttrs.initiative += initiativeBonus;
|
|
finalSetAttrs.initiative_formula += ADD + initiativeBonus + '[initiative bonus]';
|
|
}
|
|
|
|
if (v.jack_of_all_trades_toggle === '@{jack_of_all_trades}') {
|
|
var jackOfAllTrades = getIntValue(v.jack_of_all_trades);
|
|
if (exists(jackOfAllTrades)) {
|
|
finalSetAttrs.initiative += jackOfAllTrades;
|
|
finalSetAttrs.initiative_formula += ADD + jackOfAllTrades + '[jack of all trades]';
|
|
}
|
|
}
|
|
|
|
var globalCheckBonus = getIntValue(v.global_check_bonus);
|
|
if (exists(globalCheckBonus)) {
|
|
finalSetAttrs.initiative += globalCheckBonus;
|
|
finalSetAttrs.initiative_formula += ADD + globalCheckBonus + '[global check bonus]';
|
|
}
|
|
|
|
console.log('updateInitiative', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
on('change:dexterity_mod change:dexterity_check_bonus change:initiative_bonus change:jack_of_all_trades_toggle change:jack_of_all_trades change:global_check_bonus', function () {
|
|
updateInitiative();
|
|
});
|
|
|
|
var updateWeight = function () {
|
|
var collectionArray = ['weight_attacks', 'weight_armor', 'weight_equipment', 'weight_coinage', 'weight_misc'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
finalSetAttrs.weight_total = Math.round((getFloatValue(v.weight_attacks) + getFloatValue(v.weight_armor) + getFloatValue(v.weight_equipment) + getFloatValue(v.weight_coinage) + getFloatValue(v.weight_misc)) * 100) / 100;
|
|
|
|
console.log('updateWeight', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
on('change:weight_attacks change:weight_armor change:weight_equipment change:weight_coinage change:weight_misc', function () {
|
|
updateWeight();
|
|
});
|
|
|
|
on('change:repeating_attack', function (eventInfo) {
|
|
var changedField = getRepeatingField('repeating_attack', eventInfo);
|
|
if (changedField !== 'toggle_details' && changedField !== 'to_hit' && changedField !== 'attack_formula' && changedField !== 'damage_formula' && changedField !== 'second_damage_formula' && changedField !== 'damage_string' && changedField !== 'saving_throw_dc' && changedField !== 'parsed') {
|
|
var rowId = getRowId('repeating_attack', eventInfo);
|
|
updateAttack(rowId);
|
|
}
|
|
/*updateAttackQuery();*/
|
|
});
|
|
on('change:repeating_attack:carried change:repeating_attack:weight change:repeating_attack:ammo change:repeating_attack:ammo_weight remove:repeating_attack', function () {
|
|
var options = {
|
|
collection: 'attack',
|
|
toggle: 'carried',
|
|
qty: 'ammo'
|
|
};
|
|
var sumItems = [
|
|
{
|
|
addOnAfterQty: 'weight',
|
|
fieldToAdd: 'ammo_weight',
|
|
itemTotal: 'weight_total',
|
|
totalField: 'weight_attacks'
|
|
}
|
|
];
|
|
sumRepeating(options, sumItems);
|
|
});
|
|
on('change:global_attack_bonus change:global_melee_attack_bonus change:global_ranged_attack_bonus change:global_damage_bonus change:global_melee_damage_bonus change:global_ranged_damage_bonus', function () {
|
|
updateAttack();
|
|
});
|
|
|
|
var updateAttackToggle = function (v, finalSetAttrs, repeatingString, options) {
|
|
var attackParse = {
|
|
attackAbility: options.attackAbility,
|
|
parseName: 'attack',
|
|
toggleField: 'roll_toggle',
|
|
toggleFieldSetTo: '@{roll_toggle_var}',
|
|
triggerFields: ['type']
|
|
};
|
|
parseAttackComponents(v, repeatingString, finalSetAttrs, attackParse);
|
|
|
|
var attackFormula = '';
|
|
var attackToggle = v[repeatingString + 'roll_toggle'];
|
|
|
|
var toHit = 0;
|
|
if (!attackToggle || attackToggle === '@{roll_toggle_var}') {
|
|
var proficiency = v[repeatingString + 'proficiency'];
|
|
if (!proficiency || proficiency === 'on') {
|
|
var pb = getIntValue(v.pb);
|
|
toHit += pb;
|
|
attackFormula += pb + '[proficient]';
|
|
} else {
|
|
attackFormula += 0 + '[unproficient]';
|
|
}
|
|
|
|
if (!exists(v[repeatingString + 'ammo_weight']) && !exists(finalSetAttrs[repeatingString + 'ammo_weight']) && v[repeatingString + 'type'] === 'Ranged Weapon') {
|
|
finalSetAttrs[repeatingString + 'ammo'] = '.02';
|
|
finalSetAttrs[repeatingString + 'ammo_weight'] = '.02';
|
|
}
|
|
|
|
var attackAbility = v[repeatingString + 'attack_ability'];
|
|
if (!exists(attackAbility) && v[repeatingString + 'type'] === 'Ranged Weapon') {
|
|
attackAbility = '@{dexterity_mod}';
|
|
finalSetAttrs[repeatingString + 'attack_ability'] = attackAbility;
|
|
} else if (finalSetAttrs[repeatingString + 'attack_ability']) {
|
|
attackAbility = finalSetAttrs[repeatingString + 'attack_ability'];
|
|
}
|
|
attackAbility = getAbilityValue(v, attackAbility, options.defaultAbility);
|
|
if (exists(attackAbility)) {
|
|
toHit += attackAbility;
|
|
attackFormula += ADD + attackAbility + '[' + getAbilityShortName(v[repeatingString + 'attack_ability']) + ']';
|
|
}
|
|
|
|
var attackBonus = getIntValue(v[repeatingString + 'attack_bonus']);
|
|
if (exists(attackBonus)) {
|
|
toHit += attackBonus;
|
|
attackFormula += ADD + attackBonus + '[bonus]';
|
|
}
|
|
|
|
if (exists(options.globalAttackBonus)) {
|
|
toHit += options.globalAttackBonus;
|
|
attackFormula += ADD + options.globalAttackBonus + '[' + options.globalAttackBonusLabel + ']';
|
|
}
|
|
|
|
if (!v[repeatingString + 'type'] || v[repeatingString + 'type'] === 'Melee Weapon') {
|
|
if (exists(options.globalMeleeAttackBonus)) {
|
|
toHit += options.globalMeleeAttackBonus;
|
|
attackFormula += ADD + options.globalMeleeAttackBonus + '[global melee attack bonus]';
|
|
}
|
|
} else if (v[repeatingString + 'type'] === 'Ranged Weapon') {
|
|
if (exists(options.globalRangedAttackBonus)) {
|
|
toHit += options.globalRangedAttackBonus;
|
|
attackFormula += ADD + options.globalRangedAttackBonus + '[global ranged attack bonus]';
|
|
}
|
|
}
|
|
}
|
|
if (!exists(toHit)) {
|
|
toHit = 0;
|
|
}
|
|
if (options.type === 'attack') {
|
|
finalSetAttrs[repeatingString + 'to_hit'] = toHit;
|
|
}
|
|
finalSetAttrs[repeatingString + 'attack_formula'] = attackFormula;
|
|
};
|
|
|
|
var updateSavingThrowToggle = function (v, finalSetAttrs, repeatingString, options) {
|
|
var savingThrowParse = {
|
|
parseName: 'savingThrow',
|
|
toggleField: 'saving_throw_toggle',
|
|
toggleFieldSetTo: '@{saving_throw_toggle_var}',
|
|
triggerFields: ['saving_throw_vs_ability']
|
|
};
|
|
parseAttackComponents(v, repeatingString, finalSetAttrs, savingThrowParse);
|
|
|
|
var savingThrowToggle = v[repeatingString + 'saving_throw_toggle'];
|
|
if (savingThrowToggle === '@{saving_throw_toggle_var}') {
|
|
var savingThrowDC = 8 + getIntValue(v.pb);
|
|
var savingThrowAbility = v[repeatingString + 'saving_throw_ability'];
|
|
if (!savingThrowAbility && savingThrowAbility !== '0') {
|
|
savingThrowAbility = v.default_ability;
|
|
finalSetAttrs[repeatingString + 'saving_throw_ability'] = v.default_ability;
|
|
}
|
|
|
|
savingThrowDC += getAbilityValue(v, savingThrowAbility, 'strength_mod');
|
|
if (options && options.bonusDC) {
|
|
savingThrowDC += getIntValue(options.bonusDC);
|
|
}
|
|
savingThrowDC += getIntValue(v[repeatingString + 'saving_throw_bonus']);
|
|
finalSetAttrs[repeatingString + 'saving_throw_dc'] = savingThrowDC;
|
|
}
|
|
};
|
|
|
|
var updateDamageToggle = function (v, finalSetAttrs, repeatingString, options) {
|
|
var damageParse = {
|
|
addCastingModifier: exists(v[repeatingString + 'add_casting_modifier']),
|
|
parseName: 'damage',
|
|
toggleField: 'damage_toggle',
|
|
toggleFieldSetTo: '@{damage_toggle_var}',
|
|
triggerFields: ['damage']
|
|
};
|
|
parseAttackComponents(v, repeatingString, finalSetAttrs, damageParse);
|
|
|
|
var damageString = '';
|
|
var damageFormula = '';
|
|
var damageToggle = v[repeatingString + 'damage_toggle'];
|
|
var damageAbility;
|
|
var damageType;
|
|
|
|
if (!damageToggle || damageToggle === '@{damage_toggle_var}') {
|
|
var damageAddition = 0;
|
|
|
|
var damage = v[repeatingString + 'damage'];
|
|
if (exists(damage)) {
|
|
damageString += damage;
|
|
damageFormula += damage + '[damage]';
|
|
}
|
|
|
|
if (!options.defaultDamageAbility) {
|
|
options.defaultDamageAbility = 0;
|
|
}
|
|
|
|
damageAbility = v[repeatingString + 'damage_ability'];
|
|
if (!exists(damageAbility) && v[repeatingString + 'type'] === 'Ranged Weapon') {
|
|
damageAbility = '@{dexterity_mod}';
|
|
finalSetAttrs[repeatingString + 'damage_ability'] = damageAbility;
|
|
}
|
|
|
|
if (exists(damageAbility) || options.defaultDamageAbility) {
|
|
damageAbility = getAbilityValue(v, damageAbility, options.defaultDamageAbility);
|
|
if (exists(damageAbility)) {
|
|
damageAddition += damageAbility;
|
|
if (damageFormula !== '') {
|
|
damageFormula += ADD;
|
|
}
|
|
damageFormula += damageAbility + '[' + getAbilityShortName(v[repeatingString + 'damage_ability']) + ']';
|
|
}
|
|
}
|
|
|
|
var damageBonus = getIntValue(v[repeatingString + 'damage_bonus']);
|
|
if (exists(damageBonus)) {
|
|
damageAddition += damageBonus;
|
|
if (damageFormula !== '') {
|
|
damageFormula += ADD;
|
|
}
|
|
damageFormula += damageBonus + '[bonus]';
|
|
}
|
|
|
|
if (exists(options.globalDamageBonus)) {
|
|
damageAddition += options.globalDamageBonus;
|
|
damageFormula += ADD + options.globalDamageBonus + '[global damage bonus]';
|
|
}
|
|
|
|
if (options && options.globalMeleeDamageBonus && !v[repeatingString + 'type'] || v[repeatingString + 'type'] === 'Melee Weapon') {
|
|
damageAddition += options.globalMeleeDamageBonus;
|
|
if (damageFormula !== '') {
|
|
damageFormula += ADD;
|
|
}
|
|
damageFormula += options.globalMeleeDamageBonus + '[global melee damage bonus]';
|
|
} else if (options && options.globalRangedDamageBonus && v[repeatingString + 'type'] === 'Ranged Weapon') {
|
|
damageAddition += options.globalRangedDamageBonus;
|
|
if (damageFormula !== '') {
|
|
damageFormula += ADD;
|
|
}
|
|
damageFormula += options.globalRangedDamageBonus + '[global ranged damage bonus]';
|
|
}
|
|
|
|
if (exists(damageAddition)) {
|
|
damageString += ADD + damageAddition;
|
|
}
|
|
|
|
damageType = v[repeatingString + 'damage_type'];
|
|
if (exists(damageType)) {
|
|
if (hasUpperCase(damageType)) {
|
|
damageType = damageType.toLowerCase();
|
|
finalSetAttrs[repeatingString + 'damage_type'] = damageType;
|
|
}
|
|
damageString += SPACE + damageType;
|
|
}
|
|
}
|
|
if (!exists(damageFormula)) {
|
|
damageFormula = 0;
|
|
}
|
|
finalSetAttrs[repeatingString + 'damage_formula'] = damageFormula;
|
|
|
|
var secondDamageParse = {
|
|
parseName: 'secondDamage',
|
|
toggleField: 'second_damage_toggle',
|
|
toggleFieldSetTo: '@{second_damage_toggle_var}',
|
|
triggerFields: ['second_damage']
|
|
};
|
|
parseAttackComponents(v, repeatingString, finalSetAttrs, secondDamageParse);
|
|
|
|
var secondDamageFormula = '';
|
|
|
|
var secondDamageToggle = fromVOrFinalSetAttrs(v, finalSetAttrs, repeatingString + 'second_damage_toggle');
|
|
if (secondDamageToggle === '@{second_damage_toggle_var}') {
|
|
var secondDamageAddition = 0;
|
|
var secondDamage = v[repeatingString + 'second_damage'];
|
|
if (exists(secondDamage)) {
|
|
damageString += ADD + secondDamage;
|
|
secondDamageFormula += secondDamage + '[second damage]';
|
|
}
|
|
|
|
var secondDamageAbility = v[repeatingString + 'second_damage_ability'];
|
|
if (exists(secondDamageAbility)) {
|
|
secondDamageAbility = getAbilityValue(v, secondDamageAbility);
|
|
if (exists(secondDamageAbility)) {
|
|
secondDamageAddition += secondDamageAbility;
|
|
if (secondDamageFormula !== '') {
|
|
secondDamageFormula += ADD;
|
|
}
|
|
secondDamageFormula += secondDamageAbility + '[' + getAbilityShortName(v[repeatingString + 'second_damage_ability']) + ']';
|
|
}
|
|
}
|
|
|
|
var secondDamageBonus = getIntValue(v[repeatingString + 'second_damage_bonus']);
|
|
if (exists(secondDamageBonus)) {
|
|
secondDamageAddition += secondDamageBonus;
|
|
if (secondDamageFormula !== '') {
|
|
secondDamageFormula += ADD;
|
|
}
|
|
secondDamageFormula += secondDamageBonus + '[bonus]';
|
|
}
|
|
|
|
if (exists(secondDamageAddition)) {
|
|
damageString += ADD + secondDamageAddition;
|
|
}
|
|
|
|
var secondDamageType = v[repeatingString + 'second_damage_type'];
|
|
if (exists(secondDamageType)) {
|
|
if (hasUpperCase(secondDamageType)) {
|
|
secondDamageType = secondDamageType.toLowerCase();
|
|
finalSetAttrs[repeatingString + 'second_damage_type'] = secondDamageType;
|
|
}
|
|
damageString += SPACE + secondDamageType;
|
|
}
|
|
|
|
if (!exists(v[repeatingString + 'parsed']) || v[repeatingString + 'parsed'].indexOf('damageProperties') === -1) {
|
|
var damageProperties = v[repeatingString + 'properties'];
|
|
if (exists(damageProperties)) {
|
|
if (damageProperties.indexOf('Versatile') !== -1) {
|
|
if (!exists(damageAbility)) {
|
|
damageAbility = '@{strength_mod}';
|
|
}
|
|
finalSetAttrs[repeatingString + 'second_damage_ability'] = damageAbility;
|
|
finalSetAttrs[repeatingString + 'second_damage_type'] = damageType;
|
|
}
|
|
if (!finalSetAttrs[repeatingString + 'parsed']) {
|
|
finalSetAttrs[repeatingString + 'parsed'] = '';
|
|
}
|
|
finalSetAttrs[repeatingString + 'parsed'] += ' damageProperties';
|
|
}
|
|
}
|
|
|
|
}
|
|
if (!exists(secondDamageFormula)) {
|
|
secondDamageFormula = 0;
|
|
}
|
|
if (!exists(damageString)) {
|
|
damageString = ' ';
|
|
}
|
|
finalSetAttrs[repeatingString + 'second_damage_formula'] = secondDamageFormula;
|
|
if (options.type === 'attack') {
|
|
finalSetAttrs[repeatingString + 'damage_string'] = damageString;
|
|
}
|
|
};
|
|
|
|
var updateHealToggle = function (v, finalSetAttrs, repeatingString) {
|
|
var healParse = {
|
|
addCastingModifier: exists(v[repeatingString + 'add_casting_modifier']),
|
|
parseName: 'heal',
|
|
toggleField: 'heal_toggle',
|
|
toggleFieldSetTo: '@{heal_toggle_var}',
|
|
triggerFields: ['heal']
|
|
};
|
|
parseAttackComponents(v, repeatingString, finalSetAttrs, healParse);
|
|
|
|
var healFormula = '@{heal}[heal]';
|
|
var healToggle = v[repeatingString + 'heal_toggle'];
|
|
if (healToggle === '@{heal_toggle_var}') {
|
|
var healAbility = v[repeatingString + 'heal_ability'];
|
|
healAbility = getAbilityValue(v, healAbility);
|
|
if (exists(healAbility)) {
|
|
healFormula += ADD + healAbility + '[' + getAbilityShortName(v[repeatingString + 'heal_ability']) + ']';
|
|
}
|
|
|
|
var healBonus = getIntValue(v[repeatingString + 'heal_bonus']);
|
|
if (exists(healBonus)) {
|
|
healFormula += ADD + healBonus + '[bonus]';
|
|
}
|
|
|
|
if (exists(v.global_spell_heal_bonus)) {
|
|
healFormula += ADD + '@{global_spell_heal_bonus}[global spell heal bonus]';
|
|
}
|
|
}
|
|
|
|
finalSetAttrs[repeatingString + 'heal_formula'] = healFormula;
|
|
console.log('updateHealToggle', finalSetAttrs);
|
|
};
|
|
|
|
var updateHigherLevelToggle = function (v, finalSetAttrs, repeatingString) {
|
|
var higherLevelParse = {
|
|
addCastingModifier: exists(v[repeatingString + 'add_casting_modifier']),
|
|
parseName: 'higherLevel',
|
|
toggleField: 'higher_level_toggle',
|
|
toggleFieldSetTo: '@{higher_level_toggle_var}',
|
|
triggerFields: ['higher_level_dice', 'higher_level_die', 'second_higher_level_dice', 'second_higher_level_die', 'higher_level_heal']
|
|
};
|
|
parseAttackComponents(v, repeatingString, finalSetAttrs, higherLevelParse);
|
|
|
|
var higherLevelToggle = v[repeatingString + 'higher_level_toggle'];
|
|
if (exists(higherLevelToggle) && higherLevelToggle === '@{higher_level_toggle_var}') {
|
|
var spellLevelQuery = '?{Spell Level';
|
|
|
|
var spellLevel = getIntValue(v[repeatingString + 'spell_level']);
|
|
for (var i = spellLevel; i <= 9; i++) {
|
|
spellLevelQuery += '|' + i;
|
|
}
|
|
spellLevelQuery += '}';
|
|
finalSetAttrs[repeatingString + 'higher_level_query'] = spellLevelQuery;
|
|
|
|
var damageToggle = v[repeatingString + 'damage_toggle'];
|
|
if (damageToggle && damageToggle === '@{damage_toggle_var}') {
|
|
finalSetAttrs[repeatingString + 'damage_formula'] += ADD + '((@{higher_level_query} - @{spell_level}) * @{higher_level_dice})@{higher_level_die}[higher lvl]';
|
|
}
|
|
|
|
var secondDamageToggle = v[repeatingString + 'second_damage_toggle'];
|
|
if (secondDamageToggle && secondDamageToggle === '@{second_damage_toggle_var}') {
|
|
finalSetAttrs[repeatingString + 'second_damage_formula'] += ADD + '((@{higher_level_query} - @{spell_level}) * @{second_higher_level_dice})@{second_higher_level_die}[higher lvl]';
|
|
}
|
|
|
|
var healToggle = v[repeatingString + 'heal_toggle'];
|
|
if (healToggle && healToggle === '@{heal_toggle_var}') {
|
|
finalSetAttrs[repeatingString + 'heal_formula'] += ADD + '((@{higher_level_query} - @{spell_level}) * @{higher_level_dice})@{higher_level_die}[higher lvl] + (@{higher_level_heal} * (@{higher_level_query} - @{spell_level}))[higher lvl flat amount]';
|
|
}
|
|
}
|
|
console.log('updateHigherLevelToggle', finalSetAttrs);
|
|
};
|
|
|
|
var updateAttackQuery = function () {
|
|
var repeatingItem = 'repeating_attack';
|
|
var collectionArray = [];
|
|
var finalSetAttrs = {};
|
|
|
|
finalSetAttrs.attack_query_var = '?{Attack';
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'reach');
|
|
collectionArray.push(repeatingString + 'range');
|
|
collectionArray.push(repeatingString + 'ammo');
|
|
collectionArray.push(repeatingString + 'to_hit');
|
|
collectionArray.push(repeatingString + 'attack_formula');
|
|
collectionArray.push(repeatingString + 'roll_toggle');
|
|
collectionArray.push(repeatingString + 'saving_throw_toggle');
|
|
collectionArray.push(repeatingString + 'damage_toggle');
|
|
collectionArray.push(repeatingString + 'reach');
|
|
collectionArray.push(repeatingString + 'second_damage_toggle');
|
|
collectionArray.push(repeatingString + 'extras_toggle');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
finalSetAttrs.attack_query_var += '|' + v[repeatingString + 'name'] + ',';
|
|
finalSetAttrs.attack_query_var += ' {{title=' + v[repeatingString + 'name'] + '}}';
|
|
finalSetAttrs.attack_query_var += '{{reach=' + emptyIfUndefined(v[repeatingString + 'reach']) + '}}';
|
|
finalSetAttrs.attack_query_var += '{{range=' + emptyIfUndefined(v[repeatingString + 'range']) + '}}';
|
|
finalSetAttrs.attack_query_var += '{{ammo=' + emptyIfUndefined(v[repeatingString + 'ammo']) + '}}';
|
|
finalSetAttrs.attack_query_var += '' + emptyIfUndefined(v[repeatingString + 'roll_toggle']);
|
|
finalSetAttrs.attack_query_var += '' + emptyIfUndefined(v[repeatingString + 'saving_throw_toggle']);
|
|
finalSetAttrs.attack_query_var += '' + emptyIfUndefined(v[repeatingString + 'damage_toggle']);
|
|
finalSetAttrs.attack_query_var += '' + emptyIfUndefined(v[repeatingString + 'second_damage_toggle']);
|
|
finalSetAttrs.attack_query_var += '' + emptyIfUndefined(v[repeatingString + 'extras_toggle']);
|
|
}
|
|
finalSetAttrs.attack_query_var += '}';
|
|
|
|
console.log('updateAttackQuery', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
var updateAttack = function (rowId) {
|
|
var repeatingItem = 'repeating_attack';
|
|
var collectionArray = ['pb', 'finesse_mod', 'global_attack_bonus', 'global_melee_attack_bonus', 'global_ranged_attack_bonus', 'global_damage_bonus', 'global_melee_damage_bonus', 'global_ranged_damage_bonus', 'default_ability'];
|
|
var finalSetAttrs = {};
|
|
|
|
for (var i = 0; i < ABILITIES.length; i++) {
|
|
collectionArray.push(ABILITIES[i] + '_mod');
|
|
}
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
if (rowId) {
|
|
ids = [];
|
|
ids.push(rowId);
|
|
}
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'type');
|
|
collectionArray.push(repeatingString + 'roll_toggle');
|
|
collectionArray.push(repeatingString + 'to_hit');
|
|
collectionArray.push(repeatingString + 'attack_formula');
|
|
collectionArray.push(repeatingString + 'proficiency');
|
|
collectionArray.push(repeatingString + 'attack_ability');
|
|
collectionArray.push(repeatingString + 'attack_bonus');
|
|
collectionArray.push(repeatingString + 'saving_throw_toggle');
|
|
collectionArray.push(repeatingString + 'saving_throw_ability');
|
|
collectionArray.push(repeatingString + 'saving_throw_bonus');
|
|
collectionArray.push(repeatingString + 'saving_throw_dc');
|
|
collectionArray.push(repeatingString + 'damage_toggle');
|
|
collectionArray.push(repeatingString + 'damage_formula');
|
|
collectionArray.push(repeatingString + 'damage');
|
|
collectionArray.push(repeatingString + 'damage_ability');
|
|
collectionArray.push(repeatingString + 'damage_bonus');
|
|
collectionArray.push(repeatingString + 'damage_type');
|
|
collectionArray.push(repeatingString + 'second_damage_toggle');
|
|
collectionArray.push(repeatingString + 'second_damage_formula');
|
|
collectionArray.push(repeatingString + 'second_damage');
|
|
collectionArray.push(repeatingString + 'second_damage_ability');
|
|
collectionArray.push(repeatingString + 'second_damage_bonus');
|
|
collectionArray.push(repeatingString + 'second_damage_type');
|
|
collectionArray.push(repeatingString + 'damage_string');
|
|
collectionArray.push(repeatingString + 'modifiers');
|
|
collectionArray.push(repeatingString + 'properties');
|
|
collectionArray.push(repeatingString + 'weight');
|
|
collectionArray.push(repeatingString + 'ammo_weight');
|
|
collectionArray.push(repeatingString + 'parsed');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
var attackName = v[repeatingString + 'name'];
|
|
if (!exists(attackName)) {
|
|
return;
|
|
}
|
|
|
|
if (!exists(v[repeatingString + 'parsed']) || v[repeatingString + 'parsed'].indexOf('modifiers') === -1) {
|
|
var attackModifiers = v[repeatingString + 'modifiers'];
|
|
if (exists(attackModifiers)) {
|
|
var attackBonus = attackModifiers.replace(/.*(?:Melee|Ranged) Attacks \+(\d+).*/gi, '$1');
|
|
var damageBonus = attackModifiers.replace(/.*(?:Melee|Ranged) Damage \+(\d+).*/gi, '$1');
|
|
|
|
finalSetAttrs[repeatingString + 'attack_bonus'] = attackBonus;
|
|
finalSetAttrs[repeatingString + 'damage_bonus'] = damageBonus;
|
|
if (!finalSetAttrs[repeatingString + 'parsed']) {
|
|
finalSetAttrs[repeatingString + 'parsed'] = '';
|
|
}
|
|
finalSetAttrs[repeatingString + 'parsed'] += ' modifiers';
|
|
}
|
|
}
|
|
if (!exists(v[repeatingString + 'parsed']) || v[repeatingString + 'parsed'].indexOf('attackProperties') === -1) {
|
|
var attackProperties = v[repeatingString + 'properties'];
|
|
if (exists(attackProperties)) {
|
|
if (attackProperties.indexOf('Reach') !== -1) {
|
|
finalSetAttrs[repeatingString + 'reach'] = '10 ft';
|
|
} else if (v[repeatingString + 'type'] === 'Melee Weapon') {
|
|
finalSetAttrs[repeatingString + 'reach'] = '5 ft';
|
|
}
|
|
if (attackProperties.indexOf('Finesse') !== -1) {
|
|
finalSetAttrs[repeatingString + 'attack_ability'] = '@{finesse_mod}';
|
|
finalSetAttrs[repeatingString + 'damage_ability'] = '@{finesse_mod}';
|
|
}
|
|
if (attackProperties.indexOf('Thrown') !== -1) {
|
|
var ammoWeight = parseFloat(v[repeatingString + 'weight']);
|
|
finalSetAttrs[repeatingString + 'ammo'] = 1;
|
|
finalSetAttrs[repeatingString + 'ammo_weight'] = ammoWeight;
|
|
finalSetAttrs[repeatingString + 'weight'] = 0;
|
|
}
|
|
if (!finalSetAttrs[repeatingString + 'parsed']) {
|
|
finalSetAttrs[repeatingString + 'parsed'] = '';
|
|
}
|
|
finalSetAttrs[repeatingString + 'parsed'] += ' attackProperties';
|
|
}
|
|
}
|
|
|
|
var attackOptions = {
|
|
defaultAbility: 'strength_mod',
|
|
globalAttackBonus: getIntValue(v.global_attack_bonus),
|
|
globalAttackBonusLabel: 'global attack bonus',
|
|
globalMeleeAttackBonus: getIntValue(v.global_melee_attack_bonus),
|
|
globalRangedAttackBonus: getIntValue(v.global_ranged_attack_bonus),
|
|
type: 'attack'
|
|
};
|
|
updateAttackToggle(v, finalSetAttrs, repeatingString, attackOptions);
|
|
|
|
updateSavingThrowToggle(v, finalSetAttrs, repeatingString);
|
|
|
|
var damageOptions = {
|
|
defaultDamageAbility: 'strength_mod',
|
|
globalDamageBonus: getIntValue(v.global_damage_bonus),
|
|
globalMeleeDamageBonus: getIntValue(v.global_melee_damage_bonus),
|
|
globalRangedDamageBonus: getIntValue(v.global_ranged_damage_bonus),
|
|
type: 'attack'
|
|
};
|
|
updateDamageToggle(v, finalSetAttrs, repeatingString, damageOptions);
|
|
}
|
|
|
|
console.log('updateAttack', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
var updateSpell = function (rowId) {
|
|
var repeatingItem = 'repeating_spell';
|
|
var collectionArray = ['pb', 'finesse_mod', 'global_spell_attack_bonus', 'global_spell_damage_bonus', 'global_spell_dc_bonus', 'global_spell_heal_bonus', 'default_ability'];
|
|
var finalSetAttrs = {};
|
|
|
|
for (var i = 0; i < ABILITIES.length; i++) {
|
|
collectionArray.push(ABILITIES[i] + '_mod');
|
|
}
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
if (rowId) {
|
|
ids = [];
|
|
ids.push(rowId);
|
|
}
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'type');
|
|
collectionArray.push(repeatingString + 'roll_toggle');
|
|
collectionArray.push(repeatingString + 'to_hit');
|
|
collectionArray.push(repeatingString + 'attack_formula');
|
|
collectionArray.push(repeatingString + 'proficiency');
|
|
collectionArray.push(repeatingString + 'attack_ability');
|
|
collectionArray.push(repeatingString + 'attack_bonus');
|
|
collectionArray.push(repeatingString + 'saving_throw_toggle');
|
|
collectionArray.push(repeatingString + 'saving_throw_ability');
|
|
collectionArray.push(repeatingString + 'saving_throw_vs_ability');
|
|
collectionArray.push(repeatingString + 'saving_throw_bonus');
|
|
collectionArray.push(repeatingString + 'saving_throw_dc');
|
|
collectionArray.push(repeatingString + 'damage_toggle');
|
|
collectionArray.push(repeatingString + 'damage_formula');
|
|
collectionArray.push(repeatingString + 'damage');
|
|
collectionArray.push(repeatingString + 'damage_ability');
|
|
collectionArray.push(repeatingString + 'damage_bonus');
|
|
collectionArray.push(repeatingString + 'damage_type');
|
|
collectionArray.push(repeatingString + 'second_damage_toggle');
|
|
collectionArray.push(repeatingString + 'second_damage_formula');
|
|
collectionArray.push(repeatingString + 'second_damage');
|
|
collectionArray.push(repeatingString + 'second_damage_ability');
|
|
collectionArray.push(repeatingString + 'second_damage_bonus');
|
|
collectionArray.push(repeatingString + 'second_damage_type');
|
|
collectionArray.push(repeatingString + 'damage_string');
|
|
collectionArray.push(repeatingString + 'parsed');
|
|
collectionArray.push(repeatingString + 'spell_level');
|
|
collectionArray.push(repeatingString + 'casting_time');
|
|
collectionArray.push(repeatingString + 'components');
|
|
collectionArray.push(repeatingString + 'heal_toggle');
|
|
collectionArray.push(repeatingString + 'heal');
|
|
collectionArray.push(repeatingString + 'heal_ability');
|
|
collectionArray.push(repeatingString + 'heal_bonus');
|
|
collectionArray.push(repeatingString + 'add_casting_modifier');
|
|
collectionArray.push(repeatingString + 'higher_level_toggle');
|
|
collectionArray.push(repeatingString + 'higher_level_dice');
|
|
collectionArray.push(repeatingString + 'higher_level_die');
|
|
collectionArray.push(repeatingString + 'second_higher_level_dice');
|
|
collectionArray.push(repeatingString + 'second_higher_level_die');
|
|
collectionArray.push(repeatingString + 'higher_level_heal');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
var spellName = v[repeatingString + 'name'];
|
|
if (!exists(spellName)) {
|
|
return;
|
|
}
|
|
|
|
var spellLevel = getIntValue(v[repeatingString + 'spell_level']);
|
|
if (!exists(spellLevel)) {
|
|
spellLevel = 0;
|
|
finalSetAttrs[repeatingString + 'spell_level'] = spellLevel;
|
|
}
|
|
if (spellLevel === 0) {
|
|
finalSetAttrs[repeatingString + 'is_prepared'] = 'on';
|
|
}
|
|
finalSetAttrs[repeatingString + 'friendly_level'] = ordinalSpellLevel(spellLevel);
|
|
|
|
var spellComponents = v[repeatingString + 'components'];
|
|
if (exists(spellComponents)) {
|
|
if (spellComponents.indexOf('V') !== -1) {
|
|
finalSetAttrs[repeatingString + 'components_verbal'] = 1;
|
|
}
|
|
if (spellComponents.indexOf('S') !== -1) {
|
|
finalSetAttrs[repeatingString + 'components_somatic'] = 1;
|
|
}
|
|
if (spellComponents.indexOf('M') !== -1) {
|
|
finalSetAttrs[repeatingString + 'components_material'] = 1;
|
|
}
|
|
}
|
|
|
|
var attackOptions = {
|
|
attackAbility: true,
|
|
globalAttackBonus: getIntValue(v.global_spell_attack_bonus),
|
|
type: 'spell'
|
|
};
|
|
updateAttackToggle(v, finalSetAttrs, repeatingString, attackOptions);
|
|
|
|
var savingThrowOptions = {
|
|
bonusDC: v.global_spell_dc_bonus
|
|
};
|
|
updateSavingThrowToggle(v, finalSetAttrs, repeatingString, savingThrowOptions);
|
|
|
|
var damageOptions = {
|
|
globalDamageBonus: getIntValue(v.global_spell_damage_bonus),
|
|
type: 'spell'
|
|
};
|
|
updateDamageToggle(v, finalSetAttrs, repeatingString, damageOptions);
|
|
|
|
updateHealToggle(v, finalSetAttrs, repeatingString);
|
|
|
|
updateHigherLevelToggle(v, finalSetAttrs, repeatingString);
|
|
}
|
|
|
|
console.log('updateSpell', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
on('change:repeating_spell', function (eventInfo) {
|
|
var changedField = getRepeatingField('repeating_spell', eventInfo);
|
|
if (changedField !== 'toggle_details' && changedField !== 'to_hit' && changedField !== 'attack_formula' && changedField !== 'damage_formula' && changedField !== 'second_damage_formula' && changedField !== 'damage_string' && changedField !== 'saving_throw_dc' && changedField !== 'heal_formula' && changedField !== 'higher_level_query' && changedField !== 'parsed') {
|
|
console.log('spell changedField', changedField);
|
|
var rowId = getRowId('repeating_spell', eventInfo);
|
|
updateSpell(rowId);
|
|
}
|
|
});
|
|
on('change:global_spell_attack_bonus change:global_spell_damage_bonus change:global_spell_dc_bonus change:global_spell_heal_bonus', function () {
|
|
console.log('updateSpell triggered by global');
|
|
updateSpell();
|
|
});
|
|
|
|
var updateD20Mod = function () {
|
|
var collectionArray = ['halfling_luck'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
if (v.halfling_luck === 'on') {
|
|
finalSetAttrs.d20_mod = 'ro<1[halfling luck]';
|
|
} else {
|
|
finalSetAttrs.d20_mod = '';
|
|
}
|
|
|
|
console.log('updateD20Mod', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:halfling_luck', function () {
|
|
updateD20Mod();
|
|
});
|
|
|
|
var updateSkill = function (rowId) {
|
|
console.log('updateSkill', rowId);
|
|
var repeatingItem = 'repeating_skill';
|
|
var collectionArray = ['jack_of_all_trades_toggle', 'jack_of_all_trades', 'pb', 'exp', 'global_check_bonus'];
|
|
var finalSetAttrs = {};
|
|
|
|
for (var i = 0; i < ABILITIES.length; i++) {
|
|
collectionArray.push(ABILITIES[i] + '_mod');
|
|
collectionArray.push(ABILITIES[i] + '_check_bonus');
|
|
}
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
if (rowId) {
|
|
ids = [];
|
|
ids.push(rowId);
|
|
}
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'proficiency');
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'ability');
|
|
collectionArray.push(repeatingString + 'bonus');
|
|
}
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
var skillName = v[repeatingString + 'name'];
|
|
if (!exists(skillName)) {
|
|
return;
|
|
}
|
|
|
|
var ability = getAbilityModName(v[repeatingString + 'ability']);
|
|
console.log('ability', ability);
|
|
finalSetAttrs[repeatingString + 'ability_short_name'] = getAbilityShortName(ability, true);
|
|
|
|
var total = 0;
|
|
var totalFormula = '';
|
|
var proficiency = v[repeatingString + 'proficiency'];
|
|
if (!proficiency || proficiency === 'unproficient') {
|
|
if (v.jack_of_all_trades_toggle === '@{jack_of_all_trades}') {
|
|
var jackOfAllTrades = getIntValue(v.jack_of_all_trades);
|
|
total += jackOfAllTrades;
|
|
totalFormula += jackOfAllTrades + '[jack of all trades]';
|
|
} else {
|
|
totalFormula += 0 + '[unproficient]';
|
|
}
|
|
} else if (proficiency === 'proficient') {
|
|
var pb = getIntValue(v.pb);
|
|
total += pb;
|
|
totalFormula += pb + '[proficient]';
|
|
} else if (proficiency === 'expertise') {
|
|
var exp = getIntValue(v.exp);
|
|
total += exp;
|
|
totalFormula += exp + '[expertise]';
|
|
}
|
|
|
|
var skillAbility = getAbilityValue(v, ability, 'strength_mod');
|
|
if (exists(skillAbility)) {
|
|
total += skillAbility;
|
|
totalFormula += ADD + skillAbility + '[' + getAbilityShortName(ability) + ']';
|
|
}
|
|
|
|
var skillBonus = getIntValue(v[repeatingString + 'bonus']);
|
|
if (exists(skillBonus)) {
|
|
total += skillBonus;
|
|
totalFormula += ADD + skillBonus + '[bonus]';
|
|
}
|
|
|
|
var skillAbilityName = getAbilityName(ability);
|
|
if (exists(skillAbilityName)) {
|
|
var checkBonus = v[skillAbilityName + '_check_bonus'];
|
|
if (exists(checkBonus)) {
|
|
checkBonus = getIntValue(checkBonus);
|
|
total += checkBonus;
|
|
totalFormula += ADD + checkBonus + '[' + getAbilityShortName(ability) + ' check bonus]';
|
|
}
|
|
}
|
|
|
|
var globalCheckBonus = getIntValue(v.global_check_bonus);
|
|
if (exists(globalCheckBonus)) {
|
|
total += globalCheckBonus;
|
|
totalFormula += ADD + globalCheckBonus + '[global check bonus]';
|
|
}
|
|
|
|
finalSetAttrs[repeatingString + 'total'] = total;
|
|
finalSetAttrs[repeatingString + 'formula'] = totalFormula;
|
|
}
|
|
|
|
console.log('updateSkill', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
on('change:repeating_skill', function (eventInfo) {
|
|
var changedField = getRepeatingField('repeating_skill', eventInfo);
|
|
if (changedField !== 'ability_short_name' && changedField !== 'total' && changedField !== 'formula') {
|
|
console.log('change:repeating_skill changedField', changedField);
|
|
var rowId = getRowId('repeating_skill', eventInfo);
|
|
console.log('change:repeating_skill rowId', rowId);
|
|
updateSkill(rowId);
|
|
}
|
|
});
|
|
on('change:jack_of_all_trades_toggle change:jack_of_all_trades change:global_check_bonus change:strength_check_bonus change:dexterity_check_bonus change:constitution_check_bonus change:intelligence_check_bonus change:wisdom_check_bonus change:charisma_check_bonus', function () {
|
|
updateSkill();
|
|
});
|
|
|
|
function updateSkillsFromSRD () {
|
|
var repeatingItem = 'repeating_skill';
|
|
var collectionArray = ['skills_srd', 'level', 'challenge', 'strength_mod', 'dexterity_mod', 'constitution_mod', 'intelligence_mod', 'wisdom_mod', 'charisma_mod'];
|
|
var finalSetAttrs = {};
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'ability');
|
|
}
|
|
getAttrs(collectionArray, function (v) {
|
|
var skillsFromSRD = v.skills_srd;
|
|
var skillsObj = {};
|
|
var pb = getPB(v.level, v.challenge);
|
|
var expertise = pb * 2;
|
|
var skillName;
|
|
var repeatingString;
|
|
|
|
if (exists(skillsFromSRD)) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
skillName = v[repeatingString + 'name'];
|
|
skillsObj[skillName] = ids[j];
|
|
}
|
|
|
|
var re = /(\w+)\s?((?:\+|\-)\d+)/gi;
|
|
while (match = re.exec(skillsFromSRD)) {
|
|
if (match && match[1] && match[2]) {
|
|
skillName = match[1];
|
|
if (skillsObj[skillName]) {
|
|
var skillId = skillsObj[skillName];
|
|
repeatingString = repeatingItem + '_' + skillId + '_';
|
|
|
|
var skillAbility = v[repeatingString + 'ability'];
|
|
var abilityValue = getAbilityValue(v, skillAbility);
|
|
var skillBonus = getIntValue(match[2]) - abilityValue;
|
|
|
|
if (skillBonus >= expertise) {
|
|
finalSetAttrs[repeatingString + 'proficiency'] = 'expertise';
|
|
if (skillBonus > expertise) {
|
|
finalSetAttrs[repeatingString + 'bonus'] = skillBonus - expertise;
|
|
}
|
|
} else if (skillBonus >= pb) {
|
|
finalSetAttrs[repeatingString + 'proficiency'] = 'proficient';
|
|
if (skillBonus > pb) {
|
|
finalSetAttrs[repeatingString + 'bonus'] = skillBonus - pb;
|
|
}
|
|
}
|
|
} else {
|
|
console.log(skillName + ' does not exist in the list of skills');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('updateSkillsFromSRD', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
}
|
|
on('change:skills_srd', function () {
|
|
updateSkillsFromSRD();
|
|
});
|
|
|
|
function updateSavingThrowsFromSRD () {
|
|
var collectionArray = ['saving_throws_srd'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var savingThrowsFromSRD = v.saving_throws_srd;
|
|
var pbVar = '@{PB}';
|
|
|
|
if (savingThrowsFromSRD.indexOf('Str') !== -1) {
|
|
finalSetAttrs.strength_save_prof = pbVar;
|
|
}
|
|
if (savingThrowsFromSRD.indexOf('Dex') !== -1) {
|
|
finalSetAttrs.dexterity_save_prof = pbVar;
|
|
}
|
|
if (savingThrowsFromSRD.indexOf('Con') !== -1) {
|
|
finalSetAttrs.constitution_save_prof = pbVar;
|
|
}
|
|
if (savingThrowsFromSRD.indexOf('Int') !== -1) {
|
|
finalSetAttrs.intelligence_save_prof = pbVar;
|
|
}
|
|
if (savingThrowsFromSRD.indexOf('Wis') !== -1) {
|
|
finalSetAttrs.wisdom_save_prof = pbVar;
|
|
}
|
|
if (savingThrowsFromSRD.indexOf('Cha') !== -1) {
|
|
finalSetAttrs.charisma_save_prof = pbVar;
|
|
}
|
|
|
|
console.log('updateSavingThrowsFromSRD', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
}
|
|
on('change:saving_throws_srd', function () {
|
|
updateSavingThrowsFromSRD();
|
|
});
|
|
|
|
var sheetOpened = function () {
|
|
var collectionArray = ['version', 'strength', 'dexterity', 'constitution', 'intelligence', 'wisdom', 'charisma'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var version = v.version;
|
|
|
|
if (!version) {
|
|
updatePb();
|
|
|
|
var setAbilities = {};
|
|
if (!exists(v.strength)) {
|
|
setAbilities.strength = 10;
|
|
setAbilities.strength_mod = 0;
|
|
setAbilities.strength_mod_with_sign = '+0';
|
|
}
|
|
if (!exists(v.dexterity)) {
|
|
setAbilities.dexterity = 10;
|
|
setAbilities.dexterity_mod = 0;
|
|
setAbilities.dexterity_mod_with_sign = '+0';
|
|
}
|
|
if (!exists(v.constitution)) {
|
|
setAbilities.constitution = 10;
|
|
setAbilities.constitution_mod = 0;
|
|
setAbilities.constitution_mod_with_sign = '+0';
|
|
}
|
|
if (!exists(v.intelligence)) {
|
|
setAbilities.intelligence = 10;
|
|
setAbilities.intelligence_mod = 0;
|
|
setAbilities.intelligence_mod_with_sign = '+0';
|
|
}
|
|
if (!exists(v.wisdom)) {
|
|
setAbilities.wisdom = 10;
|
|
setAbilities.wisdom_mod = 0;
|
|
setAbilities.wisdom_mod_with_sign = '+0';
|
|
}
|
|
if (!exists(v.charisma)) {
|
|
setAbilities.charisma = 10;
|
|
setAbilities.charisma_mod = 0;
|
|
setAbilities.charisma_mod_with_sign = '+0';
|
|
}
|
|
setFinalAttrs(v, setAbilities);
|
|
|
|
var skills = [
|
|
{
|
|
'name': 'Acrobatics',
|
|
'ability': 'dexterity'
|
|
},
|
|
{
|
|
'name': 'Animal Handling',
|
|
'ability': 'wisdom'
|
|
},
|
|
{
|
|
'name': 'Arcana',
|
|
'ability': 'intelligence'
|
|
},
|
|
{
|
|
'name': 'Athletics',
|
|
'ability': 'strength'
|
|
},
|
|
{
|
|
'name': 'Deception',
|
|
'ability': 'charisma'
|
|
},
|
|
{
|
|
'name': 'History',
|
|
'ability': 'intelligence'
|
|
},
|
|
{
|
|
'name': 'Insight',
|
|
'ability': 'wisdom'
|
|
},
|
|
{
|
|
'name': 'Intimidation',
|
|
'ability': 'charisma'
|
|
},
|
|
{
|
|
'name': 'Investigation',
|
|
'ability': 'intelligence'
|
|
},
|
|
{
|
|
'name': 'Medicine',
|
|
'ability': 'wisdom'
|
|
},
|
|
{
|
|
'name': 'Nature',
|
|
'ability': 'intelligence'
|
|
},
|
|
{
|
|
'name': 'Perception',
|
|
'ability': 'wisdom'
|
|
},
|
|
{
|
|
'name': 'Performance',
|
|
'ability': 'charisma'
|
|
},
|
|
{
|
|
'name': 'Persuasion',
|
|
'ability': 'charisma'
|
|
},
|
|
{
|
|
'name': 'Religion',
|
|
'ability': 'intelligence'
|
|
},
|
|
{
|
|
'name': 'Sleight of Hand',
|
|
'ability': 'dexterity'
|
|
},
|
|
{
|
|
'name': 'Stealth',
|
|
'ability': 'dexterity'
|
|
},
|
|
{
|
|
'name': 'Survival',
|
|
'ability': 'wisdom'
|
|
}
|
|
];
|
|
|
|
for (var i = 0; i < skills.length; i++) {
|
|
var newRowId = generateRowID();
|
|
var repeatingString = 'repeating_skill_' + newRowId + '_';
|
|
finalSetAttrs[repeatingString + 'name'] = skills[i].name;
|
|
finalSetAttrs[repeatingString + 'ability'] = '@{' + skills[i].ability + '_mod}';
|
|
updateSkill(newRowId);
|
|
}
|
|
}
|
|
|
|
if (versionCompare(version, '2.0.10') < 0) {
|
|
updateAbilityModifier('strength');
|
|
updateAbilityModifier('dexterity');
|
|
updateAbilityModifier('constitution');
|
|
updateAbilityModifier('intelligence');
|
|
updateAbilityModifier('wisdom');
|
|
updateAbilityModifier('charisma');
|
|
}
|
|
|
|
if (!version || version !== currentVersion) {
|
|
finalSetAttrs.version = currentVersion;
|
|
}
|
|
|
|
console.log('sheetOpened', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
|
|
updateInitiative();
|
|
updateArmor();
|
|
});
|
|
};
|
|
|
|
on('sheet:opened', function () {
|
|
sheetOpened();
|
|
});
|
|
|
|
var updateAttachers = function () {
|
|
var repeatingItem = 'repeating_attacher';
|
|
var collectionArray = ['attacher_initiative', 'attacher_death_saving_throw', 'attacher_hit_dice', 'attacher_attack', 'attacher_spell', 'attacher_skill'];
|
|
var finalSetAttrs = {};
|
|
var itemsToPush = ['initiative', 'death_saving_throw', 'hit_dice', 'attack', 'spell', 'skill'];
|
|
|
|
for (var i = 0; i < ABILITIES.length; i++) {
|
|
collectionArray.push('attacher_' + ABILITIES[i] + '_check');
|
|
collectionArray.push('attacher_' + ABILITIES[i] + '_saving_throw');
|
|
itemsToPush.push(ABILITIES[i] + '_check');
|
|
itemsToPush.push(ABILITIES[i] + '_saving_throw');
|
|
}
|
|
|
|
getSectionIDs(repeatingItem, function (ids) {
|
|
for (var i = 0; i < ids.length; i++) {
|
|
var repeatingString = repeatingItem + '_' + ids[i] + '_';
|
|
collectionArray.push(repeatingString + 'name');
|
|
collectionArray.push(repeatingString + 'freetext');
|
|
collectionArray.push(repeatingString + 'freeform');
|
|
|
|
for (var x = 0; x < itemsToPush.length; x++) {
|
|
collectionArray.push(repeatingString + itemsToPush[x] + '_attacher');
|
|
finalSetAttrs['attacher_' + itemsToPush[x]] = ' ';
|
|
}
|
|
}
|
|
getAttrs(collectionArray, function (v) {
|
|
for (var j = 0; j < ids.length; j++) {
|
|
var repeatingString = repeatingItem + '_' + ids[j] + '_';
|
|
|
|
for (var x = 0; x < itemsToPush.length; x++) {
|
|
var attacher = v[repeatingString + itemsToPush[x] + '_attacher'];
|
|
if (exists(attacher) && attacher === 'on') {
|
|
|
|
var attacherName = v[repeatingString + 'name'] || '';
|
|
|
|
var freeText = v[repeatingString + 'freetext'];
|
|
if (exists(attacherName) && exists(freeText)) {
|
|
finalSetAttrs['attacher_' + itemsToPush[x]] += '{{' + attacherName + '=' + freeText + '}}' + ' ';
|
|
}
|
|
var freeForm = v[repeatingString + 'freeform'];
|
|
if (exists(freeForm)) {
|
|
finalSetAttrs['attacher_' + itemsToPush[x]] += freeForm + ' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('updateAttachers', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
});
|
|
};
|
|
|
|
on('change:repeating_attacher remove:repeating_attacher', function () {
|
|
updateAttachers();
|
|
});
|
|
|
|
var updateNPCSizeTypeAlignment = function () {
|
|
var collectionArray = ['size', 'type', 'alignment'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var creatureSize = v.size || 'Large';
|
|
finalSetAttrs.size_type_alignment = creatureSize;
|
|
|
|
var sizeToHdSize = {
|
|
Tiny: 4,
|
|
Small: 6,
|
|
Medium: 8,
|
|
Large: 10,
|
|
Huge: 12,
|
|
Gargantuan: 20
|
|
};
|
|
finalSetAttrs.hit_die = 'd' + sizeToHdSize[creatureSize];
|
|
|
|
if (v.type) {
|
|
finalSetAttrs.size_type_alignment += SPACE + v.type;
|
|
}
|
|
if (v.alignment) {
|
|
finalSetAttrs.size_type_alignment += ',' + SPACE + v.alignment;
|
|
}
|
|
|
|
console.log('updateNPCSizeTypeAlignment', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:size change:type change:alignment', function () {
|
|
updateNPCSizeTypeAlignment();
|
|
});
|
|
|
|
var updateNPCChallenge = function () {
|
|
var collectionArray = ['challenge', 'xp'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var challenge = v.challenge;
|
|
var xpPerChallenge = {
|
|
0: 0,
|
|
'1/8': 25,
|
|
'1/4': 50,
|
|
'1/2': 100,
|
|
1: 200,
|
|
2: 450,
|
|
3: 700,
|
|
4: 1100,
|
|
5: 1800,
|
|
6: 2300,
|
|
7: 2900,
|
|
8: 3900,
|
|
9: 5000,
|
|
10: 5900,
|
|
11: 7200,
|
|
12: 8400,
|
|
13: 10000,
|
|
14: 11500,
|
|
15: 13000,
|
|
16: 15000,
|
|
17: 18000,
|
|
18: 20000,
|
|
19: 22000,
|
|
20: 25000,
|
|
21: 33000,
|
|
22: 41000,
|
|
23: 50000,
|
|
24: 62000,
|
|
25: 75000,
|
|
26: 90000,
|
|
27: 105000,
|
|
28: 120000,
|
|
29: 135000,
|
|
30: 155000
|
|
};
|
|
|
|
finalSetAttrs.xp = xpPerChallenge[challenge];
|
|
|
|
console.log('updateNPCChallenge', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:challenge', function () {
|
|
updateNPCChallenge();
|
|
updatePb();
|
|
});
|
|
|
|
var updateNPCHPFromSRD = function () {
|
|
var collectionArray = ['hp_srd', 'constitution', 'constitution_mod', 'constitution_bonus', 'global_ability_bonus'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
if (exists(v.hp_srd)) {
|
|
var match = v.hp_srd.match(/\((\d+)d(\d+)(?:\s?(?:\+|\-)\s?(\d+))?\)/i);
|
|
if (!match || !match[1] || !match[2]) {
|
|
console.log('Character doesn\'t have valid HP/HD format');
|
|
} else {
|
|
var hdNum = getIntValue(match[1]);
|
|
|
|
var conMod = getIntValue(v.constitution_mod);
|
|
if (!conMod) {
|
|
var conScore = getIntValue(v.constitution);
|
|
var conBonus = getIntValue(v.constitution_bonus);
|
|
var globalAbilityBonus = getIntValue(v.global_ability_bonus);
|
|
console.log('conScore', conScore);
|
|
console.log('conBonus', conBonus);
|
|
console.log('globalAbilityBonus', globalAbilityBonus);
|
|
|
|
conMod = getAbilityMod((conScore + conBonus + globalAbilityBonus));
|
|
console.log('conMod 1', conMod);
|
|
}
|
|
console.log('conMod', conMod);
|
|
|
|
console.log('hdNum', hdNum);
|
|
finalSetAttrs.hit_dice = hdNum;
|
|
finalSetAttrs.hit_die = 'd' + getIntValue(match[2]);
|
|
|
|
var hpExpectedBonus = hdNum * conMod;
|
|
var hpBonus = getIntValue(match[3]);
|
|
if (hpBonus !== hpExpectedBonus) {
|
|
finalSetAttrs.hp_extra = hpBonus - hpExpectedBonus;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('updateNPCHPFromSRD', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
on('change:hp_srd', function () {
|
|
updateNPCHPFromSRD();
|
|
});
|
|
|
|
|
|
var updateNPCHP = function () {
|
|
var collectionArray = ['hit_dice', 'hit_die', 'hp_extra', 'constitution_mod'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var hdNum = getIntValue(v.hit_dice);
|
|
var hdSize = getIntValue(v.hit_die.replace('d', ''));
|
|
var hdAverage = (hdSize / 2) + 0.5;
|
|
var hpFormula = hdNum + 'd' + hdSize;
|
|
var conMod = getIntValue(v.constitution_mod);
|
|
var totalHP = Math.floor(hdNum * hdAverage);
|
|
var amount;
|
|
|
|
if (conMod !== 0) {
|
|
var bonusHP = hdNum * conMod;
|
|
if (conMod > 0) {
|
|
hpFormula += ADD + bonusHP;
|
|
totalHP += bonusHP;
|
|
} else if (conMod < 0) {
|
|
hpFormula += SUBTRACT + bonusHP;
|
|
totalHP -= bonusHP;
|
|
}
|
|
}
|
|
|
|
if (exists(v.hp_extra)) {
|
|
var regex = (/(?:(\+|\-)\s)?(\d+)(?:d(\d+))?/gi);
|
|
var splitFormula;
|
|
|
|
while (splitFormula = regex.exec(v.hp_extra)) {
|
|
if (!splitFormula || !splitFormula[2]) {
|
|
console.log('Character doesn\'t have valid hp formula');
|
|
} else {
|
|
amount = 0;
|
|
|
|
if (!splitFormula[3]) {
|
|
amount = getIntValue(splitFormula[2]);
|
|
} else {
|
|
var extraHdNum = getIntValue(splitFormula[2]);
|
|
var extraHdSize = getIntValue(splitFormula[3]);
|
|
var extraHdAverage = (extraHdSize / 2) + 0.5;
|
|
amount = Math.floor(extraHdNum * extraHdAverage);
|
|
}
|
|
|
|
if (!splitFormula[1] || splitFormula[1] === '+') {
|
|
totalHP += amount;
|
|
hpFormula += ADD + amount;
|
|
} else if (splitFormula[1] === '-') {
|
|
totalHP -= amount;
|
|
hpFormula += SUBTRACT + amount;
|
|
}
|
|
}
|
|
v.hp_extra.replace(splitFormula[0], '');
|
|
}
|
|
}
|
|
|
|
if (totalHP) {
|
|
finalSetAttrs.hp = totalHP;
|
|
finalSetAttrs.hp_max = totalHP;
|
|
finalSetAttrs.hp_formula = hpFormula;
|
|
}
|
|
|
|
console.log('updateNPCHP', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
on('change:hit_dice change:hit_die change:hp_extra change:constitution_mod', function () {
|
|
console.log('hd stuff changed');
|
|
updateNPCHP();
|
|
});
|
|
|
|
var updateNPCAC = function () {
|
|
var collectionArray = ['ac_srd', 'ac', 'ac_note'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
if (exists(v.ac_srd)) {
|
|
var match = v.ac_srd.match(/(\d+)\s?(.*)/);
|
|
if (!match || !match[1] || !match[2]) {
|
|
console.log('Character doesn\'t have valid AC format');
|
|
} else {
|
|
finalSetAttrs.ac = match[1];
|
|
finalSetAttrs.ac_note = match[2].replace(/\(|\)/g, '');
|
|
}
|
|
}
|
|
|
|
console.log('updateNPC', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:ac_srd', function () {
|
|
updateNPCAC();
|
|
});
|
|
|
|
var updateNPCName = function () {
|
|
var collectionArray = ['character_name_srd', 'character_name_srd'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
finalSetAttrs.character_name = v.character_name_srd;
|
|
|
|
console.log('updateNPCName', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:character_name_srd', function () {
|
|
console.log('character_name_srd changed');
|
|
});
|
|
|
|
var updateNPCContent = function () {
|
|
console.log('updateNPCContent');
|
|
var collectionArray = ['content_srd', 'character_name_srd'];
|
|
var finalSetAttrs = {};
|
|
|
|
getAttrs(collectionArray, function (v) {
|
|
var content = v.content_srd;
|
|
var legendaryActions;
|
|
var actions;
|
|
var traits;
|
|
var re = /\*\*(.*)\*\*:\s(.*)/gi;
|
|
var match;
|
|
var newRowId;
|
|
var repeatingString;
|
|
|
|
if (exists(content)) {
|
|
if (content.indexOf('Legendary Actions') !== -1) {
|
|
var legendaryActionsSplit = content.split(/Legendary Actions\n/);
|
|
legendaryActions = legendaryActionsSplit[1];
|
|
content = legendaryActionsSplit[0];
|
|
}
|
|
if (exists(legendaryActions)) {
|
|
console.log('legendaryActions', legendaryActions);
|
|
console.log('v.character_name_srd', v.character_name_srd);
|
|
var creatureName = v.character_name_srd;
|
|
var legendaryActionAmount = 3;
|
|
var legendaryActionsMatch = legendaryActions.match(/Can take (\d+) Legendary Actions/gi);
|
|
|
|
if (legendaryActionsMatch && legendaryActionsMatch[1]) {
|
|
console.log('legendaryActionsMatch[1]', legendaryActionsMatch[1]);
|
|
legendaryActionAmount = legendaryActionAmount[1];
|
|
}
|
|
console.log('legendaryActionAmount', legendaryActionAmount);
|
|
|
|
finalSetAttrs.legendary_actions_blurb = 'The ' + creatureName + ' can take ' + legendaryActionAmount + ' legendary actions, choosing from the options below. Only one legendary option can be used at a time and only at the end of another creature\'s turn. The ' + creatureName + ' regains spent legendary actions at the start of its turn.';
|
|
while (match = re.exec(legendaryActions)) {
|
|
console.log('match', match);
|
|
if (match && match[1] && match[2]) {
|
|
newRowId = generateRowID();
|
|
repeatingString = 'repeating_legendary_action_' + newRowId + '_';
|
|
finalSetAttrs[repeatingString + 'name'] = match[1];
|
|
finalSetAttrs[repeatingString + 'freetext'] = match[2];
|
|
} else {
|
|
console.log('Character doesn\'t have a valid legendary action format');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (content.indexOf('Actions') !== -1) {
|
|
var actionsSplit = content.split(/Actions\n/);
|
|
actions = actionsSplit[1];
|
|
content = actionsSplit[0];
|
|
}
|
|
if (exists(actions)) {
|
|
while (match = re.exec(actions)) {
|
|
if (match && match[1] && match[2]) {
|
|
newRowId = generateRowID();
|
|
repeatingString = 'repeating_action_' + newRowId + '_';
|
|
finalSetAttrs[repeatingString + 'name'] = match[1];
|
|
finalSetAttrs[repeatingString + 'freetext'] = match[2];
|
|
} else {
|
|
console.log('Character doesn\'t have a valid action format');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (content.indexOf('Traits') !== -1) {
|
|
var traitsSplit = content.split(/Traits\n/);
|
|
traits = traitsSplit[1];
|
|
content = traitsSplit[0];
|
|
}
|
|
if (exists(traits)) {
|
|
while (match = re.exec(traits)) {
|
|
if (match && match[1] && match[2]) {
|
|
newRowId = generateRowID();
|
|
repeatingString = 'repeating_trait_' + newRowId + '_';
|
|
finalSetAttrs[repeatingString + 'name'] = match[1];
|
|
finalSetAttrs[repeatingString + 'freetext'] = match[2];
|
|
} else {
|
|
console.log('Character doesn\'t have a valid trait format');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('updateNPCContent', finalSetAttrs);
|
|
setFinalAttrs(v, finalSetAttrs);
|
|
});
|
|
};
|
|
|
|
on('change:content_srd', function () {
|
|
console.log('content_srd changed');
|
|
updateNPCContent();
|
|
});
|