Created
June 16, 2019 19:35
-
-
Save vince-roll20/20ae0e0a18ab13e790400747a721286e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
import _ from 'underscore'; | |
import {PFLog, PFConsole} from './PFLog'; | |
import TAS from 'exports-loader?TAS!TheAaronSheet'; | |
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'; | |
var 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"], | |
commandMacroFields:["name","used","used_max","showinmenu","ability_type","frequency","rule_category"] | |
}; | |
function setClassName (id,callback,eventInfo){ | |
var 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',"race","class-0-name","class-1-name","class-2-name","class-3-name","class-4-name","class-5-name"],function(v){ | |
var clBase='',setter={},match; | |
try { | |
if (v[clbasisField]){ | |
if (v[clbasisField]==="@{level}"){ | |
clBase =v["race"]; | |
} else if (v[clbasisField]==="@{npc-hd-num}"){ | |
clBase = v["race"]; | |
} 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){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var setter={}, ruleForTab='', params=PFConst.silentParams; | |
switch(v[prefix + 'rule_category']){ | |
case 'racial-traits': | |
ruleForTab='traits'; | |
break; | |
case 'monster-rule': | |
ruleForTab='other'; | |
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.abilites_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){ | |
var done= function(typeObj){ | |
//TAS.debug('Ability.getAbilityTypes returning with ',typeObj); | |
if (typeof callback === "function"){ | |
callback(typeObj); | |
} | |
}; | |
getSectionIDs('repeating_ability',function(ids){ | |
var fields=[]; | |
if(!ids || _.size(ids)===0){ | |
done({'rules':[],'types':[]}); | |
return; | |
} | |
_.each(ids,function(id){ | |
var prefix='repeating_ability_'+id+'_'; | |
fields.push(prefix+'rule_category'); | |
fields.push(prefix+'showinmenu'); | |
fields.push(prefix+'ability_type'); | |
}); | |
getAttrs(fields,function(v){ | |
var basearray=[], rulearray = [], typearray=[]; | |
basearray = _.chain(ids) | |
.map(function(id){ | |
var 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){ | |
var 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) { | |
var 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){ | |
var 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){ | |
var 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){ | |
var 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("cound 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){ | |
var 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){ | |
var 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){ | |
var 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.specialAttackDCAbilityBase[abilname]){ | |
ability_basis= PFDB.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){ | |
var 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 += "Save: "+ v[spellPrefix+"save"] + " DC: [[@{" + spellPrefix + "savedc}]]"; | |
} | |
if ( v[spellPrefix+"abil-sr"]){ | |
if (notes) { notes += ", ";} | |
notes += "Spell resist:"+ v[spellPrefix+"abil-sr"]; | |
} | |
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) { | |
var 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"] | |
; | |
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) { | |
var 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) { | |
var 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"]; | |
} | |
} 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"]; | |
} | |
getAttrs(attributes,function(spellVal){ | |
getSectionIDs("repeating_weapon", function (idarray) { // get the repeating set | |
var spellsourcesFields=[]; | |
spellsourcesFields = _.reduce(idarray,function(memo,currentID){ | |
memo.push("repeating_weapon_"+currentID+"_source-ability"); | |
return memo; | |
},[]); | |
getAttrs(spellsourcesFields,function(v){ | |
var setter={}, params={},idlist=[]; | |
try { | |
_.each(idarray,function(currentID){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var 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){ | |
var done = _.once(function(){ | |
//TAS.debug("leaving PFAbility.recalcAbilities"); | |
if (typeof callback === "function"){ | |
callback(); | |
} | |
}); | |
getSectionIDs('repeating_ability',function(ids){ | |
var 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 var recalculate = TAS.callback(function callPFAbilityRecalculate(callback, silently, oldversion) { | |
var 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(); | |
} | |
}); | |
function registerEventHandlers () { | |
var eventToWatch="", | |
macroEvent = "remove:repeating_ability ", | |
singleEvent = "change:repeating_ability:, change:showinmenu_all_abilities"; | |
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: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(idArray) { | |
const fieldNames = idArray.map(id => `repeating_ability_${id}_showinmenu`); | |
getAttrs(['showinmenu_all_abilities'], function(values) { | |
const toggle = +values['showinmenu_all_abilities']||0; | |
const settings = fieldNames.reduce((obj, item) => (obj[item] = toggle, obj) ,{}); | |
setAttrs(settings); | |
}); | |
}); | |
}); | |
} | |
registerEventHandlers(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment