'use strict'; import _ from 'underscore'; import TAS from './TheAaronSheet.js'; import {PFLog, PFConsole} from './PFLog'; import PFConst from './PFConst'; import * as SWUtils from './SWUtils'; import * as PFUtils from './PFUtils'; export var optionToggles = ["toggle_spell_school_notes", "toggle_spell_casting_time_notes", "toggle_spell_duration_notes", "toggle_spell_saving_throw_notes", "toggle_spell_sr_notes", "toggle_spell_range_notes", "toggle_spell_targets_notes", "toggle_spell_description_notes", "toggle_spell_concentration_notes", "toggle_spell_concentration_check", "toggle_spell_casterlevel_notes", "toggle_spell_casterlevel_check", "toggle_spell_level_notes", "toggle_spell_components_notes", "toggle_spell_spellnotes_notes", "toggle_spell_spell_fail_check", "toggle_spell_damage_notes", "toggle_spell_spellPen_check", "toggle_spell_dc_notes"], optionTemplates = { school: "{{school=@{school}}}", casting_time: "{{casting_time=@{cast-time}}}", components: "{{components=@{components}}}", duration: "{{duration=@{duration}}}", saving_throw: "{{saving_throw=@{save}}}", sr: "{{sr=@{sr}}}", casterlevel: "{{casterlevel=[[ @{casterlevel} ]]}}", range: "{{range=@{range_numeric}}}", targets: "{{targets=@{targets}}}", Concentration: "{{Concentration=[[ @{Concentration-mod} ]]}}", description: "{{description=@{description}}}", dc: "{{dc=[[ @{savedc} ]]}}", spellPen: "{{spellPen=[[ @{SP-mod} ]]}}", range_pick: "{{@{range_pick}=Range_pick}}", rangetext: "{{rangetext=@{range}}}", level: "{{level=@{spell_level}}}", spellclass: "{{spellclass=@{spellclass}}}", cast_def: "{{cast_def=[[ REPLACE ]]}}", cast_defDC: "{{cast_defDC=[[ REPLACE ]]}}", concentrationNote: "{{concentrationNote=REPLACE}}", spellPenNote: "{{spellPenNote=REPLACE}}", casterlevel_chk: "{{casterlevel_chk=[[ 1d20 + @{casterlevel} ]]}}", Concentration_chk: "{{Concentration_chk=[[ 1d20 + @{Concentration-mod} ]]}}", spellnotes: "{{spells_notes=REPLACE}}", spell_fail_check: "{{spell_fail_check=[[ 1d100cf<[[ @{spell-fail} ]]cs>[[ @{spell-fail}+1 ]] ]]}}", spell_fail: "{{spell_fail=@{spell-fail}}}", spelldamage: "{{spelldamage=@{damage-macro-text}}}", spelldamagetype: "{{spelldamagetype=@{damage-type}}}", spellPen_chk: "{{spellPen_chk=[[ 1d20 + @{casterlevel} + @{SP-mod} ]]}}" }, optionTemplateRegexes = PFUtils.getOptionsCompiledRegexMap(optionTemplates); /* non repeating */ var optionHelperAttrs = ["Concentration-0-def", "Concentration-1-def", "Concentration-2-def", "spell-fail"], optionTogglesPlusOptionAttrs = optionToggles.concat(optionHelperAttrs), /* repeating*/ repeatingOptionAttrs = ["school", "cast-time", "duration", "save", "sr", "range_numeric", "targets", "description", "Concentration-mod", "savedc", "SP-mod", "range_pick", "range", "spell_level", "spellclass", "casterlevel", "components", "spellclass_number", "damage-macro-text", "damage-type"], repeatingOptionHelperAttrs = ["spellclass_number", "SP_misc", "CL_misc", "Concentration_misc", "slot", "spell-attack-type"], repeatingOptionAttrsToGet = repeatingOptionAttrs.concat(repeatingOptionHelperAttrs), rowattrToOptionToggleMap = { school: "toggle_spell_school_notes", "cast-time": "toggle_spell_casting_time_notes", components: "toggle_spell_components_notes", duration: "toggle_spell_duration_notes", save: "toggle_spell_saving_throw_notes", dc: "toggle_spell_dc_notes", sr: "toggle_spell_sr_notes", range: "toggle_spell_range_notes", targets: "toggle_spell_targets_notes", description: "toggle_spell_description_notes", spellnotes:"toggle_spells_notes", spell_fail_check: "toggle_spell_spell_fail_check", "damage-macro-text": "toggle_spell_damage_notes", "damage-type": "toggle_spell_damage_notes" }, events = { spellOptionEventsPlayer: ["school", "cast-time", "components", "duration", "save", "sr", "range", "targets", "damage-macro-text", "damage-type","sp-mod"] }; /** updateSpellOption - updates an existing @{spell_options} text for a row depending on the field updated on existing row * * @param {obj} eventInfo * @param {string} fieldUpdated */ export function updateSpellOption (eventInfo, fieldUpdated) { var fieldName = "repeating_spells_" + fieldUpdated, toggleField = rowattrToOptionToggleMap[fieldUpdated]; getAttrs([fieldName, "repeating_spells_spell_options", "repeating_spells_spell_lvlstr", toggleField, "repeating_spells_sr", "repeating_spells_SP-mod", "repeating_spells_savedc"], function (v) { var optionText = v["repeating_spells_spell_options"], newValue = "", setter = {}; //make sure we are not updating from compendium //this works it is just fast enough that it will not do anything since importFromCompendium is not done. if ((!v["repeating_spells_spell_lvlstr"]) && optionText) { try { //TAS.debug("PFSpellOptions.updateSpellOption, field: "+ fieldUpdated,v); newValue = v[fieldName] || ""; if (parseInt(v[toggleField],10) === 1) { //TAS.debug"made it inside toggleField"); switch (fieldUpdated) { case 'school': optionText = optionText.replace(optionTemplateRegexes.school, optionTemplates.school);//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); break; case 'cast-time': if ( !newValue || (/1\s+(S\.A\.|standard action)/i).test( newValue )){ optionText = PFUtils.deleteOption(optionText, "casting_time", optionTemplateRegexes); } else { optionText = optionText.replace(optionTemplateRegexes.casting_time, optionTemplates.casting_time);//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); } break; case 'components': optionText = optionText.replace(optionTemplateRegexes.components, optionTemplates.components);//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); break; case 'duration': optionText = optionText.replace(optionTemplateRegexes.duration, optionTemplates.duration);//replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); break; case 'range': optionText = optionText.replace(optionTemplateRegexes.range, optionTemplates.range);//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); break; case 'targets': optionText = optionText.replace(optionTemplateRegexes.targets, optionTemplates.targets);//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); break; case 'save': if (PFUtils.shouldNotDisplayOption('saving_throw', newValue)) { optionText = PFUtils.deleteOption(optionText, "saving_throw", optionTemplateRegexes); } else { optionText = optionText.replace(optionTemplateRegexes.saving_throw, optionTemplates.saving_throw);//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue))); } break; case 'sr': if (PFUtils.shouldNotDisplayOption('sr', newValue)) { optionText = PFUtils.deleteOption(optionText, "sr", optionTemplateRegexes); } else { optionText = optionText.replace(optionTemplateRegexes.sr, optionTemplates.sr);//.replace("REPLACE", newValue)); } break; case 'damage-macro-text': //TAS.debug("found damage macro-text="+newValue); if (!newValue) { optionText = PFUtils.deleteOption(optionText, "spelldamage", optionTemplateRegexes); } else { optionText = optionText.replace(optionTemplateRegexes.spelldamage, optionTemplates.spelldamage);//.replace("REPLACE", newValue)); } break; case 'damage-type': //TAS.debug("found damage type"+newValue); if (!newValue) { optionText = PFUtils.deleteOption(optionText, "spelldamagetype", optionTemplateRegexes); } else { optionText = optionText.replace(optionTemplateRegexes.spelldamagetype, optionTemplates.spelldamagetype);//.spelldamagetype.replace("REPLACE", newValue)); } break; case 'sp-mod': TAS.debug("found damage type"+newValue); if (!newValue) { optionText = PFUtils.deleteOption(optionText, "spellPen", optionTemplateRegexes); } else { optionText = optionText.replace(optionTemplateRegexes.spellPen, optionTemplates.spellPen);//.spelldamagetype.replace("REPLACE", newValue)); } } setter["repeating_spells_spell_options"] = optionText; SWUtils.setWrapper(setter, { silent: true }); } } catch (err){ TAS.error("PFSpellOptions.updateSpellOption",err); } } }); } /** getOptionText - resets entire @{spell_options} text for a spell row * if the field to update is one that is set by updateSpellOption, then need to set {{key=}} so it can find correct one to replace. *@param {string} id of row or null *@param {jsobj} eventInfo NOT USED *@param {object} toggleValues values from getAttrs of spell toggle option fields *@param {object} rowValues values from getAttrs of row attributes *@returns {string} */ export function getOptionText (id, eventInfo, toggleValues, rowValues) { var prefix = "repeating_spells_" + SWUtils.getRepeatingIDStr(id), customConcentration = parseInt(rowValues[prefix + "Concentration_misc"], 10) || 0, customCasterlevel = parseInt(rowValues[prefix + "CL_misc"], 10) || 0, classNum = parseInt(rowValues[prefix + "spellclass_number"], 10), spellLevel = parseInt(rowValues[prefix + "spell_level"], 10), spellSlot = parseInt(rowValues[prefix + "slot"], 10), casterlevel = parseInt(rowValues[prefix + "casterlevel"], 10), concentrationMod = parseInt(rowValues[prefix + "Concentration-mod"], 10), levelForConcentrate = (isNaN(spellSlot) || spellSlot === spellLevel) ? spellLevel : spellSlot, defDC = 15 + (levelForConcentrate * 2), defMod = parseInt(toggleValues["Concentration-" + classNum + "-def"], 10) || 0, optionText = "{{condition_note=@{condition_spell_notes}}}", newValue = ""; //TAS.debug("getOptionText, defMod: " + defMod); if (isNaN(classNum) || isNaN(spellLevel)) { TAS.warn("cannot set options for spell! id:" + id + " class or level are not numbers"); return "{{condition_note=@{condition_spell_notes}}}"; } if (toggleValues.showschool) { optionText += optionTemplates.school;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "school"]))||""; } if (toggleValues.showlevel) { optionText += optionTemplates.spellclass;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "spellclass"]))||""; optionText += optionTemplates.level;//.replace("REPLACE", spellLevel); } if (toggleValues.showcasting_time) { if ( !rowValues[prefix+"cast-time"] || (/1 (S\.A\.|standard action)/i).test( rowValues[prefix+"cast-time"])){ optionText += "{{casting_time=}}"; } else { optionText += optionTemplates.casting_time;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "cast-time"]))||""; } } if (toggleValues.showcomponents) { optionText += optionTemplates.components;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "components"]))||""; } if (toggleValues.showsaving_throw && toggleValues.showdc) { //checks to see if DC should be included newValue = rowValues[prefix + "save"] || ""; if (PFUtils.shouldNotDisplayOption('saving_throw', newValue)) { optionText += "{{saving_throw=}}"; } else { optionText += optionTemplates.saving_throw;//.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue)||""); } optionText += optionTemplates.dc;//.replace("REPLACE", parseInt(rowValues[prefix + "savedc"], 10) || 0); } else { newValue = rowValues[prefix + "save"] || ""; if (PFUtils.shouldNotDisplayOption('saving_throw', newValue)) { optionText += "{{saving_throw=}}"; } else { optionText += optionTemplates.saving_throw; //.replace("REPLACE", SWUtils.escapeForRollTemplate(newValue)||""); } optionText += "{{dc=}}"; //.replace("REPLACE", parseInt(rowValues[prefix + "savedc"], 10) || 0); } if (toggleValues.showrange) { optionText += optionTemplates.range_pick;//.replace("REPLACE", rowValues[prefix + "range_pick"] || "blank")||""; optionText += optionTemplates.range;//.replace("REPLACE", parseInt(rowValues[prefix + "range_numeric"], 10) || 0)||""; optionText += optionTemplates.rangetext;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "range"] || "")||""); } if (toggleValues.showtargets) { optionText += optionTemplates.targets;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "targets"])||""); } if (toggleValues.showduration) { optionText += optionTemplates.duration;//.replace("REPLACE", SWUtils.escapeForRollTemplate(rowValues[prefix + "duration"])||""); } if (toggleValues.showsr) { newValue = rowValues[prefix + "sr"] || ""; if (PFUtils.shouldNotDisplayOption('sr', newValue)) { optionText += "{{sr=}}"; } else { optionText += optionTemplates.sr;//.replace("REPLACE", newValue)||""; } newValue = parseInt(rowValues[prefix+"SP-mod"],10)||0; if(newValue){ optionText += optionTemplates.spellPen; } else { optionText += "{{spellPen=}}"; } } if (toggleValues.showcasterlevel && customCasterlevel) { optionText += optionTemplates.casterlevel;//.replace("REPLACE", casterlevel)||""; } else { optionText += "{{casterlevel=}}"; } if (toggleValues.showcasterlevel) { optionText += optionTemplates.casterlevel;//.replace("REPLACE", casterlevel)||""; } if (toggleValues.showcasterlevel_check) { optionText += optionTemplates.casterlevel_chk;//.replace("REPLACE", casterlevel)||""; } if (toggleValues.showspellpen_check) { optionText += optionTemplates.spellPen_chk; } if (toggleValues.showcasterlevel || toggleValues.showcasterlevel_check) { newValue = parseInt(rowValues[prefix + "SP-mod"], 10) || 0; if (newValue === 0) { optionText += optionTemplates.spellPen;//.replace("REPLACE", newValue)||""; } } if (toggleValues.showconcentration && (customConcentration || customCasterlevel)) { optionText += optionTemplates.Concentration;//.replace("REPLACE", concentrationMod)||""; } else { optionText += "{{Concentration=}}"; } if (toggleValues.showconcentration_check && toggleValues.showcasterlevel) { optionText += optionTemplates.Concentration;//.replace("REPLACE", concentrationMod)||""; } if (toggleValues.showconcentration_check) { optionText += optionTemplates.Concentration_chk;//.replace("REPLACE", concentrationMod)||""; } if ((toggleValues.showconcentration || toggleValues.showconcentration_check) && toggleValues.showdc) { //checks to see if DC should be included if (defMod > 0) { optionText += optionTemplates.cast_def.replace("REPLACE", defMod)||""; } else { optionText += "{{cast_def=}}"; } optionText += optionTemplates.cast_defDC.replace("REPLACE", defDC)||""; } else { if (defMod > 0) { optionText += optionTemplates.cast_def.replace("REPLACE", defMod) || ""; } else { optionText += "{{cast_def=}}"; } optionText += "{{cast_defDC=}}"; } if (toggleValues.showdescription) { optionText += optionTemplates.description;//.replace("REPLACE", "@{description}")||""; } if (toggleValues.showspellnotes) { optionText += optionTemplates.spellnotes.replace("REPLACE", "@{spell-class-"+classNum+"-spells-notes}")||""; } if (toggleValues.showspell_fail_check) { optionText += optionTemplates.spell_fail_check; optionText += optionTemplates.spell_fail; } if (toggleValues.showdamage ) { if(rowValues[prefix+"damage-macro-text"]){ optionText += optionTemplates.spelldamage;//.replace("REPLACE",(rowValues[prefix+"damage-macro-text"])||""); } else { optionText += "{{spelldamage=}}"; } if (rowValues["damage-type"]){ optionText += optionTemplates.spelldamagetype;//.replace("REPLACE", rowValues["damage-type"]||""); } else { optionText += "{{spelldamagetype=@{damage-type}}}"; } } else { optionText += "{{spelldamage=}}{{spelldamagetype=@{damage-type}}}"; } //TAS.debug("PFSpell.resetOption returning "+optionText); return optionText; } /** resetOption updates repeating_spells_$X_spell_options *@param {string} id id of row or null *@param {jsobj} eventInfo NOT USED */ export function resetOption (id, eventInfo) { var prefix = "repeating_spells_" + SWUtils.getRepeatingIDStr(id), allFields; allFields = _.map(repeatingOptionAttrsToGet, function (field) { return prefix + field; }).concat(optionTogglesPlusOptionAttrs); getAttrs(allFields, function (v) { var toggleValues = _.chain(optionToggles).reduce(function (memo, attr) { memo['show' + attr.toLowerCase().slice(13).replace('_notes', '')] = (parseInt(v[attr], 10) || 0); return memo; }, {}).extend({ "Concentration-0-def": (parseInt(v["Concentration-0-def"], 10) || 0), "Concentration-1-def": (parseInt(v["Concentration-1-def"], 10) || 0), "Concentration-2-def": (parseInt(v["Concentration-2-def"], 10) || 0) }).value(), optionText = "", setter = {}; TAS.debug("PFSPellOptions.resetOption id : "+id,toggleValues); optionText = getOptionText(id, eventInfo, toggleValues, v)||"{{condition_note=@{condition_spell_notes}}}"; //TAS.debug("resetOption","About to set",setter); setter["repeating_spells_" + SWUtils.getRepeatingIDStr(id) + "spell_options"] = optionText; SWUtils.setWrapper(setter, { silent: true }); }); } /**resetOptions - updates repeating_spells_spell_options for all spells. *@param {function} callback call when done *@param {object} eventInfo NOT USED */ export function resetOptions (callback, eventInfo) { var done = _.once(function(){ if (typeof callback === "function"){ callback(); } }); getAttrs(optionTogglesPlusOptionAttrs, function (tv) { var toggleValues; try { TAS.debug("PFSpellOptions.resetOptions:",tv); toggleValues = _.chain(optionToggles).reduce(function (memo, attr) { //get word between toggle_spell_ and _notes memo['show' + attr.toLowerCase().slice(13).replace('_notes', '')] = (parseInt(tv[attr], 10) || 0); return memo; }, {}).extend({ "Concentration-0-def": (parseInt(tv["Concentration-0-def"], 10) || 0), "Concentration-1-def": (parseInt(tv["Concentration-1-def"], 10) || 0), "Concentration-2-def": (parseInt(tv["Concentration-2-def"], 10) || 0) }).value(); TAS.debug("PFSPellOptions.resetOptions: ",toggleValues); getSectionIDs("repeating_spells", function (ids) { var fields,prefixes; if (!ids || _.size(ids)===0){ done(); return; } prefixes = _.map(ids, function (id) { return "repeating_spells_" + id + "_"; }); fields = SWUtils.cartesianAppend(prefixes,repeatingOptionAttrsToGet); getAttrs(fields, function (v) { var setter = {}; _.each(ids, function (id) { var optionText =''; try { optionText= getOptionText(id, eventInfo, toggleValues, v)||"{{condition_note=@{condition_spell_notes}}}"; //TAS.debug("setting option text for "+id+" to "+optionText); setter["repeating_spells_" + id + "_spell_options"] = optionText; } catch (innererr){ TAS.error("PFSpellOptions.resetOptions error on id "+id+", innererr",innererr); } }); if(_.size(setter)>0){ SWUtils.setWrapper(setter,PFConst.silentParams,done); } else { done(); } }); }); } catch (outererr){ TAS.error("PFSpellOptions.resetOptions outererr:",outererr); done(); } }); } export var recalculate = TAS.callback(function PFSpellOptionsRecalculate(callback) { resetOptions(callback); }); function registerEventHandlers () { //spell options for one row _.each(events.spellOptionEventsPlayer, function (fieldToWatch) { var eventToWatch = "change:repeating_spells:" + fieldToWatch; on(eventToWatch, TAS.callback(function eventOptionsRepeatingSpellsPlayer(eventInfo) { if (eventInfo.sourceType === "player" || eventInfo.sourceType === "api") { TAS.debug("caught " + eventInfo.sourceAttribute + " event: " + eventInfo.sourceType); updateSpellOption(eventInfo, fieldToWatch); } })); }); //update the spell options _.each(optionToggles, function (toggleField) { on("change:" + toggleField, TAS.callback(function toggleSpellOptionField(eventInfo) { if (eventInfo.sourceType === "player" || eventInfo.sourceType === "api") { TAS.debug("caught " + eventInfo.sourceAttribute + " event: " + eventInfo.sourceType); resetOptions(null, eventInfo); } })); }); } registerEventHandlers(); //PFConsole.log(' PFSpellOptions module loaded '); //PFLog.modulecount++;