// repeating sections data array const BUFF = { active: 'active_r', ws: 'WS_mod_r', bs: 'BS_mod_r', str: 'STR_mod_r', tou: 'TOU_mod_r', agi: 'AGI_mod_r', int: 'INT_mod_r', per: 'PER_mod_r', wp: 'WP_mod_r', fel: 'FEL_mod_r', inf: 'INF_mod_r' }, GEAR = { carry: 'gear_carry_r', name: 'gear_name_r', craftsmanship: 'gear_crafts_r', quantity: 'gear_quantity_r', availability: 'gear_avail_r', weight: 'gear_weight_r' }, R_WEAPON = { name : 'R_weapon_name', class : 'R_weapon_class', range : 'R_weapon_range', damage : 'R_weapon_dam-roll', pen : 'R_weapon_pen', type : 'R_weapon_type', max : 'R_weapon_max', min : 'R_weapon_min', vengeful : 'R_weapon_vengeful', accurate : 'R_weapon_accurate', burst : 'R_weapon_burst', auto : 'R_weapon_auto', mag : 'R_weapon_mag', mag_max : 'R_weapon_mag_max', trained : 'R_weapon_trained', mod1 : 'R_weapon_mod1', reload : 'R_weapon_reload', weight : 'R_weapon_weight', domain : 'R_weapon_domain' } // ___________________________________________________________________________ // __________________________Script for Ammo API______________________________ // ___________________________________________________________________________ on("sheet:opened change:repeating_rangedweapons:r_weapon_attack change:repeating_rangedweapons:r_weapon_burst change:repeating_rangedweapons:r_weapon_auto remove:repeating_rangedweapons change:repeating_vehiclerangedweapons:v_r_weapon_attack change:repeating_vehiclerangedweapons:v_r_weapon_burst change:repeating_vehiclerangedweapons:v_r_weapon_auto remove:repeating_vehiclerangedweapons", function() { getSectionIDs("rangedweapons", function(idarray) { // first get the attribute names for all rows in put in one array const fieldnames = []; idarray.forEach(id => fieldnames.push( `repeating_rangedweapons_${id}_R_weapon_attack`, `repeating_rangedweapons_${id}_R_weapon_burst`, `repeating_rangedweapons_${id}_R_weapon_auto` )); getAttrs(fieldnames, values => { // create a variable to hold all the attribute values you re going to create. const attr = {}; // now loop through the rows again idarray.forEach(id => { let row = 'repeating_rangedweapons_' + id; let attack = +values[`${row}_R_weapon_attack`] || 0, burst = +values[`${row}_R_weapon_burst`] || 0, auto = +values[`${row}_R_weapon_auto`] || 0; let ammo = attack === -10 ? -auto : (attack === 0 ? -burst : -1); attr[`${row}_id`] = id; // id of the repeating element attr[`${row}_ammo`] = ammo; // number of ammo to use }); setAttrs(attr); }); }); getSectionIDs("vehiclerangedweapons", function(idarray) { // first get the attribute names for all rows in put in one array const fieldnames = []; idarray.forEach(id => fieldnames.push( `repeating_vehiclerangedweapons_${id}_V_R_weapon_attack`, `repeating_vehiclerangedweapons_${id}_V_R_weapon_burst`, `repeating_vehiclerangedweapons_${id}_V_R_weapon_auto` )); getAttrs(fieldnames, values => { // create a variable to hold all the attribute values you re going to create. const attr = {}; // now loop through the rows again idarray.forEach(id => { let row = 'repeating_vehiclerangedweapons_' + id; let attack = +values[`${row}_V_R_weapon_attack`] || 0, burst = +values[`${row}_V_R_weapon_burst`] || 0, auto = +values[`${row}_V_R_weapon_auto`] || 0; let ammo = attack === -10 ? -auto : (attack === 0 ? -burst : -1); attr[`${row}_id`] = id; // id of the repeating element attr[`${row}_ammo`] = ammo; // number of ammo to use }); setAttrs(attr); }); }); }); // ___________________________________________________________________________ // __________________Skills unnatural bonus sheet worker______________________ // ___________________________________________________________________________ /** * skillRoll description * @param {string} skill - name of the skill * @param {string} focus - short name of the characteristic */ const skillRoll = (skill, focus) => { let attr = {}; attr[`${skill}_focus`] = `@{${focus}Total}`; attr[`${skill}_unnat`]=`@{${focus.toUpperCase()}_unnat}`; return attr; }; let skills = [ 'acrobatics','athletics','awareness','charm','command','commerce','deceive','dodge','inquiry', 'interrogation','intimidate','logic','medicae','nav_surface','nav_stellar','nav_warp','op_aero','op_surface','op_void', 'parry','psyniscience','scrutiny','security','sleight','stealth','survival','tech_use', 'linguistics','common-lore','scholastic-lore','forbidden-lore','trade' ]; let repeatingSkills = ['skill','linguistics','common-lore','scholastic-lore','forbidden-lore','trade']; on('sheet:opened ' + skills.map(skill => `change:${skill}_char change:${skill}_name`).join(' ') + repeatingSkills.map(skill => `change:repeating_${skill}:${skill}_r_char change:repeating_${skill}:${skill}_name`).join(' '), function () { console.log('== UPDATING SKILLS =='); getAttrs(skills.map( skill => `${skill}_char`), (values) => { let attr = {}; for (var [skill, focus] of Object.entries(values)) { attr = {...attr, ...skillRoll(skill.replace('_char', ''), focus)}; } setAttrs(attr); }); repeatingSkills.forEach(field => { var repSkills = []; getSectionIDs(field, ids => { ids.forEach( id => { repSkills.push(`repeating_${field}_${id}_${field}_r_char`); }); getAttrs(repSkills, (values) => { let attr = {}; for (var [skill, focus] of Object.entries(values)) { attr = {...attr, ...skillRoll(skill.replace('_char', ''), focus)}; } if (attr) setAttrs(attr); console.table(values) console.table(attr) }); }); }); console.log('== SKILLS UPDATED =='); }); // ___________________________________________________________________________ // ____________________Movement and Weight sheet worker_______________________ // ___________________________and Fatigue Pts_________________________________ let fieldsMWF = [ 'size', 'fatigue', 'carry', 'weight_total', 'ws', 'ws_unnat', 'ws_advancement', 'ws_mod', 'bs', 'bs_unnat', 'bs_advancement', 'bs_mod', 'str', 'str_unnat', 'str_advancement', 'str_mod', 'tou', 'tou_unnat', 'tou_advancement', 'tou_mod', 'int', 'int_unnat', 'int_advancement', 'int_mod', 'per', 'per_unnat', 'per_advancement', 'per_mod', 'wp', 'wp_unnat', 'wp_advancement', 'wp_mod', 'fel', 'fel_unnat', 'fel_advancement', 'fel_mod', 'inf', 'inf_unnat', 'agi', 'agi_unnat', 'agi_advancement', 'agi_mod', 'agi_mov_mod', 'armor_agi', 'armor_limit', 'fatigue_ignore', 'psykana_class' ]; on('sheet:opened ' + fieldsMWF.map(field => 'change:' + field).join(' '), function(e) { // console.log(e); getAttrs(fieldsMWF, function(values) { // SIZE var size = parseInt(values['size'], 10) || 0; // FATIGUE var fatigue = parseInt(values['fatigue'], 10) || 0; var fatigueIgnore = parseInt(values['fatigue_ignore'], 10) || 0; if (fatigueIgnore) fatigue = 0; // CARRY var carry = parseInt(values['carry'], 10) || 0; var weight_total = parseInt(values['weight_total'], 10) || 0; // STATS function Stat(stat) { this.base = parseInt(values[`${stat}`], 10) || 0 this.modifier = parseInt(values[`${stat}_mod`], 10) || 0 this.advancement = parseInt(values[`${stat}_advancement`], 10) || 0 this.unnat = parseInt(values[`${stat}_unnat`], 10) || 0 this.total = this.base + this.modifier + this.advancement this.bonus = () => Math.floor(this.total / 10 + this.unnat) } // WS var ws = new Stat('ws') if (ws.bonus() < fatigue) ws.total = Math.ceil(ws.total / 2) // BS var bs = new Stat('bs') if (bs.bonus() < fatigue) bs.total = Math.ceil(bs.total / 2) // STR var str = new Stat('str') if (str.bonus() < fatigue) str.total = Math.ceil(str.total / 2) // TOU var tou = new Stat('tou') if (tou.bonus() < fatigue) tou.total = Math.ceil(tou.total / 2) // AGI var agi = new Stat('agi') agi_mov_mod = parseInt(values['agi_mov_mod'], 10) || 0, armor_agi = parseInt(values['armor_agi'], 10) || 0, armor_limit = parseInt(values['armor_limit'], 10) || 0; if (weight_total > carry) agi.total -= 10 if (armor_limit) agi.total = (agi.total > armor_agi ) ? armor_agi : agi.total if (agi.bonus() < fatigue) agi.total = Math.ceil(agi.total / 2) // INT var int = new Stat('int') if (int.bonus() < fatigue) int.total = Math.ceil(int.total / 2) // PER var per = new Stat('per') if (per.bonus() < fatigue) per.total = Math.ceil(per.total / 2) // WP var wp = new Stat('wp') if (wp.bonus() < fatigue) wp.total = Math.ceil(wp.total / 2) // FEL var fel = new Stat('fel') // Blank FEL halfing var blank = values['psykana_class'] if (blank === '-1') fel.total = Math.ceil(fel.total / 2); if (fel.bonus() < fatigue) fel.total = Math.ceil(fel.total / 2) // INF var inf = new Stat('inf') if (inf.bonus() < fatigue) inf.total = Math.ceil(inf.total / 2) var move_mod = agi.bonus() + size + agi_mov_mod; var carry_mod = str.bonus() + tou.bonus(); var carry_calc; // 40k system does their carry weight off a sheet-table switch (carry_mod) { case 1: carry_calc = 2.25; break; case 2: carry_calc = 4.5; break; case 3: carry_calc = 9; break; case 4: carry_calc = 18; break; case 5: carry_calc = 27; break; case 6: carry_calc = 36; break; case 7: carry_calc = 45; break; case 8: carry_calc = 56; break; case 9: carry_calc = 67; break; case 10: carry_calc = 78; break; case 11: carry_calc = 90; break; case 12: carry_calc = 112; break; case 13: carry_calc = 225; break; case 14: carry_calc = 337; break; case 15: carry_calc = 450; break; case 16: carry_calc = 675; break; case 17: carry_calc = 900; break; case 18: carry_calc = 1350; break; case 19: carry_calc = 1800; break; case 20: carry_calc = 2250; break; } // normal, no unnatural setAttrs({ half_move: (move_mod), full_move: (move_mod * 2), charge_move: (move_mod * 3), run_move: (move_mod * 6), carry: (carry_calc * 1), //taking out the * 1 seems to cause attr_carry to freeze when carry_mod is set to 1 or 0 lift: (carry_calc * 2), push: (carry_calc * 4), bsTotal: (bs.total), bsB: (bs.bonus()), wsTotal: (ws.total), wsB: (ws.bonus()), strTotal: (str.total), strB: (str.bonus()), touTotal: (tou.total), touB: (tou.bonus()), agiTotal: (agi.total), agiB: (agi.bonus()), intTotal: (int.total), intB: (int.bonus()), perTotal: (per.total), perB: (per.bonus()), wpTotal: (wp.total), wpB: (wp.bonus()), felTotal: (fel.total), felB: (fel.bonus()), infTotal: (inf.total), infB: (inf.bonus()), }); // 0 mod movement override if (move_mod <= 0) { setAttrs({ half_move: (0.5), full_move: (1), charge_move: (2), run_move: (3) }); } // 0 mod carry override if (carry_mod <=0) { setAttrs({ carry: (0.9), lift: (2.25), push: (4.5) }); } }); }); // ___________________________________________________________________________ // ________________________Fatigue and Armor sheet worker_____________________ // ___________________________________________________________________________ let fieldsFA = [ 'fatigue_mod', 'tou', 'tou_unnat', 'tou_advancement', 'tou_mod', 'wp', 'wp_unnat', 'wp_advancement', 'armor_head', 'armor_arm_right', 'armor_body', 'armor_arm_left', 'armor_leg_right', 'armor_leg_left' ]; on('sheet:opened ' + fieldsFA.map(field => 'change:' + field).join(' '), function() { getAttrs(fieldsFA, function(values) { var fatigue_mod = parseInt(values['fatigue_mod'], 10) || 0; var toughness = parseInt(values['tou'], 10) || 0; var toughness_unnat = parseInt(values['tou_unnat'], 10) || 0; var toughness_advancement = parseInt(values['tou_advancement'], 10) || 0; var toughness_modifier = parseInt(values['tou_mod'], 10) || 0; var toughness_bonus = Math.floor((toughness + toughness_advancement + toughness_modifier) / 10 + toughness_unnat); var toughness_mod = Math.floor((toughness + toughness_advancement) / 10 + toughness_unnat); var willpower = parseInt(values['wp'], 10) || 0; var willpower_unnat = parseInt(values['wp_unnat'], 10) || 0; var willpower_advancement = parseInt(values['wp_advancement'], 10) || 0; var willpower_mod = Math.floor((willpower + willpower_advancement) / 10 + willpower_unnat); var armor_head = parseInt(values['armor_head'], 10) || 0; var armor_arm_right = parseInt(values['armor_arm_right'], 10) || 0; var armor_body = parseInt(values['armor_body'], 10) || 0; var armor_arm_left = parseInt(values['armor_arm_left'], 10) || 0; var armor_leg_right = parseInt(values['armor_leg_right'], 10) || 0; var armor_leg_left = parseInt(values['armor_leg_left'], 10) || 0; // normal, no modifiers ___________________________________________________ setAttrs({ soak_head: (armor_head + toughness_bonus), soak_arm_right: (armor_arm_right + toughness_bonus), soak_body: (armor_body + toughness_bonus), soak_arm_left: (armor_arm_left + toughness_bonus), soak_leg_right: (armor_leg_right + toughness_bonus), soak_leg_left: (armor_leg_left + toughness_bonus), fatigue_max: (toughness_mod + willpower_mod + fatigue_mod) }); }); }); // ___________________________________________________________________________ // ____________________Tabs sheet worker______________________________________ // ___________________________________________________________________________ const buttonlist = ['core','equipment','advancements','journal','vehicle','psykana','settings']; buttonlist.forEach(button => { on(`clicked:${button}`, function() { setAttrs({ sheetTab: button }); }); }); // ___________________________________________________________________________ // ____________________Inventory Tabs sheet worker____________________________ // ___________________________________________________________________________ const buttonListIventory = ['all', 'weapon', 'armour', 'gear', 'tool', 'consumables', 'ammo', 'cyberntic', 'other']; buttonListIventory.forEach(button => { on(`clicked:inv_${button}`, function() { setAttrs({ equipmentTab: button }); }); }); // ___________________________________________________________________________ // ____________________insanity/corruption sheet worker_______________________ // ___________________________________________________________________________ on('sheet:opened change:insanity_pts change:corruption_pts', function() { getAttrs(['insanity_pts', 'corruption_pts'], function(values) { var insanity_pts = parseInt(values['insanity_pts'], 0) || 0; var corruption_pts = parseInt(values['corruption_pts'], 10) || 0; function insanity (points) { if (points <= 9) return ('Stable') if (points <= 39) return ('Unsettled') if (points <= 59) return ('Disturbed') if (points <= 79) return ('Unhinged') if (points <= 99) return ('Deranged') return ('Terminally Insane') } function corruption (points) { if (points <= 9) return ('Pure') if (points <= 39) return ('Tainted') if (points <= 59) return ('Soiled') if (points <= 79) return ('Debased') if (points <= 99) return ('Profane') return ('Damned') } setAttrs({ insanity_degree: (insanity(insanity_pts)), corruption_degree: (corruption(corruption_pts)) }) }); }); // ___________________________________________________________________________ // _______________________psychic info sheet worker___________________________ // ___________________________________________________________________________ const PSY_RANK = {} let fieldsPsy = ['psykana_class', 'psykana_limit', 'psy_push', 'psy_rating','psykana_sustain','psykana_sustain_count']; on('sheet:opened ' + fieldsPsy.map(field => 'change:' + field).join(' '), function() { getAttrs(fieldsPsy, function(values) { var psykana_class = parseInt(values['psykana_class'])||0; var psykana_limit = parseInt(values['psykana_limit'])||0; var psy_push = parseInt(values['psy_push'])||0; var psy_rating = parseInt(values['psy_rating'], 10) || 0; var psykana_sustain = parseInt(values['psykana_sustain'], 10) || 0; var psykana_sustain_count = parseInt(values['psykana_sustain_count'], 10) || 0; var psykana_limit_factor = 0; var push_addend_epr = 0; var sustain_count_addend = 0; var unfettered_addend = 0; var push_addend_pp = 0; var sustain_addend = 0; // psy rating calc switch (psykana_limit) { case 0: psykana_limit_factor = 0.5; setAttrs({ psy_roll: ('cf>96') }); break; case 1: psykana_limit_factor = 1; setAttrs({ psy_roll: ('cf11cf22cf33cf44cf55cf66cf77cf88cf99cf100') }); break; case 2: psykana_limit_factor = 1; push_addend_epr = psy_push; if (psykana_class == 0) { setAttrs({ psy_roll: ('cs1cs11cs22cs33cs44cs55cs66cs77cs88cs99cs100cf>0') //psy_roll: ('cf>0') }); } else { setAttrs({ psy_roll: ('cf>0') }); } break; } if (psykana_sustain == -5) { sustain_count_addend = psykana_sustain_count; } var epr = Math.ceil(psy_rating * psykana_limit_factor) + push_addend_epr; // phenomena calc if (psykana_class == 2 && psykana_limit == 1) { unfettered_addend = 10; } if (psykana_class == 0 && psykana_limit == 2) { push_addend_pp = 10; } if (psykana_class == 1 && psykana_limit == 2) { push_addend_pp = 5 * psy_push; } if (psykana_class == 2 && psykana_limit == 2) { push_addend_pp = 10 * psy_push; } if (psykana_sustain == -5) { sustain_addend = 5 * sustain_count_addend; } var pp = unfettered_addend + push_addend_pp + sustain_addend; //output setAttrs({ effective_psy_rating: epr, psykana_phenomena: pp }); }); }); // ___________________________________________________________________________ // ____________________gear cost/weight sheet worker__________________________ // ___________________________________________________________________________ on('change:repeating_rangedweapons remove:repeating_rangedweapons change:repeating_meleeweapons remove:repeating_meleeweapons', function() { repeatingWeight("R_weapon_weight_t","rangedweapons",[['weight',"R_weapon_weight"],['carry',"R_weapon_carry"]]), repeatingWeight("M_weapon_weight_t","meleeweapons",[['weight',"M_weapon_weight"],['carry',"M_weapon_carry"]]); }); on('change:M_weapon_weight_t change:R_weapon_weight_t change:gear_carry change:gear_weight change:gear_quantity change:repeating_gear remove:repeating_gear', () => { getSectionIDs(`repeating_gear`, idArray => { const fieldset_weight = []; const fieldset_quantity = []; const fieldset_carry = []; idArray.forEach(id => fieldset_weight.push (`repeating_gear_${id}_gear_weight_r`)); idArray.forEach(id => fieldset_quantity.push (`repeating_gear_${id}_gear_quantity_r`)); idArray.forEach(id => fieldset_carry.push (`repeating_gear_${id}_gear_carry_r`)); getAttrs(['M_weapon_weight_t','R_weapon_weight_t','weight_total', 'gear_carry', 'gear_weight', 'gear_quantity', ...fieldset_weight, ...fieldset_quantity, ...fieldset_carry], v => { const getValue = (id, field) => parseFloat(v[`repeating_gear_${id}_${field}`]) || 0 const weight_base = parseFloat(v.gear_weight) * parseFloat(v.gear_quantity) * parseFloat(v.gear_carry) const weight_repeating = idArray.reduce((total, id) => total + (getValue(id, 'gear_weight_r') * getValue(id, 'gear_quantity_r') * getValue(id, 'gear_carry_r')), 0) const weapon_weight = parseFloat(v.M_weapon_weight_t) + parseFloat(v.R_weapon_weight_t) setAttrs({ weight_total: weight_base + weight_repeating + weapon_weight, }); }); }); }); // ___________________________________________________________________________ // ____________________Advancement XP sheet worker_______________________________ // ___________________________________________________________________________ on('change:xp_total change:advancement_costL change:advancement_costR change:repeating_advancements remove:repeating_advancements', () => { // get section IDs because of operation both inside and outside of fieldset getSectionIDs(`repeating_advancements`, idArray => { const fieldnames = []; idArray.forEach(id => fieldnames.push ( `repeating_advancements_${id}_advancement_costL_r`, `repeating_advancements_${id}_advancement_costR_r` )); // spread operator to get every array value getAttrs(['xp_total', 'advancement_costL', 'advancement_costR', ...fieldnames], v => { const getValue = (id, field) => parseFloat(v[`repeating_advancements_${id}_${field}`]) || 0 const cost = parseFloat(v.gear_cost) || 0 const left = parseFloat(v.advancement_costL) || 0 const right = parseFloat(v.advancement_costR) || 0 const left_r = idArray.reduce((total, id) => total + getValue(id,'advancement_costL_r'),0) const right_r = idArray.reduce((total, id) => total + getValue(id,'advancement_costR_r'),0) const spent = left + right + left_r + right_r const total = parseFloat(v.xp_total) || 0 setAttrs({ xp_spent: spent, xp_current: total - spent }); }); }); }); // ___________________________________________________________________________ // __________________________General sum sheet worker_________________________ // ___________________________________________________________________________ /** * [repeatingWeight description] * @param {[type]} destination [description] * @param {[type]} section [description] * @param {[type]} fields [description] */ const repeatingWeight = (destination, section, fields) => { getSectionObjects(section, fields, items => { const sumTotal = items.filter(item => item.carry !== '0').reduce((total, item) => total + parseFloat(item.weight),0); setAttrs({[destination]: sumTotal}); }) } // ___________________________________________________________________________ // _________________________ weapon damage primitive _________________________ // ___________________________________________________________________________ /** * calculate real damage roll * @param {string} source row id of the weapon */ const calcDmg = function (sources, fields) { var redice = /^(\d+)?(d\d+)(.*)?/; getAttrs(fields, v => { let attr = {}; sources.forEach( source => { // to avoid retyping long strings because i'm lazy var dam = source + 'weapon_dam-roll', ven = source + 'weapon_vengeful', // crit min = source + 'weapon_min', // proven max = source + 'weapon_max', // primitive set = source + 'weapon_setting'; // overcharge/load / maximal let dmg = redice.exec(v[dam]); ven = parseInt(v[ven], 10) || 0, min = parseInt(v[min], 10) || 0, max = parseInt(v[max], 10) || 0, set = parseInt(v[set], 10) || 0; if (!dmg) { console.log('no damage found'); attr[`${source}weapon_real_dmg`] = v[dam]; } else { let dicenum = parseInt(dmg[1], 10) || 1, dicetype = dmg[2], mod = dmg[3]; let damTotal = '{'; if (set === 3) dicenum ++; for (var i = 0; i < dicenum; i++) { if (i) damTotal += ', ' if (min > 0) damTotal += '{'; if (max > 0) damTotal += '{'; damTotal += (dicetype == 'd5') ? 'ceil(1d10/2)' : '1d10'; if (ven > 0) damTotal += 'cs>' + ven; if (min > 0) damTotal += ', {' + min + '}}kh1'; if (max > 0) damTotal += ', {' + max + '}}kl1'; } damTotal += '}'; if (mod) damTotal += mod; if (set === 2) damTotal += '+1'; if (set === 4) { damTotal += '+2'; attr[`${source}weapon_pen2`] = 2; } else { attr[`${source}weapon_pen2`] = 0; } attr[`${source}weapon_real_dmg`] = damTotal; } }); setAttrs(attr); }); }; let fieldsDMG = [ 'vehiclerangedweapons:V_R_weapon_dam-roll', 'vehiclerangedweapons:V_R_weapon_vengeful', 'vehiclerangedweapons:V_R_weapon_min', 'vehiclerangedweapons:V_R_weapon_max', 'vehiclerangedweapons:V_R_weapon_setting', 'rangedweapons:R_weapon_dam-roll', 'rangedweapons:R_weapon_vengeful', 'rangedweapons:R_weapon_min', 'rangedweapons:R_weapon_max', 'rangedweapons:R_weapon_setting', 'vehiclemeleeweapons:V_M_weapon_dam-roll', 'vehiclemeleeweapons:V_M_weapon_vengeful', 'vehiclemeleeweapons:V_M_weapon_min', 'vehiclemeleeweapons:V_M_weapon_max', 'meleeweapons:M_weapon_dam-roll', 'meleeweapons:M_weapon_vengeful', 'meleeweapons:M_weapon_min', 'meleeweapons:M_weapon_max', ]; on('sheet:opened ' + fieldsDMG.map(field => 'change:repeating_' + field).join(' '), () => { getSectionIDs('rangedweapons', (idArray) => { let sources = []; let fieldnames = []; idArray.forEach( id => { sources.push(`repeating_rangedweapons_${id}_R_`); fieldnames.push( `repeating_rangedweapons_${id}_R_weapon_dam-roll`, `repeating_rangedweapons_${id}_R_weapon_vengeful`, `repeating_rangedweapons_${id}_R_weapon_min`, `repeating_rangedweapons_${id}_R_weapon_max`, `repeating_rangedweapons_${id}_R_weapon_setting` ); }); calcDmg(sources, fieldnames); }); getSectionIDs('meleeweapons', (idArray) => { let sources = []; let fieldnames = []; idArray.forEach( id => { sources.push(`repeating_meleeweapons_${id}_M_`); fieldnames.push( `repeating_meleeweapons_${id}_M_weapon_dam-roll`, `repeating_meleeweapons_${id}_M_weapon_vengeful`, `repeating_meleeweapons_${id}_M_weapon_min`, `repeating_meleeweapons_${id}_M_weapon_max` ); }); calcDmg(sources, fieldnames); }); getSectionIDs('vehiclerangedweapons', (idArray) => { let sources = []; let fieldnames = []; idArray.forEach( id => { sources.push(`repeating_vehiclerangedweapons_${id}_V_R_`); fieldnames.push( `repeating_vehiclerangedweapons_${id}_V_R_weapon_dam-roll`, `repeating_vehiclerangedweapons_${id}_V_R_weapon_vengeful`, `repeating_vehiclerangedweapons_${id}_V_R_weapon_min`, `repeating_vehiclerangedweapons_${id}_V_R_weapon_max`, `repeating_vehiclerangedweapons_${id}_V_R_weapon_setting` ); }); calcDmg(sources, fieldnames); }); getSectionIDs('vehiclemeleeweapons', (idArray) => { let sources = []; let fieldnames = []; idArray.forEach( id => { sources.push(`repeating_vehiclemeleeweapons_${id}_V_M_`); fieldnames.push( `repeating_vehiclemeleeweapons_${id}_V_M_weapon_dam-roll`, `repeating_vehiclemeleeweapons_${id}_V_M_weapon_vengeful`, `repeating_vehiclemeleeweapons_${id}_V_M_weapon_min`, `repeating_vehiclemeleeweapons_${id}_V_M_weapon_max` ); }); calcDmg(sources, fieldnames); }); console.log('DAMAGES UPDATED'); }); // ___________________________________________________________________________ // __________________________ get repeating objects __________________________ // ___________________________________________________________________________ // TODO: add class suport /** * get the attributes from a repeating section and pass them in an array of objects * @param {String} section - name of the repeating section * @param {Array} fields - list of the attributes * @param {Function} callback - function to pass the array of objects */ const getSectionObjects = function (section, fields, callback) { if (!Array.isArray(fields)) fields = [fields]; getSectionIDs(`repeating_${section}`, idArray => { const attrArray = idArray.reduce( (m,id) => [...m, ...(Object.values(fields).map(field => `repeating_${section}_${id}_${field}`))],[]); getAttrs(attrArray, v => { const objects = []; idArray.forEach(id => { let obj = { _id : id, _source: `repeating_${section}_${id}` }; for (var property in fields) { obj[property] = v[`repeating_${section}_${id}_${fields[property]}`] } objects.push(obj) }); callback(objects); }); }); } // ___________________________________________________________________________ // ________________________________ Importer _________________________________ // ___________________________________________________________________________ on('clicked:import_clear', () => { setAttrs({ import_value: '' }); }); on('clicked:import', () => { getAttrs(['import_value'], (v) => { try { var data = JSON.parse(v.import_value); } catch(e) { setAttrs({import_value: 'ERROR: verify if you have copied the totality of the Export'}); return console.log(e); } console.log('Importing Character'); var attributes = {} // emptying the repeating sections const sectionList = [ 'conditions', 'skill', 'linguistics', 'common-lore', 'scholastic-lore', 'forbidden-lore', 'trade', 'abilities', 'traits', 'rangedweapons', 'meleeweapons', 'gear', 'cybernetics', 'aptitudes', 'advancements', 'disorders', 'mutations', 'objectives', 'notes', 'vehicles', 'vehiclerangedweapons', 'vehiclemeleeweapons', 'minormanifestation', 'psynotes', 'psychicabilities' ]; sectionList.forEach( section => { getSectionIDs(`repeating_${section}`, idArray => { idArray.forEach(id => removeRepeatingRow(`repeating_${section}_${id}`)) }) }) // info Block attributes = {...attributes, ...mapInfo(data)} // STATS attributes = {...attributes, ...mapStat('WS', data?.weaponSkill)}; attributes = {...attributes, ...mapStat('BS', data?.ballisticSkill)}; attributes = {...attributes, ...mapStat('STR', data?.strenght)}; attributes = {...attributes, ...mapStat('TOU', data?.toughness)}; attributes = {...attributes, ...mapStat('AGI', data?.agility)}; attributes = {...attributes, ...mapStat('INT', data?.intelligence)}; attributes = {...attributes, ...mapStat('PER', data?.perception)}; attributes = {...attributes, ...mapStat('WP', data?.willpower)}; attributes = {...attributes, ...mapStat('FEL', data?.fellowship)}; attributes = {...attributes, ...mapStat('INF', data?.influence)}; // WOUNDS, FATE, FATIGUE, FORCEFIELD attributes = {...attributes, ...mapWounds(data?.wounds)}; attributes = {...attributes, ...mapFate(data?.fate)}; attributes = {...attributes, ...mapFatigue(data?.fatigue)}; attributes = {...attributes, ...mapForceField(data?.forcefield)}; // CONDITIONS data?.conditions?.forEach((condition) => { attributes = {...attributes, ...mapCondition(condition)}; }); // SKILLS, REPEATING SKILLS data?.skills?.forEach(skill => { attributes = {...attributes, ...mapSkill(skill)}; }); data?.lores?.common?.forEach(skill => { attributes = {...attributes, ...mapRepeatingSkill(skill, 'common-lore')}; }); data?.lores?.scholastic?.forEach(skill => { attributes = {...attributes, ...mapRepeatingSkill(skill, 'scholastic-lore')}; }); data?.lores?.forbidden?.forEach(skill => { attributes = {...attributes, ...mapRepeatingSkill(skill, 'forbidden-lore')}; }); data?.linguistics?.forEach(skill => { attributes = {...attributes, ...mapRepeatingSkill(skill, 'linguistics')}; }); data?.trades?.forEach(skill => { attributes = {...attributes, ...mapRepeatingSkill(skill, 'trade')}; }); // TALENTS, TRAITS data?.abilities?.forEach(ability => { attributes = {...attributes, ...mapAbility(ability, 'abilities')}; }); data?.traits?.forEach(trait => { attributes = {...attributes, ...mapTrait(trait)}; }); // INVENTORY data?.inventory?.weapons?.ranged?.forEach( weapon => { attributes = {...attributes, ...mapRangedWeapon(weapon)}; }); data?.inventory?.weapons?.melee?.forEach( weapon => { attributes = {...attributes, ...mapMeleeWeapon(weapon)}; }); data?.inventory?.gears?.forEach( gear => { attributes = {...attributes, ...mapGear(gear)}; }); // data?.inventory?.tools?.forEach( tool => { // attributes = {...attributes, ...mapTool(tool)}; // }); // data?.inventory?.consumables?.forEach( drug => { // attributes = {...attributes, ...mapDrug(drug)}; // }); attributes = {...attributes, ...mapOldArmor(data?.inventory?.armor)}; // data?.inventory?.armors?.forEach( armor => { // attributes = {...attributes, ...mapArmor(armor)}; // }); // data?.inventory?.ammos?.forEach( ammo => { // attributes = {...attributes, ...mapAmmo(ammo)}; // }); data?.inventory?.cybernetics?.forEach( cybernetic => { attributes = {...attributes, ...mapCybernetic(cybernetic)}; }); // ADVANCEMENTS attributes = {...attributes, ...mapAptitudes(data?.advancements?.aptitudes)}; attributes = {...attributes, ...mapAdvance(data?.advancements?.advances)}; for (var training in data?.advancements?.training) { attributes[`weapons_training_${training}`] = data?.advancements?.training[training]; } // INSANITY, CORRUPTION attributes[`insanity_pts`] = data?.insanity?.points; data?.insanity?.disorders?.forEach( disorder => { attributes = {...attributes, ...mapDisorder(disorder)}; }) attributes[`corruption_pts`] = data?.corruption?.points; data?.corruption?.mutations?.forEach( mutation => { attributes = {...attributes, ...mapMutation(mutation)}; }) // OBJECTIVES, NOTES data?.objectives?.forEach( objective => { attributes = {...attributes, ...mapObjective(objective)}; }); data?.notes?.forEach( note => { attributes = {...attributes, ...mapNote(note)}; }); // VEHICLES data?.vehicles?.list?.forEach( vehicle => { attributes = {...attributes, ...mapVehicle(vehicle)}; }); data?.vehicles?.weapons?.ranged?.forEach( weapon => { attributes = {...attributes, ...mapRangedWeapon(weapon, true)}; }); data?.vehicles?.weapons?.melee?.forEach( weapon => { attributes = {...attributes, ...mapMeleeWeapon(weapon, true)}; }); // PSYKANA attributes = {...attributes, ...mapPsykana(data?.psykana)}; data?.psykana?.minor?.forEach( minor => { attributes = {...attributes, ...mapPsyMinor(minor)} }) data?.psykana?.notes?.forEach( note => { attributes = {...attributes, ...mapPsyNote(note)} }) data?.psykana?.powers?.forEach( power => { attributes = {...attributes, ...mapPsyPower(power)} }) console.log('setting attributes'); attributes.import_value = 'Success: Character Imported Successfully\n\nRemember to add the token manually'; setAttrs(attributes, true); }) }) // Update to new version let oldVariables = [ 'gear_carry','gear_name','gear_crafts', 'gear_quantity','gear_avail','gear_weight','gear_pg#','gear_desc' ] on('sheet:opened', () => { getAttrs(oldVariables, values => { var attributes = {} let gear = {carry: values['gear_carry'], name: values['gear_name'], craft: values['gear_crafts'], quantity: values['gear_quantity'], avail: values['gear_avail'], weight: values['gear_weight'], desc: values['gear_desc']} if (!gear.name == '') attributes = {...attributes, ...mapGear(gear)} console.table(gear) console.log(attributes) }) }) // functions const mapInfo = (data) => { let attr = {}; attr['character_name'] = data.name || ''; attr['Homeworld'] = data.homeworld || ''; attr['Background'] = data.background || ''; attr['Role'] = data.role || ''; attr['EliteAdvances'] = data.eliteAdvances || ''; attr['Notes'] = data.note || ''; attr['Player'] = data.player || ''; attr['appearance'] = data.appearance || ''; attr['Quirks'] = data.quirks || ''; attr['Divination'] = data.divination || ''; attr['Superstition'] = data.superstition || ''; attr['size'] = data.size || ''; attr['xp_total'] = data.xp || ''; attr['agi_mov_mod'] = data.movementModifier || ''; return attr; }; const mapStat = (statName, stat) => { let attr = {}; attr[`${statName}`] = stat.base || 0; attr[`${statName}_advancement`] = stat.advancement || 0; attr[`${statName}_mod`] = stat.modifier || 0; attr[`${statName}_unnat`] = stat.unnatural || 0; return attr; }; const mapWounds = (wounds) => { let attr = {}; attr['wounds'] = wounds.current || ''; attr['wounds_max'] = wounds.max || ''; attr['head'] = wounds.critical?.head || ''; attr['body'] = wounds.critical?.body || ''; attr['arm_left'] = wounds.critical?.arm_left || ''; attr['arm_right'] = wounds.critical?.arm_right || ''; attr['leg_left'] = wounds.critical?.leg_left || ''; attr['leg_right'] = wounds.critical?.leg_right || ''; return attr; }; const mapFate = (fate) => { let attr = {}; attr['fate'] = fate.current || ''; attr['fate_max'] = fate.max || ''; attr['settings_fate'] = fate.setting || ''; return attr; }; const mapFatigue = (fatigue) => { let attr = {}; attr['fatigue'] = fatigue.current || ''; attr['fatigue_mod'] = fatigue.modifier || ''; return attr; }; const mapForceField = (forcefield) => { let attr = {}; attr['forcefield_name'] = forcefield.name || ''; attr['forcefield_protection'] = forcefield.protection || ''; attr['forcefield_overload'] = forcefield.overload || ''; return attr; }; const mapCondition = (condition) => { if (!condition) return ; let attr = {}, section = 'condition', id = generateRowID(); attr[`repeating_${section}_${id}_condition_r`] = condition.name || ''; attr[`repeating_${section}_${id}_condition_desc_r`] = condition.description || ''; return attr; }; const mapSkill = (skill) => { if (!skill) return ; let attr = {} attr[`mod2_${skill.name}`] = skill.modifier || 0; attr[`mod3_${skill.name}`] = skill.level || 0; return attr; }; const mapRepeatingSkill = (skill, section) => { if (!skill) return ; let attr = {}, id = generateRowID(); attr[`repeating_${section}_${id}_${section}_name`] = skill.name || ''; attr[`repeating_${section}_${id}_${section}_r_mod2`] = skill.modifier || ''; attr[`repeating_${section}_${id}_${section}_r_mod3`] = skill.level || ''; return attr; }; const mapAbility = (ability) => { if (!ability) return ; let attr = {}, section = 'abilities', id = generateRowID(); attr[`repeating_${section}_${id}_ability_r`] = ability.name || ''; attr[`repeating_${section}_${id}_ability_desc_r`] = ability.description || ''; return attr; }; const mapTrait = (trait) => { if (!trait) return ; let attr = {}, section = 'traits', id = generateRowID(); attr[`repeating_${section}_${id}_trait_r`] = trait.name || ''; attr[`repeating_${section}_${id}_trait_desc_r`] = trait.description || ''; return attr; } const mapAptitudes = (aptitudes) => { var attr = {}; var section = 'aptitudes'; for (var i = 0; i < aptitudes?.length; i += 3) { let id = generateRowID(); attr[`repeating_${section}_${id}_aptitude_nameL_r`] = aptitudes[i] || ''; attr[`repeating_${section}_${id}_aptitude_nameM_r`] = aptitudes[i+1] || ''; attr[`repeating_${section}_${id}_aptitude_nameR_r`] = aptitudes[i+2] || ''; } return attr; }; const mapRangedWeapon = (weapon, vehicle) => { if (!weapon) return ; let attr = {}, section = (vehicle) ? 'vehiclerangedweapons' : 'rangedweapons', id = generateRowID(); if (vehicle) id += '_V'; attr[`repeating_${section}_${id}_R_weapon_name`] = weapon.name || ''; attr[`repeating_${section}_${id}_R_weapon_class`] = weapon.class || ''; attr[`repeating_${section}_${id}_R_weapon_range`] = weapon.range || ''; attr[`repeating_${section}_${id}_R_weapon_dam-roll`] = weapon.damage || ''; attr[`repeating_${section}_${id}_R_weapon_pen`] = weapon.pen || ''; attr[`repeating_${section}_${id}_R_weapon_type`] = weapon.type || ''; attr[`repeating_${section}_${id}_R_weapon_max`] = weapon.max || ''; attr[`repeating_${section}_${id}_R_weapon_min`] = weapon.min || ''; attr[`repeating_${section}_${id}_R_weapon_vengeful`] = weapon.vengeful || ''; attr[`repeating_${section}_${id}_R_weapon_accurate`] = weapon.accurate || ''; attr[`repeating_${section}_${id}_R_weapon_burst`] = weapon.burst || ''; attr[`repeating_${section}_${id}_R_weapon_auto`] = weapon.auto || ''; attr[`repeating_${section}_${id}_R_weapon_mag`] = weapon.mag || ''; attr[`repeating_${section}_${id}_R_weapon_mag_max`] = weapon.mag_max || ''; attr[`repeating_${section}_${id}_R_weapon_trained`] = weapon.trained || ''; attr[`repeating_${section}_${id}_R_weapon_mod1`] = weapon.mod1 || ''; attr[`repeating_${section}_${id}_R_weapon_reload`] = weapon.reload || ''; attr[`repeating_${section}_${id}_R_weapon_weight`] = weapon.weight || ''; attr[`repeating_${section}_${id}_R_weapon_domain`] = weapon.domain || ''; attr[`repeating_${section}_${id}_R_weapon_mishap_chance`] = weapon.mishap_chance || ''; attr[`repeating_${section}_${id}_R_weapon_carry`] = weapon.carry || ''; attr[`repeating_${section}_${id}_R_weapon_ability_r`] = weapon.special || ''; attr[`repeating_${section}_${id}_R_weapon_ability_desc_r`] = weapon.description || ''; return attr; } const mapMeleeWeapon = (weapon, vehicle) => { if (!weapon) return ; let attr = {}, section = (vehicle) ? 'vehiclemeleeweapons' : 'meleeweapons', id = generateRowID(); if (vehicle) id += '_V'; attr[`repeating_${section}_${id}_M_weapon_name`] = weapon.name || ''; attr[`repeating_${section}_${id}_M_weapon_class`] = weapon.class || ''; attr[`repeating_${section}_${id}_M_weapon_reach`] = weapon.reach || ''; attr[`repeating_${section}_${id}_M_weapon_dam-roll`] = weapon.damage || ''; attr[`repeating_${section}_${id}_M_weapon_pen`] = weapon.pen || ''; attr[`repeating_${section}_${id}_M_weapon_type`] = weapon.type || ''; attr[`repeating_${section}_${id}_M_weapon_max`] = weapon.max || ''; attr[`repeating_${section}_${id}_M_weapon_min`] = weapon.min || ''; attr[`repeating_${section}_${id}_M_weapon_vengeful`] = weapon.vengeful || ''; attr[`repeating_${section}_${id}_M_weapon_trained`] = weapon.trained || ''; attr[`repeating_${section}_${id}_M_weapon_mod1`] = weapon.mod1 || ''; attr[`repeating_${section}_${id}_M_weapon_weight`] = weapon.weight || ''; attr[`repeating_${section}_${id}_M_weapon_domain`] = weapon.domain || ''; attr[`repeating_${section}_${id}_M_weapon_carry`] = weapon.carry || ''; attr[`repeating_${section}_${id}_M_weapon_ability_r`] = weapon.special || ''; attr[`repeating_${section}_${id}_M_weapon_ability_desc_r`] = weapon.description || ''; return attr; } const mapGear = (gear) => { if (!gear) return ; let attr = {}, section = 'gear', id = generateRowID(); attr[`repeating_${section}_${id}_gear_carry`] = gear.carry || ''; attr[`repeating_${section}_${id}_gear_name_r`] = gear.name || ''; attr[`repeating_${section}_${id}_gear_crafts_r`] = gear.craftsmanship || ''; attr[`repeating_${section}_${id}_gear_quantity_r`] = gear.quantity || ''; attr[`repeating_${section}_${id}_gear_avail_r`] = gear.availability || ''; attr[`repeating_${section}_${id}_gear_weight_r`] = gear.weight || ''; attr[`repeating_${section}_${id}_ability_desc_r`] = gear.description || ''; return attr; } const mapCybernetic = (cybernetic) => { if (!cybernetic) return ; let attr = {}, section = 'cybernetics', id = generateRowID(); attr[`repeating_${section}_${id}_cybernetic_name_r`] = cybernetic.name || ''; attr[`repeating_${section}_${id}_cybernetic_location_r`] = cybernetic.location || ''; attr[`repeating_${section}_${id}_cybernetic_desc_r`] = cybernetic.description || ''; return attr; } const mapOldArmor = (armor) => { let attr = {}; attr.armor_AGI = armor.maxAgi || ''; attr.armor_limit = armor.limit || ''; attr.armor_head = armor.head || ''; attr.armor_body = armor.body || ''; attr.armor_arm_right = armor.arm_right || ''; attr.armor_arm_left = armor.arm_left || ''; attr.armor_leg_right = armor.leg_right || ''; attr.armor_leg_left = armor.leg_left || ''; attr.armor_name = armor.description || ''; return attr; } const mapAdvance = (advance) => { var attr = {}; var section = 'advancements'; for (var i = 0; i < advance?.length; i += 2) { let id = generateRowID(); attr[`repeating_${section}_${id}_advancement_nameL_r`] = advance[i]?.name || ''; attr[`repeating_${section}_${id}_advancement_costL_r`] = advance[i]?.cost || ''; attr[`repeating_${section}_${id}_advancement_nameR_r`] = advance[i+1]?.name || ''; attr[`repeating_${section}_${id}_advancement_costR_r`] = advance[i+1]?.cost || ''; } return attr; }; const mapDisorder = (disorder) => { if (!disorder) return; let attr = {}, section = 'disorders', id = generateRowID(); attr[`repeating_${section}_${id}_disorder_r`] = disorder.name || ''; attr[`repeating_${section}_${id}_disorder_desc_r`] = disorder.description || ''; return attr; } const mapMutation = (mutation) => { if (!mutation) return; let attr = {}, section = 'mutations', id = generateRowID(); attr[`repeating_${section}_${id}_mutation_r`] = mutation.name || ''; attr[`repeating_${section}_${id}_mutation_desc_r`] = mutation.description || ''; return attr; } const mapObjective = (objective) => { if (!objective) return; let attr = {}, section = 'objectives', id = generateRowID(); attr[`repeating_${section}_${id}_objective_r`] = objective.name || ''; attr[`repeating_${section}_${id}_objective_completed`] = objective.complete || ''; return attr; } const mapNote = (note) => { if (!note) return; let attr = {}, section = 'notes', id = generateRowID(); attr[`repeating_${section}_${id}_notes`] = note.name || ''; return attr; } const mapVehicle = (vehicle) => { if (!vehicle) return; let attr = {}, section = 'vehicles', id = generateRowID(); attr[`repeating_${section}_${id}_vehicle-name`] = vehicle.name || ''; attr[`repeating_${section}_${id}_vehicle-size`] = vehicle.size || ''; attr[`repeating_${section}_${id}_vehicle-world`] = vehicle.world || ''; attr[`repeating_${section}_${id}_vehicle-type`] = vehicle.type || ''; attr[`repeating_${section}_${id}_vehicle-tactical`] = vehicle.tactical || ''; attr[`repeating_${section}_${id}_vehicle-cruising`] = vehicle.cruising || ''; attr[`repeating_${section}_${id}_vehicle-maneuver`] = vehicle.maneuver || ''; attr[`repeating_${section}_${id}_vehicle-crew`] = vehicle.crew || ''; attr[`repeating_${section}_${id}_vehicle-compliment`] = vehicle.compliment || ''; attr[`repeating_${section}_${id}_vehicle-integrity`] = vehicle.integrity || ''; attr[`repeating_${section}_${id}_vehicle-integrity_max`] = vehicle.integrity_max || ''; attr[`repeating_${section}_${id}_vehicle-armor-front`] = vehicle['armor-front'] || ''; attr[`repeating_${section}_${id}_vehicle-armor-side`] = vehicle['armor-side'] || ''; attr[`repeating_${section}_${id}_vehicle-armor-rear`] = vehicle['armor-rear'] || ''; attr[`repeating_${section}_${id}_armaments`] = vehicle.armaments || ''; attr[`repeating_${section}_${id}_armaments_desc`] = vehicle.armaments_desc || ''; attr[`repeating_${section}_${id}_vehicle_traits`] = vehicle.traits || ''; attr[`repeating_${section}_${id}_vehicle_traits_desc`] = vehicle.traits_desc || ''; attr[`repeating_${section}_${id}_modifications`] = vehicle.modifications || ''; attr[`repeating_${section}_${id}_modifications_desc`] = vehicle.modifications_desc || ''; return attr; } const mapPsykana = (psykana) => { let attr = {}; attr[`psykana_class`] = psykana.class || ''; attr[`psykana_limit`] = psykana.limit || ''; attr[`psy_rating`] = psykana.rating || ''; attr[`psy_push`] = psykana.push || ''; attr[`psykana_sustain`] = psykana.sustain || ''; attr[`focus_mod`] = psykana.focus_mod || ''; attr[`psykana_sustain_count`] = psykana.sustain_count || ''; return attr; }; const mapPsyMinor = (minor) => { if (!minor) return let attr = {}, section = 'minormanifestation', id = generateRowID(); attr[`repeating_${section}_${id}_P_minor_r`] = minor.name || ''; attr[`repeating_${section}_${id}_P_minor_desc_r`] = minor.description || ''; return attr; } const mapPsyNote = (note) => { if (!note) return let attr = {}, section = 'psynotes', id = generateRowID(); attr[`repeating_${section}_${id}_P_note_r`] = note.name || ''; attr[`repeating_${section}_${id}_P_note_desc_r`] = note.description || ''; return attr; } const mapPsyPower = (power) => { if (!power) return; let attr = {}, section = 'psychicabilities', id = generateRowID(); attr[`repeating_${section}_${id}_P_name`] = power.name || ''; attr[`repeating_${section}_${id}_P_subtype`] = power.subtype || ''; attr[`repeating_${section}_${id}_P_range`] = power.range || ''; attr[`repeating_${section}_${id}_P_dam-roll`] = power.damage || ''; attr[`repeating_${section}_${id}_P_type`] = power.type || ''; attr[`repeating_${section}_${id}_P_pen`] = power.pen || ''; attr[`repeating_${section}_${id}_P_focus`] = power.focus || ''; attr[`repeating_${section}_${id}_P_mod1`] = power.mod || ''; attr[`repeating_${section}_${id}_P_multi`] = power.multi || ''; attr[`repeating_${section}_${id}_P_action`] = power.action || ''; attr[`repeating_${section}_${id}_P_sustained`] = power.sustained || ''; attr[`repeating_${section}_${id}_P_hitloc`] = power.hitloc || ''; attr[`repeating_${section}_${id}_P_ability_r`] = power.ability || ''; attr[`repeating_${section}_${id}_P_desc_r`] = power.desc || ''; return attr; }