diff --git a/ShadowbaneInquisition/Shadowbane.html b/ShadowbaneInquisition/Shadowbane.html
index 0ecc02ec9e..a721a4599f 100644
--- a/ShadowbaneInquisition/Shadowbane.html
+++ b/ShadowbaneInquisition/Shadowbane.html
@@ -3968,1310 +3968,1276 @@ NOTE: replace worker by javascript for IDE functionality
-->
diff --git a/ShadowbaneInquisition/SheetWorker.js b/ShadowbaneInquisition/SheetWorker.js
new file mode 100644
index 0000000000..36fdc181b7
--- /dev/null
+++ b/ShadowbaneInquisition/SheetWorker.js
@@ -0,0 +1,1271 @@
+ // 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;
+ }
\ No newline at end of file