mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 11:42:29 +00:00
1285 lines
45 KiB
JavaScript
1285 lines
45 KiB
JavaScript
import _ from 'underscore';
|
|
import {PFLog, PFConsole} from './PFLog';
|
|
import TAS from './TheAaronSheet.js';
|
|
import * as SWUtils from './SWUtils';
|
|
import PFConst from './PFConst';
|
|
import * as PFUtils from './PFUtils';
|
|
import * as PFUtilsAsync from './PFUtilsAsync';
|
|
import * as PFMacros from './PFMacros';
|
|
import * as PFMenus from './PFMenus';
|
|
import * as PFDB from './PFDB';
|
|
import * as PFAttackOptions from './PFAttackOptions';
|
|
import * as PFAttackGrid from './PFAttackGrid';
|
|
import * as PFFeatures from './PFFeatures';
|
|
import * as PFAttacks from './PFAttacks';
|
|
import * as PFAbility from './PFAbility';
|
|
|
|
let optionFields = ['is_sp', 'hasposrange', 'hasuses', 'hasattack', 'abil-attacktypestr'],
|
|
optionRepeatingHelperFields = ['ability_type', 'range_numeric', 'frequency', 'abil-attack-type'],
|
|
allOptionRepeatingFields = optionFields.concat(optionRepeatingHelperFields),
|
|
tabRuleSorted = {
|
|
'class-features': 0,
|
|
feats: 1,
|
|
'monster-rule': 8,
|
|
'mythic-abilities': 3,
|
|
'mythic-feats': 1,
|
|
other: 8,
|
|
'racial-traits': 2,
|
|
'special-abilities': 5,
|
|
'special-attacks': 4,
|
|
'special-qualities': 7,
|
|
'spell-like-abilities': 6,
|
|
traits: 2,
|
|
},
|
|
tabTypeSorted = {
|
|
Ex: 9,
|
|
Sp: 10,
|
|
Su: 11,
|
|
},
|
|
categoryAttrs = ['tabcat-1', 'tabcat0', 'tabcat1', 'tabcat2', 'tabcat3', 'tabcat4', 'tabcat5', 'tabcat6', 'tabcat7', 'tabcat8', 'tabcat9', 'tabcat10', 'tabcat11'],
|
|
otherCommandMacros = {
|
|
ex: ' [^{extraordinary-abilities-menu}](~@{character_id}|NPCPREFIXex_button)',
|
|
sp: ' [^{spell-like-abilities-menu}](~@{character_id}|NPCPREFIXsp_button)',
|
|
su: ' [^{supernatural-abilities-menu}](~@{character_id}|NPCPREFIXsu_button)',
|
|
},
|
|
events = {
|
|
attackEventsSLA: ['damage-macro-text', 'damage-type', 'abil-sr', 'save', 'abil-attack-type', 'name', 'range_numeric', 'toggle_attack_entry'],
|
|
commandMacroFields: ['name', 'used', 'used_max', 'showinmenu', 'ability_type', 'frequency', 'rule_category', 'toggle_attack_entry'],
|
|
};
|
|
|
|
function setClassName(id, callback, eventInfo) {
|
|
let done = _.once(function () {
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
idStr = SWUtils.getRepeatingIDStr(id),
|
|
prefix = 'repeating_ability_' + idStr,
|
|
clbasisField = prefix + 'CL-basis';
|
|
getAttrs(
|
|
[prefix + 'CL-basis', prefix + 'class-name', 'character_name', 'race', 'class-0-name', 'class-1-name', 'class-2-name', 'class-3-name', 'class-4-name', 'class-5-name'],
|
|
function (v) {
|
|
let clBase = '',
|
|
setter = {},
|
|
match;
|
|
try {
|
|
if (v[clbasisField]) {
|
|
if (v[clbasisField] === '@{level}') {
|
|
clBase = v['race'];
|
|
} else if (v[clbasisField] === '@{npc-hd-num}') {
|
|
clBase = v['race'];
|
|
// added "Other" value="(0*(@{level}))" on Cl-Basis selector since "N/A" option uses value="0"
|
|
} else if (v[clbasisField] === '(0*(@{level}))') {
|
|
// if Class-name has been set by the user, prevent it from being overwritten when recalculating
|
|
if (v[prefix + 'class-name'] != 'undefined') {
|
|
clBase = v[prefix + 'class-name'];
|
|
setter[prefix + 'CL-basis-mod'] = 0;
|
|
} else {
|
|
clBase = '';
|
|
}
|
|
} else if (parseInt(v[clbasisField], 10) === 0) {
|
|
clBase = '';
|
|
} else {
|
|
match = v[prefix + 'CL-basis'].match(/\d+/);
|
|
if (match) {
|
|
clBase = v['class-' + match[0] + '-name'];
|
|
}
|
|
}
|
|
if (v[prefix + 'class-name'] !== clBase) {
|
|
setter[prefix + 'class-name'] = clBase;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
TAS.error('PFAbility.setClassName', err);
|
|
} finally {
|
|
if (_.size(setter) > 0) {
|
|
SWUtils.setWrapper(setter, PFConst.silentParams, done);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
function setTypeTab(callback, silently, id, eventInfo) {
|
|
let prefix = 'repeating_ability_' + SWUtils.getRepeatingIDStr(id);
|
|
getAttrs(
|
|
[prefix + 'frequency', prefix + 'rule_category', prefix + 'CL-basis', prefix + 'ability_type', prefix + 'tabcat', prefix + 'tabcat2', 'abilities_tab', 'npc-abilities_tab'],
|
|
function (v) {
|
|
let setter = {},
|
|
params = PFConst.silentParams;
|
|
//TAS.debug("############ PFAbility setTypeTab",v);
|
|
setter[prefix + 'tabcat2'] = v[prefix + 'ability_type'] || '-1';
|
|
//TAS.notice("############","Ability setting ",setter);
|
|
SWUtils.setWrapper(setter, params);
|
|
},
|
|
);
|
|
}
|
|
function setRuleTab(callback, silently, id, eventInfo) {
|
|
let prefix = 'repeating_ability_' + SWUtils.getRepeatingIDStr(id);
|
|
getAttrs(
|
|
[prefix + 'frequency', prefix + 'rule_category', prefix + 'CL-basis', prefix + 'ability_type', prefix + 'tabcat', prefix + 'tabcat2', 'abilities_tab', 'npc-abilities_tab'],
|
|
function (v) {
|
|
let setter = {},
|
|
ruleForTab = '',
|
|
params = PFConst.silentParams;
|
|
switch (v[prefix + 'rule_category']) {
|
|
case 'racial-traits':
|
|
ruleForTab = 'traits';
|
|
break;
|
|
case 'monster-rule':
|
|
ruleForTab = 'other';
|
|
break;
|
|
case 'mythic-abilities':
|
|
ruleForTab = 'mythic';
|
|
break;
|
|
case 'mythic-feats':
|
|
ruleForTab = 'feats';
|
|
break;
|
|
default:
|
|
ruleForTab = v[prefix + 'rule_category'] || '';
|
|
break;
|
|
}
|
|
if (!ruleForTab) {
|
|
ruleForTab = '-1';
|
|
}
|
|
setter[prefix + 'tabcat'] = ruleForTab;
|
|
|
|
//if users changed the rule then change the tab we're checked on
|
|
if (eventInfo) {
|
|
if (ruleForTab !== '-1') {
|
|
if (v.abilities_tab !== ruleForTab && !/Ex|Sp|Su|99/i.test(v.abilities_tab)) {
|
|
setter.abilities_tab = ruleForTab;
|
|
}
|
|
if (v['npc-abilities_tab'] !== ruleForTab && !/Ex|Sp|Su|99/i.test(v['npc-abilities_tab'])) {
|
|
setter['npc-abilities_tab'] = ruleForTab;
|
|
}
|
|
}
|
|
if (v[prefix + 'rule_category'] === 'class-features' && (!v[prefix + 'CL-basis'] || v[prefix + 'CL-basis'] === '0')) {
|
|
setter[prefix + 'CL-basis'] = '@{class-0-level}';
|
|
params = {};
|
|
} else if (v[prefix + 'rule_category'] === 'racial-traits' && (!v[prefix + 'CL-basis'] || v[prefix + 'CL-basis'] === '0')) {
|
|
setter[prefix + 'CL-basis'] = '@{level}';
|
|
params = {};
|
|
}
|
|
}
|
|
if (v[prefix + 'rule_category'] === 'spell-like-abilities') {
|
|
v[prefix + 'tabcat2'] = 'Sp';
|
|
setter[prefix + 'ability_type'] = 'Sp';
|
|
}
|
|
if (!v[prefix + 'frequency']) {
|
|
setter[prefix + 'frequency'] = 'not-applicable';
|
|
}
|
|
if (_.size(setter)) {
|
|
//TAS.notice("############","Ability setting ",setter);
|
|
SWUtils.setWrapper(setter, params, setClassName);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
export function setRuleTabs() {
|
|
getSectionIDs('repeating_ability', function (ids) {
|
|
_.each(ids, function (id) {
|
|
setRuleTab(null, null, id);
|
|
setTypeTab(null, null, id);
|
|
});
|
|
});
|
|
}
|
|
|
|
/** returns all rule_category and ability_type used
|
|
* @returns {'rules':[values of rule_category], 'types':[valuesof ability_type]} object of rules
|
|
*/
|
|
function getAbilityTypes(callback) {
|
|
let done = function (typeObj) {
|
|
//TAS.debug('Ability.getAbilityTypes returning with ',typeObj);
|
|
if (typeof callback === 'function') {
|
|
callback(typeObj);
|
|
}
|
|
};
|
|
getSectionIDs('repeating_ability', function (ids) {
|
|
let fields = [];
|
|
if (!ids || _.size(ids) === 0) {
|
|
done({rules: [], types: []});
|
|
return;
|
|
}
|
|
_.each(ids, function (id) {
|
|
let prefix = 'repeating_ability_' + id + '_';
|
|
fields.push(prefix + 'rule_category');
|
|
fields.push(prefix + 'showinmenu');
|
|
fields.push(prefix + 'ability_type');
|
|
});
|
|
getAttrs(fields, function (v) {
|
|
let basearray = [],
|
|
rulearray = [],
|
|
typearray = [];
|
|
basearray = _.chain(ids)
|
|
.map(function (id) {
|
|
let retObj = {},
|
|
prefix = 'repeating_ability_' + id + '_';
|
|
retObj.id = id;
|
|
retObj.showinmenu = parseInt(v[prefix + 'showinmenu'], 10) || 0;
|
|
retObj.rule_category = v[prefix + 'rule_category'] || '';
|
|
retObj.ability_type = (v[prefix + 'ability_type'] || '').toLowerCase();
|
|
//TAS.debug("row "+id+" is ",retObj);
|
|
return retObj;
|
|
})
|
|
.filter(function (o) {
|
|
return o.showinmenu;
|
|
})
|
|
.value();
|
|
|
|
if (basearray) {
|
|
rulearray = _.chain(basearray).groupBy('rule_category').keys().compact().value();
|
|
typearray = _.chain(basearray).groupBy('ability_type').keys().compact().value();
|
|
}
|
|
if (!rulearray) {
|
|
rulearray = [];
|
|
}
|
|
if (!typearray) {
|
|
typearray = [];
|
|
}
|
|
done({rules: rulearray, types: typearray});
|
|
});
|
|
});
|
|
}
|
|
function getNewAbilityAttrs(ability) {
|
|
let setter = {},
|
|
id = '',
|
|
prefix = '',
|
|
matches;
|
|
try {
|
|
id = generateRowID();
|
|
prefix = 'repeating_ability_' + id + '_';
|
|
setter[prefix + 'row_id'] = id;
|
|
setter[prefix + 'showinmenu'] = ability['showinmenu'] || 0;
|
|
setter[prefix + 'name'] = ability.name || '';
|
|
setter[prefix + 'used'] = ability['used'] || 0;
|
|
setter[prefix + 'used_max'] = ability['used_max'] || 0;
|
|
setter[prefix + 'max-calculation'] = ability['max-calculation'] || '';
|
|
setter[prefix + 'short-description'] = ability['short-description'] || '';
|
|
setter[prefix + 'description'] = ability['description'] || '';
|
|
setter[prefix + 'rule_category'] = ability['rule_category'] || '';
|
|
setter[prefix + 'CL-basis'] = ability['CL-basis'] || '0';
|
|
setter[prefix + 'class-name'] = ability['class-name'] || '';
|
|
if (ability.rule_category === 'spell-like-abilities') {
|
|
setter[prefix + 'ability_type'] = 'Sp';
|
|
if (ability['spell_level-misc']) {
|
|
setter[prefix + 'spell_level-misc'] = ability['spell_level-misc'];
|
|
setter[prefix + 'spell_level-misc-mod'] = ability['spell_level-misc-mod'];
|
|
setter[prefix + 'spell_level-basis'] = ability['spell_level-basis'];
|
|
}
|
|
if (ability['range_numeric']) {
|
|
setter[prefix + 'range'] = ability['range'];
|
|
setter[prefix + 'range_numeric'] = ability['range_numeric'];
|
|
setter[prefix + 'range_pick'] = ability['range_pick'];
|
|
}
|
|
if (ability['abil-sr']) {
|
|
setter[prefix + 'abil-sr'] = ability['abil-sr'];
|
|
}
|
|
if (ability['save']) {
|
|
setter[prefix + 'save'] = ability['save'];
|
|
}
|
|
} else {
|
|
matches = ability.name.match(/\b(Sp|Su|Ex)\b/i);
|
|
if (matches && matches[1]) {
|
|
setter[prefix + 'ability_type'] = matches[0][0].toUpperCase() + matches[0][1].toLowerCase();
|
|
} else {
|
|
setter[prefix + 'ability_type'] = '';
|
|
}
|
|
}
|
|
setter[prefix + 'macro-text'] = ability['macro-text'] || '';
|
|
} catch (err) {
|
|
TAS.error('PFAbility.getNewAbilityAttrs', err, ability);
|
|
} finally {
|
|
return setter;
|
|
}
|
|
}
|
|
export function copyToAbilities(callback, abilities) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving PFAbility.copyToAbilities");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
setter = {};
|
|
//TAS.debug("At PFAbility.copyToAbilities");
|
|
if (_.size(abilities)) {
|
|
_.each(abilities, function (ability) {
|
|
let xtra = getNewAbilityAttrs(ability);
|
|
//TAS.debug("PFAbility.copyToAbilities adding ",xtra);
|
|
_.extend(setter, xtra);
|
|
});
|
|
//TAS.debug("##########################","PFAbility.copyToAbilities setting",setter);
|
|
}
|
|
if (_.size(setter)) {
|
|
SWUtils.setWrapper(setter, PFConst.silentParams, done);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
|
|
/** resetTopCommandMacro sets all-abilities_buttons_macro (menu of ability menus) */
|
|
function getTopOfMenu(callback, isNPC) {
|
|
let done = function (str) {
|
|
//TAS.debug("leaving PFAbility.getTopOfMenu");
|
|
if (typeof callback === 'function') {
|
|
callback(str);
|
|
}
|
|
},
|
|
newMacro = '',
|
|
setter = {};
|
|
try {
|
|
newMacro = ' @{orig_ability_header_macro}';
|
|
getAbilityTypes(function (used) {
|
|
let addlMacros = '',
|
|
prefix = '';
|
|
try {
|
|
if (isNPC) {
|
|
prefix = 'NPC-';
|
|
}
|
|
if (used.types) {
|
|
_.each(used.types, function (type) {
|
|
if (otherCommandMacros[type]) {
|
|
addlMacros += otherCommandMacros[type].replace('NPCPREFIX', prefix);
|
|
} else if (type) {
|
|
TAS.warn('could not find top macro for ' + type);
|
|
}
|
|
});
|
|
}
|
|
if (addlMacros) {
|
|
newMacro += ' {{row03=^{ability-menus}}} {{row04=' + addlMacros + '}}';
|
|
}
|
|
//TAS.debug("PFAbility.getTopOfMenu: done building top macro it is :",newMacro);
|
|
} catch (innererr) {
|
|
TAS.error('PFAbility.getTopOfMenu innererr', innererr);
|
|
} finally {
|
|
done(newMacro);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
TAS.error('PFAbility.getTopOfMenu', err);
|
|
done(newMacro);
|
|
}
|
|
}
|
|
export function resetCommandMacro(callback) {
|
|
let done = _.once(function () {
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
doneOne = _.after(8, done);
|
|
|
|
getTopOfMenu(function (header) {
|
|
PFMenus.resetOneCommandMacro('ability', true, doneOne, header);
|
|
}, true);
|
|
getTopOfMenu(function (header) {
|
|
PFMenus.resetOneCommandMacro('ability', false, doneOne, header);
|
|
}, false);
|
|
PFMenus.resetOneCommandMacro('ex', true, doneOne);
|
|
PFMenus.resetOneCommandMacro('sp', true, doneOne);
|
|
PFMenus.resetOneCommandMacro('su', true, doneOne);
|
|
PFMenus.resetOneCommandMacro('ex', false, doneOne);
|
|
PFMenus.resetOneCommandMacro('sp', false, doneOne);
|
|
PFMenus.resetOneCommandMacro('su', false, doneOne);
|
|
}
|
|
export function importFromCompendium(callback, eventInfo) {
|
|
let done = _.once(function () {
|
|
resetCommandMacro();
|
|
//TAS.debug("leaving PFAbility.importFromCompendium");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
id = SWUtils.getRowId(eventInfo.sourceAttribute), //row doesn't really exist yet so get id from event
|
|
prefix = 'repeating_ability_' + id + '_';
|
|
//TAS.debug"at PFAbility.importFromCompendium for "+ prefix);
|
|
getAttrs(
|
|
[
|
|
'is_undead',
|
|
prefix + 'name',
|
|
prefix + 'compendium_category',
|
|
prefix + 'rule_category',
|
|
prefix + 'ability_type_compendium',
|
|
prefix + 'ability_type',
|
|
prefix + 'description',
|
|
prefix + 'range_from_compendium',
|
|
prefix + 'target_from_compendium',
|
|
prefix + 'area_from_compendium',
|
|
prefix + 'effect_from_compendium',
|
|
],
|
|
function (v) {
|
|
let compcat = '',
|
|
abilitytype = '',
|
|
ability_basis = '',
|
|
location = '',
|
|
setter = {},
|
|
newcat = '',
|
|
abilname = '',
|
|
silentSetter = {},
|
|
match,
|
|
note = '',
|
|
areaEffectText = '',
|
|
newRangeSettings;
|
|
try {
|
|
//TAS.debug("PFAbility.importFromCompendium got values: ",v);
|
|
if (v[prefix + 'ability_type_compendium']) {
|
|
abilitytype = v[prefix + 'ability_type_compendium'];
|
|
setter[prefix + 'ability_type'] = abilitytype;
|
|
silentSetter[prefix + 'ability_type_compendium'] = '';
|
|
}
|
|
compcat = v[prefix + 'compendium_category'];
|
|
silentSetter[prefix + 'compendium_category'] = '';
|
|
if (compcat) {
|
|
compcat = compcat.toLowerCase();
|
|
if (compcat === 'feats') {
|
|
newcat = 'feats';
|
|
} else if (compcat === 'monster rule') {
|
|
newcat = 'monster-rule';
|
|
} else if (compcat === 'spell') {
|
|
newcat = 'spell-like-abilities';
|
|
}
|
|
if (newcat === 'monster-rule') {
|
|
if (v[prefix + 'description']) {
|
|
match = v[prefix + 'description'].match(/Location\:\s*(.*)$/i);
|
|
//TAS.debug"matching "+match);
|
|
if (match && match[1]) {
|
|
location = SWUtils.trimBoth(match[1].toLowerCase());
|
|
match = location.match(/special qual|sq|special att|special abil|defens|spell/i);
|
|
if (match) {
|
|
switch (match[0]) {
|
|
case 'special qual':
|
|
case 'sq':
|
|
newcat = 'special-qualities';
|
|
break;
|
|
case 'special att':
|
|
newcat = 'special-attacks';
|
|
break;
|
|
case 'special abil':
|
|
newcat = 'special-abilities';
|
|
break;
|
|
case 'defens':
|
|
newcat = 'defensive-abilities';
|
|
break;
|
|
case 'spell':
|
|
newcat = 'spell-like-abilities';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (abilitytype === 'Sp' && !newcat) {
|
|
newcat = 'spell-like-abilities';
|
|
}
|
|
if (!abilitytype && newcat === 'spell-like-abilities') {
|
|
abilitytype = 'Sp';
|
|
setter[prefix + 'ability_type'] = 'Sp';
|
|
} else if (abilitytype === 'Sp' && !newcat) {
|
|
newcat = 'spell-like-abilities';
|
|
}
|
|
|
|
if (newcat) {
|
|
setter[prefix + 'rule_category'] = newcat;
|
|
} else {
|
|
note += compcat;
|
|
}
|
|
if (abilitytype === 'Sp') {
|
|
areaEffectText = v[prefix + 'target_from_compendium'] || v[prefix + 'area_from_compendium'] || v[prefix + 'effect_from_compendium'] || '';
|
|
setter[prefix + 'targets'] = areaEffectText;
|
|
if (v[prefix + 'range_from_compendium']) {
|
|
newRangeSettings = PFUtils.parseSpellRangeText(v[prefix + 'range_from_compendium'], areaEffectText);
|
|
setter[prefix + 'range_pick'] = newRangeSettings.dropdown;
|
|
setter[prefix + 'range'] = newRangeSettings.rangetext;
|
|
}
|
|
setter[prefix + 'ability-basis'] = '@{CHA-mod}';
|
|
} else if (v[prefix + 'name']) {
|
|
abilname = v[prefix + 'name'].toLowerCase();
|
|
abilname = abilname.match(/^[^(]+/);
|
|
if (PFDB.default.specialAttackDCAbilityBase[abilname]) {
|
|
ability_basis = PFDB.default.specialAttackDCAbilityBase[abilname];
|
|
} else {
|
|
ability_basis = 'CON';
|
|
}
|
|
if (ability_basis === 'CON' && parseInt(v.is_undead, 10)) {
|
|
ability_basis = 'CHA';
|
|
}
|
|
ability_basis = '@{' + ability_basis + '}';
|
|
setter[prefix + 'ability-basis'] = ability_basis;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
TAS.error('PFAbility.importFromCompendium', err);
|
|
} finally {
|
|
if (_.size(silentSetter) > 0) {
|
|
SWUtils.setWrapper(silentSetter, PFConst.silentParams);
|
|
}
|
|
//TAS.debug"PFAbility.importFromCompendium, setting",setter);
|
|
if (_.size(setter) > 0) {
|
|
SWUtils.setWrapper(setter, {}, done);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
export function setAttackEntryVals(spellPrefix, weaponPrefix, v, setter, noName) {
|
|
let notes = '',
|
|
attackType = '';
|
|
setter = setter || {};
|
|
try {
|
|
attackType = PFUtils.findAbilityInString(v[spellPrefix + 'abil-attack-type']);
|
|
if (v[spellPrefix + 'name']) {
|
|
if (!noName) {
|
|
setter[weaponPrefix + 'name'] = v[spellPrefix + 'name'];
|
|
}
|
|
setter[weaponPrefix + 'source-ability-name'] = v[spellPrefix + 'name'];
|
|
}
|
|
if (attackType) {
|
|
setter[weaponPrefix + 'attack-type'] = v[spellPrefix + 'abil-attack-type'];
|
|
if (/CMB/i.test(attackType)) {
|
|
setter[weaponPrefix + 'vs'] = 'cmd';
|
|
} else if (/ranged/i.test(attackType)) {
|
|
setter[weaponPrefix + 'vs'] = 'touch';
|
|
setter[weaponPrefix + 'isranged'] = 1;
|
|
setter[weaponPrefix + 'range'] = v[spellPrefix + 'range_numeric'] || 0;
|
|
} else {
|
|
setter[weaponPrefix + 'vs'] = 'touch';
|
|
setter[weaponPrefix + 'range'] = 0;
|
|
}
|
|
}
|
|
if (v[spellPrefix + 'damage-macro-text']) {
|
|
setter[weaponPrefix + 'precision_dmg_macro'] = v[spellPrefix + 'damage-macro-text'];
|
|
if (attackType) {
|
|
setter[weaponPrefix + 'critical_dmg_macro'] = v[spellPrefix + 'damage-macro-text'];
|
|
} else {
|
|
setter[weaponPrefix + 'critical_dmg_macro'] = '';
|
|
}
|
|
}
|
|
if (v[spellPrefix + 'damage-type']) {
|
|
setter[weaponPrefix + 'precision_dmg_type'] = v[spellPrefix + 'damage-type'];
|
|
if (attackType) {
|
|
setter[weaponPrefix + 'critical_dmg_type'] = v[spellPrefix + 'damage-type'];
|
|
} else {
|
|
setter[weaponPrefix + 'critical_dmg_type'] = '';
|
|
}
|
|
}
|
|
if (v[spellPrefix + 'save']) {
|
|
if (notes) {
|
|
notes += ', ';
|
|
}
|
|
notes += '\n**Save:** ' + v[spellPrefix + 'save'] + ' **DC:** [[@{' + spellPrefix + 'savedc}]]';
|
|
}
|
|
if (v[spellPrefix + 'abil-sr']) {
|
|
if (notes) {
|
|
notes += ', ';
|
|
}
|
|
notes += '\n**Spell Resistance:** ' + v[spellPrefix + 'abil-sr'];
|
|
}
|
|
// include a link in the weapon notes to execute the spell from chat
|
|
let toggle_attack_entry = v[spellPrefix + 'toggle_attack_entry'];
|
|
if (toggle_attack_entry === 1) {
|
|
if (v[spellPrefix + 'name']) {
|
|
if (notes) {
|
|
notes += ', ';
|
|
}
|
|
notes += `\n[${v[spellPrefix + 'name']}](\~@\{character_name\}|${spellPrefix}roll)`;
|
|
}
|
|
}
|
|
if (toggle_attack_entry !== 1) {
|
|
if (v[spellPrefix + 'name']) {
|
|
if (notes) {
|
|
notes += '';
|
|
}
|
|
notes += '';
|
|
}
|
|
}
|
|
if (notes) {
|
|
setter[weaponPrefix + 'notes'] = notes;
|
|
}
|
|
} catch (err) {
|
|
TAS.error('PFAbility.setAttackEntryVals', err);
|
|
} finally {
|
|
return setter;
|
|
}
|
|
}
|
|
/**Triggered from a button in repeating spells
|
|
*@param {string} id the row id or null
|
|
*@param {string} weaponId if updating existing row
|
|
*/
|
|
export function createAttackEntryFromRow(id, callback, silently, eventInfo, weaponId) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving PFAbility.createAttackEntryFromRow");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
attribList = [],
|
|
itemId,
|
|
item_entry,
|
|
slaPrefix,
|
|
attributes = ['range_numeric', 'damage-macro-text', 'damage-type', 'abil-sr', 'savedc', 'save', 'abil-attack-type', 'name', 'toggle_attack_entry'];
|
|
if (id === 'DELETED') {
|
|
done();
|
|
return;
|
|
}
|
|
itemId = id || (eventInfo ? SWUtils.getRowId(eventInfo.sourceAttribute) : '');
|
|
item_entry = 'repeating_ability_' + SWUtils.getRepeatingIDStr(itemId);
|
|
slaPrefix = item_entry;
|
|
if (!itemId) {
|
|
TAS.warn('Cannot create usable attack entry from SLA since we cannot identify the row id');
|
|
done();
|
|
return;
|
|
}
|
|
attributes.forEach(function (attr) {
|
|
attribList.push(slaPrefix + attr);
|
|
});
|
|
|
|
//TAS.debug("PFAbility.createAttackEntryFromRow: attribList=" + attribList);
|
|
getAttrs(attribList, function (v) {
|
|
let newRowId = '',
|
|
setter = {},
|
|
prefix = 'repeating_weapon_',
|
|
idStr = '',
|
|
abilityexists = true,
|
|
deletedability = false,
|
|
params = {};
|
|
try {
|
|
if (_.size(v) === 0) {
|
|
abilityexists = false;
|
|
}
|
|
if (abilityexists) {
|
|
//TAS.debug("at PFAbility.createAttackEntryFromRow",v);
|
|
if (!PFUtils.findAbilityInString(v[slaPrefix + 'abil-attack-type']) && !v[slaPrefix + 'damage-macro-text']) {
|
|
TAS.warn('no attack to create for ability ' + v[slaPrefix + 'name'] + ', ' + itemId);
|
|
} else {
|
|
if (!weaponId) {
|
|
newRowId = generateRowID();
|
|
} else {
|
|
newRowId = weaponId;
|
|
}
|
|
idStr = newRowId + '_';
|
|
prefix += idStr;
|
|
setter = setAttackEntryVals(slaPrefix, prefix, v, setter, weaponId);
|
|
setter[prefix + 'source-ability'] = itemId;
|
|
setter[prefix + 'group'] = 'Special';
|
|
setter[prefix + 'link_type'] = PFAttacks.linkedAttackType.ability;
|
|
}
|
|
} else if (weaponId) {
|
|
setter['repeating_weapon_' + weaponId + '_source-ability'] = 'DELETED';
|
|
deletedability = true;
|
|
}
|
|
} catch (err) {
|
|
TAS.error('PFAbility.createAttackEntryFromRow', err);
|
|
} finally {
|
|
if (deletedability) {
|
|
SWUtils.setWrapper(setter, params, done);
|
|
} else if (_.size(setter) > 0) {
|
|
setter[slaPrefix + 'create-attack-entry'] = 0;
|
|
if (silently) {
|
|
params = PFConst.silentParams;
|
|
}
|
|
//TAS.debug("PFAbility.createAttackEntryFromRow setting:",setter);
|
|
SWUtils.setWrapper(setter, params, function () {
|
|
//can do these in parallel
|
|
//TAS.debug("PFAbility.createAttackEntryFromRow came back from setter ");
|
|
PFAttackOptions.resetOption(newRowId);
|
|
PFAttackGrid.resetCommandMacro();
|
|
done();
|
|
});
|
|
} else {
|
|
setter[slaPrefix + 'create-attack-entry'] = 0;
|
|
SWUtils.setWrapper(setter, PFConst.silentParams, done);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
export function updateAssociatedAttack(id, callback, silently, eventInfo) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving PFAbility.updateAssociatedAttack");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
itemId = '',
|
|
item_entry = '',
|
|
attrib = '',
|
|
attributes = [];
|
|
itemId = id || (eventInfo ? SWUtils.getRowId(eventInfo.sourceAttribute) : '');
|
|
item_entry = 'repeating_ability_' + SWUtils.getRepeatingIDStr(itemId);
|
|
attrib = eventInfo ? SWUtils.getAttributeName(eventInfo.sourceAttribute) : '';
|
|
attributes = [];
|
|
//TAS.debug("at PF Spell like abilities updateAssociatedAttack: for row" + id );
|
|
if (attrib) {
|
|
attributes = [item_entry + attrib];
|
|
if (/range/i.test(attrib)) {
|
|
attributes = [item_entry + 'range_pick', item_entry + 'range', item_entry + 'range_numeric'];
|
|
} else {
|
|
attributes = [
|
|
item_entry + 'range_pick',
|
|
item_entry + 'range',
|
|
item_entry + 'range_numeric',
|
|
item_entry + 'damage-macro-text',
|
|
item_entry + 'damage-type',
|
|
item_entry + 'sr',
|
|
item_entry + 'savedc',
|
|
item_entry + 'save',
|
|
item_entry + 'abil-attack-type',
|
|
item_entry + 'name',
|
|
item_entry + 'toggle_attack_entry',
|
|
];
|
|
}
|
|
} else {
|
|
attributes = [
|
|
item_entry + 'range_pick',
|
|
item_entry + 'range',
|
|
item_entry + 'range_numeric',
|
|
item_entry + 'damage-macro-text',
|
|
item_entry + 'damage-type',
|
|
item_entry + 'sr',
|
|
item_entry + 'savedc',
|
|
item_entry + 'save',
|
|
item_entry + 'abil-attack-type',
|
|
item_entry + 'name',
|
|
item_entry + 'toggle_attack_entry',
|
|
];
|
|
}
|
|
getAttrs(attributes, function (spellVal) {
|
|
getSectionIDs('repeating_weapon', function (idarray) {
|
|
// get the repeating set
|
|
let spellsourcesFields = [];
|
|
spellsourcesFields = _.reduce(
|
|
idarray,
|
|
function (memo, currentID) {
|
|
memo.push('repeating_weapon_' + currentID + '_source-ability');
|
|
return memo;
|
|
},
|
|
[],
|
|
);
|
|
getAttrs(spellsourcesFields, function (v) {
|
|
let setter = {},
|
|
params = {},
|
|
idlist = [];
|
|
try {
|
|
_.each(idarray, function (currentID) {
|
|
let prefix = 'repeating_weapon_' + currentID + '_';
|
|
if (v[prefix + 'source-ability'] === itemId) {
|
|
idlist.push(currentID);
|
|
setter = setAttackEntryVals(item_entry, prefix, spellVal, setter);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
TAS.error('PFAbility.updateAssociatedAttack', err);
|
|
} finally {
|
|
if (_.size(setter) > 0) {
|
|
if (silently) {
|
|
params = PFConst.silentParams;
|
|
}
|
|
SWUtils.setWrapper(setter, params, function () {
|
|
PFAttackOptions.resetSomeOptions(idlist);
|
|
});
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
function updateCharLevel(id, callback, eventInfo) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving updateCharLevel");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
idStr = SWUtils.getRepeatingIDStr(id),
|
|
prefix = 'repeating_ability_' + idStr;
|
|
getAttrs([prefix + 'CL-misc-mod', prefix + 'CL-basis-mod', prefix + 'casterlevel', prefix + 'ability_type', 'buff_CasterLevel-total', 'CasterLevel-Penalty'], function (v) {
|
|
let clBase = 0,
|
|
cl = 0,
|
|
misc = 0,
|
|
pen = 0,
|
|
isSP = 0,
|
|
setter = {};
|
|
try {
|
|
isSP = parseInt(v[prefix + 'ability_type'], 10) || 0;
|
|
clBase = parseInt(v[prefix + 'CL-basis-mod'], 10) || 0;
|
|
misc = parseInt(v[prefix + 'CL-misc-mod'], 10) || 0;
|
|
pen = parseInt(v['CasterLevel-Penalty'], 10) || 0;
|
|
cl = clBase + misc + pen;
|
|
if (isSP) {
|
|
cl += parseInt(v['buff_CasterLevel-total'], 10) || 0;
|
|
}
|
|
if (cl !== parseInt(v[prefix + 'casterlevel'], 10)) {
|
|
setter[prefix + 'casterlevel'] = cl;
|
|
}
|
|
} catch (err) {
|
|
TAS.error('PFAbility.updateCharLevel', err);
|
|
} finally {
|
|
if (_.size(setter)) {
|
|
SWUtils.setWrapper(setter, PFConst.silentParams, done);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function updateAbilityRange(id, callback, silently, eventInfo) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving updateAbilityRange");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
idStr = SWUtils.getRepeatingIDStr(id),
|
|
prefix = 'repeating_ability_' + idStr;
|
|
getAttrs([prefix + 'range_pick', prefix + 'range', prefix + 'range_numeric', prefix + 'casterlevel', prefix + 'ability_type'], function (v) {
|
|
let newRange = 0,
|
|
currRange = 0,
|
|
cl = 0,
|
|
setter = {},
|
|
isSP = 0,
|
|
currPosRange = 0;
|
|
try {
|
|
isSP = v[prefix + 'ability_type'] === 'Sp' ? 1 : 0;
|
|
currRange = parseInt(v[prefix + 'range_numeric'], 10) || 0;
|
|
if (isSP) {
|
|
cl = parseInt(v[prefix + 'casterlevel'], 10) || 0;
|
|
newRange = PFUtils.findSpellRange(v[prefix + 'range'], v[prefix + 'range_pick'], cl) || 0;
|
|
} else {
|
|
newRange = parseInt(SWUtils.trimBoth(v[prefix + 'range']), 10) || 0;
|
|
}
|
|
if (newRange !== currRange) {
|
|
//TAS.debug("updating range");
|
|
setter[prefix + 'range_numeric'] = newRange;
|
|
}
|
|
currPosRange = parseInt(v[prefix + 'hasposrange'], 10) || 0;
|
|
if (newRange > 0 && !currPosRange) {
|
|
setter[prefix + 'hasposrange'] = 1;
|
|
} else if (currPosRange) {
|
|
setter[prefix + 'hasposrange'] = 0;
|
|
}
|
|
} catch (err) {
|
|
TAS.error('updateAbilityRange', err);
|
|
} finally {
|
|
if (_.size(setter)) {
|
|
SWUtils.setWrapper(setter, {}, done);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/** to use in calls to _.invoke or otherwise, sets switch variables to setter for given row
|
|
* @param {jsobj} setter to pass in first var of SWUtils.setWrapper
|
|
* @param {string} id the id of this row, or null if we are within the row context already
|
|
* @param {jsobj} v the values needed returned by getAttrs
|
|
*/
|
|
function resetOption(setter, id, v, eventInfo) {
|
|
let idStr = SWUtils.getRepeatingIDStr(id),
|
|
prefix = 'repeating_ability_' + idStr,
|
|
isSP = '',
|
|
posRange = '',
|
|
hasUses = '',
|
|
hasFrequency = '',
|
|
hasAttack = '',
|
|
atkstr = '',
|
|
attackStrForDisplay = '';
|
|
setter = setter || {};
|
|
try {
|
|
if (!v) {
|
|
return setter;
|
|
}
|
|
isSP = v[prefix + 'ability_type'] === 'Sp' ? '1' : '';
|
|
|
|
if (isSP !== v[prefix + 'is_sp']) {
|
|
setter[prefix + 'is_sp'] = isSP;
|
|
}
|
|
posRange = (parseInt(v[prefix + 'range_numeric'], 10) || 0) > 0 ? '1' : '';
|
|
if (posRange !== v[prefix + 'hasposrange']) {
|
|
setter[prefix + 'hasposrange'] = posRange;
|
|
}
|
|
if (v[prefix + 'frequency'] && v[prefix + 'frequency'] !== 'not-applicable') {
|
|
hasFrequency = '1';
|
|
switch (v[prefix + 'frequency']) {
|
|
case 'perday':
|
|
case 'permonth':
|
|
case 'hexfreq':
|
|
case 'other':
|
|
hasUses = '1';
|
|
break;
|
|
}
|
|
}
|
|
if (hasFrequency !== v[prefix + 'hasfrequency']) {
|
|
setter[prefix + 'hasfrequency'] = hasFrequency;
|
|
}
|
|
if (hasUses !== v[prefix + 'hasuses']) {
|
|
setter[prefix + 'hasuses'] = hasUses;
|
|
}
|
|
if (PFUtils.findAbilityInString(v[prefix + 'abil-attack-type'])) {
|
|
hasAttack = '1';
|
|
}
|
|
if (hasAttack !== v[prefix + 'hasattack']) {
|
|
setter[prefix + 'hasattack'] = hasAttack;
|
|
}
|
|
if (hasAttack) {
|
|
atkstr = v[prefix + 'abil-attack-type'].toLowerCase();
|
|
if (atkstr.indexOf('melee') >= 0) {
|
|
attackStrForDisplay = 'touch';
|
|
} else if (atkstr.indexOf('range') >= 0) {
|
|
attackStrForDisplay = 'ranged-touch-ray';
|
|
} else if (atkstr.indexOf('cmb') >= 0) {
|
|
attackStrForDisplay = 'combat-maneuver-bonus-abbrv';
|
|
}
|
|
}
|
|
if (attackStrForDisplay !== v[prefix + 'abil-attacktypestr']) {
|
|
setter[prefix + 'abil-attacktypestr'] = attackStrForDisplay;
|
|
}
|
|
} catch (err) {
|
|
TAS.error('PFAbility.recalcAbilities', err);
|
|
} finally {
|
|
return setter;
|
|
}
|
|
}
|
|
function resetOptionAsync(id, callback, eventInfo) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving PFAbility.resetOption");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
idStr = SWUtils.getRepeatingIDStr(id),
|
|
prefix = 'repeating_ability_' + idStr,
|
|
fields = [];
|
|
fields = _.map(allOptionRepeatingFields, function (attr) {
|
|
return prefix + attr;
|
|
});
|
|
getAttrs(fields, function (v) {
|
|
let setter = {};
|
|
try {
|
|
setter = resetOption(setter, id, v);
|
|
} catch (err) {
|
|
TAS.error('PFAbility.recalcAbilities', err);
|
|
} finally {
|
|
if (_.size(setter)) {
|
|
SWUtils.setWrapper(setter, PFConst.silentParams, done, eventInfo);
|
|
} else {
|
|
done();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function recalcAbilities(callback, silently, eventInfo, levelOnly) {
|
|
let done = _.once(function () {
|
|
//TAS.debug("leaving PFAbility.recalcAbilities");
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
});
|
|
getSectionIDs('repeating_ability', function (ids) {
|
|
let numids = _.size(ids),
|
|
doneOne,
|
|
calllevel;
|
|
if (numids === 0) {
|
|
done();
|
|
return;
|
|
}
|
|
doneOne = _.after(numids, done);
|
|
//refactor to do all rows at once
|
|
calllevel = function (id) {
|
|
PFUtilsAsync.setRepeatingDropdownValue(
|
|
'ability',
|
|
id,
|
|
'CL-basis',
|
|
'CL-basis-mod',
|
|
function () {
|
|
updateCharLevel(id, function () {
|
|
setClassName(id);
|
|
updateAbilityRange(id, function () {
|
|
doneOne();
|
|
});
|
|
});
|
|
},
|
|
true,
|
|
true,
|
|
);
|
|
};
|
|
_.each(ids, function (id) {
|
|
calllevel(id);
|
|
if (!levelOnly) {
|
|
resetOptionAsync(id);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
export function migrate(callback) {
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}
|
|
export let recalculate = TAS.callback(function callPFAbilityRecalculate(callback, silently, oldversion) {
|
|
let done = _.once(function () {
|
|
TAS.info('leaving PFAbility.recalculate');
|
|
if (typeof callback === 'function') {
|
|
callback();
|
|
}
|
|
}),
|
|
doneWithList = function () {
|
|
resetCommandMacro();
|
|
done();
|
|
},
|
|
callRecalcAbilities = function () {
|
|
//TAS.debug("PF1 calling recalcAbilities");
|
|
recalcAbilities(TAS.callback(doneWithList));
|
|
setRuleTabs();
|
|
};
|
|
try {
|
|
//TAS.debug("at PFAbility.recalculate");
|
|
migrate(TAS.callback(callRecalcAbilities));
|
|
} catch (err) {
|
|
TAS.error('PFAbility.recalculate, ', err);
|
|
done();
|
|
}
|
|
});
|
|
//sync repeating_ability with settings>attacks>link spells
|
|
function updateIncludeLink() {
|
|
getSectionIDs('repeating_ability', function (ids) {
|
|
_.each(ids, function (id) {
|
|
getAttrs(['include_link_abilities'], function (values) {
|
|
let include_link_abilities = parseInt([values.include_link_abilities]) || 0;
|
|
setAttrs({
|
|
['repeating_ability_' + id + '_toggle_attack_entry']: include_link_abilities,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
//trigger updateIncludeLink when updating the sheet version
|
|
export function updateIncludeLinkVersionCheck() {
|
|
setAttrs({
|
|
include_link_abilities: 1,
|
|
include_link_spells: 1,
|
|
});
|
|
}
|
|
|
|
function registerEventHandlers() {
|
|
let eventToWatch = '',
|
|
macroEvent = 'remove:repeating_ability ',
|
|
singleEvent = 'change:repeating_ability:';
|
|
|
|
on(
|
|
'remove:repeating_ability',
|
|
TAS.callback(function eventRemoveAbility(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
PFAttacks.removeLinkedAttack(null, PFAttacks.linkedAttackType.ability, SWUtils.getRowId(eventInfo.sourceAttribute));
|
|
}),
|
|
);
|
|
macroEvent = _.reduce(
|
|
events.commandMacroFields,
|
|
function (m, a) {
|
|
m += singleEvent + a + ' ';
|
|
return m;
|
|
},
|
|
macroEvent,
|
|
);
|
|
on(
|
|
macroEvent,
|
|
TAS.callback(function eventRepeatingAbilityCommandMacroUpdate(eventInfo) {
|
|
if (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api' || /used_max/i.test(eventInfo.sourceAttribute)) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
PFFeatures.resetTopCommandMacro(null, eventInfo);
|
|
resetCommandMacro();
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:CL-basis',
|
|
TAS.callback(function eventAbilityClassDropdown(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
SWUtils.evaluateAndSetNumber('repeating_ability_CL-basis', 'repeating_ability_CL-basis-mod');
|
|
if (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api') {
|
|
setClassName(null, null, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
eventToWatch = _.reduce(
|
|
optionRepeatingHelperFields,
|
|
function (m, a) {
|
|
m += 'change:repeating_ability:' + a + ' ';
|
|
return m;
|
|
},
|
|
'',
|
|
);
|
|
on(
|
|
eventToWatch,
|
|
TAS.callback(function eventChangeAbilityTypeFrequencyOrRange(eventInfo) {
|
|
if (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api' || /range/i.test(eventInfo.sourceAttribute)) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
resetOptionAsync();
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:CL-misc change:repeating_ability:spell_level-misc',
|
|
TAS.callback(function eventSLAEquationMacro(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
SWUtils.evaluateAndSetNumber(eventInfo.sourceAttribute, eventInfo.sourceAttribute + '-mod');
|
|
}),
|
|
);
|
|
on(
|
|
'change:buff_CasterLevel-total change:CasterLevel-Penalty',
|
|
TAS.callback(function eventAbilityLevelChange(eventInfo) {
|
|
if (eventInfo.sourceType === 'sheetworker' || eventInfo.sourceType === 'api') {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
recalcAbilities(null, null, eventInfo, true);
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:CL-basis-mod change:repeating_ability:CL-misc-mod',
|
|
TAS.callback(function eventAbilityLevelChange(eventInfo) {
|
|
if (eventInfo.sourceType === 'sheetworker' || eventInfo.sourceType === 'api') {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
updateCharLevel(null, null, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:compendium_category',
|
|
TAS.callback(function eventAbilityCompendium(eventInfo) {
|
|
if (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api') {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
importFromCompendium(null, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:create-attack-entry',
|
|
TAS.callback(function eventcreateAttackEntryFromSLA(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
if (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api') {
|
|
createAttackEntryFromRow(null, null, false, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:CL-misc-mod change:repeating_ability:CL-basis-mod change:repeating_ability:range_pick change:repeating_ability:range',
|
|
TAS.callback(function eventClassRangeMod(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
//cl-misc-mod, cl-basis-mod is sheetworker, range_pick and range must be player
|
|
if (
|
|
(/range/i.test(eventInfo.sourceAttribute) && (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api')) ||
|
|
(/CL/i.test(eventInfo.sourceAttribute) && eventInfo.sourceType === 'sheetworker') ||
|
|
eventInfo.sourceType === 'api'
|
|
) {
|
|
updateAbilityRange(null, null, false, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
eventToWatch = _.reduce(
|
|
events.attackEventsSLA,
|
|
function (memo, attr) {
|
|
memo += 'change:repeating_ability:' + attr + ' ';
|
|
return memo;
|
|
},
|
|
'',
|
|
);
|
|
on(
|
|
eventToWatch,
|
|
TAS.callback(function eventupdateAssociatedSLAttackAttack(eventInfo) {
|
|
if (eventInfo.sourceType === 'player' || eventInfo.sourceType === 'api' || /range_numeric/i.test(eventInfo.sourceAttribute)) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
updateAssociatedAttack(null, null, null, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:toggle_attack_entry',
|
|
TAS.callback(function eventupdateAssociatedSLAttackAttack(eventInfo) {
|
|
if (eventInfo.sourceType === 'sheetworker' || eventInfo.sourceType === 'api') {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
updateAssociatedAttack(null, null, null, eventInfo);
|
|
}
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:rule_category',
|
|
TAS.callback(function eventUpdateAbilityRule(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
setRuleTab(null, null, null, eventInfo);
|
|
}),
|
|
);
|
|
on(
|
|
'change:repeating_ability:ability_type',
|
|
TAS.callback(function eventUpdateAbilityType(eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event: ' + eventInfo.sourceType);
|
|
setTypeTab(null, null, null, eventInfo);
|
|
}),
|
|
);
|
|
// toggles the chat menu Show option for all repeating abilities
|
|
on('change:showinmenu_all_abilities', function () {
|
|
getSectionIDs('repeating_ability', function (idArrayAbility) {
|
|
getSectionIDs('repeating_mythic-ability', function (idArrayMythicAbility) {
|
|
getSectionIDs('repeating_mythic-feat', function (idArrayMythicFeat) {
|
|
const fieldNamesAbility = idArrayAbility.map((id) => `repeating_ability_${id}_showinmenu`),
|
|
fieldNamesMythicAbility = idArrayMythicAbility.map((id) => `repeating_mythic-ability_${id}_showinmenu`),
|
|
fieldNamesMythicFeat = idArrayMythicFeat.map((id) => `repeating_mythic-feat_${id}_showinmenu`);
|
|
getAttrs(['showinmenu_all_abilities'], function (values) {
|
|
const toggle = +values['showinmenu_all_abilities'] || 0,
|
|
settings = {};
|
|
fieldNamesAbility.forEach((field) => {
|
|
// loop through the showinmenu array
|
|
settings[field] = toggle; // assign the toggle value to each row's attribute
|
|
});
|
|
fieldNamesMythicAbility.forEach((field) => {
|
|
settings[field] = toggle;
|
|
});
|
|
fieldNamesMythicFeat.forEach((field) => {
|
|
settings[field] = toggle;
|
|
});
|
|
setAttrs(settings);
|
|
PFAbility.recalculate();
|
|
PFFeatures.recalculate();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
//sync repeating_abilities with settings>attacks>link abilities
|
|
on('change:include_link_abilities', function (eventInfo) {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event' + eventInfo.sourceType);
|
|
if (eventInfo.sourceType === 'sheetworker' || eventInfo.sourceType === 'player') {
|
|
updateIncludeLink();
|
|
}
|
|
});
|
|
// dynamic translation of datalist for ability_type2
|
|
on('change:repeating_ability:ability_type2', (eventInfo) => {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event' + eventInfo.sourceType);
|
|
const id = eventInfo.sourceAttribute.split('_')[2];
|
|
const updateAttrs = {};
|
|
const featName = eventInfo.newValue.replace(/\s/g, '').toLowerCase();
|
|
let translation = getTranslationByKey(featName);
|
|
if (translation) {
|
|
console.info({featName, translation});
|
|
updateAttrs[`repeating_ability_${id}_ability_type2`] = translation;
|
|
const attr = `repeating_ability_${id}_ability_type2`;
|
|
const i18n = `${featName}`;
|
|
if (getTranslationByKey(i18n)) {
|
|
updateAttrs[attr] = getTranslationByKey(i18n);
|
|
}
|
|
TAS.debug(updateAttrs);
|
|
setAttrs(updateAttrs, {silent: true});
|
|
}
|
|
});
|
|
|
|
on('change:repeating_ability:ability_type2 change:repeating_ability:rule_category', (eventInfo) => {
|
|
TAS.debug('caught ' + eventInfo.sourceAttribute + ' event' + eventInfo.sourceType);
|
|
PFMacros.checkAbilityType2Row(eventInfo);
|
|
});
|
|
}
|
|
registerEventHandlers();
|