diff --git a/BattleTech-A-Time-of-War/BattleTech-A-Time-of-War.html b/BattleTech-A-Time-of-War/BattleTech-A-Time-of-War.html index 008ca3305c..6d8c7101e9 100644 --- a/BattleTech-A-Time-of-War/BattleTech-A-Time-of-War.html +++ b/BattleTech-A-Time-of-War/BattleTech-A-Time-of-War.html @@ -517,6 +517,9 @@

Skills

+ Fast Learner + Slow Learner + None
Skill Lvl @@ -763,8 +766,10 @@ on("sheet:opened", () => { sheetMigration(); - getAttrs(["btatow_sheet_version"], values => { - if (values["btatow_sheet_version"] >= 3) { + getAttrs(["btatow_sheet_version"], ({ + btatow_sheet_version + }) => { + if (btatow_sheet_version >= 3) { recalculateSkills(); } }); @@ -775,7 +780,7 @@ const recalculateSkills = () => { ids.forEach(id => getAttrs(["repeating_skills_" + id + "_skill_xp"], values => { skillXPChanged({ newValue: values["repeating_skills_" + id + "_skill_xp"], - id: id + id }) })); }); @@ -800,57 +805,67 @@ const CHARISMA_LINK = "charisma_link"; const EDGE_LINK = "edge_link"; // calculate stat values when XP amount changes -on("change:strength_xp change:body_xp change:reflex_xp change:dexterity_xp change:intelligence_xp change:will_xp change:charisma_xp change:edge_xp", eventInfo => { - attributeName = eventInfo.sourceAttribute; - - attributeValue = Math.floor(eventInfo.newValue/100); +on("change:strength_xp change:body_xp change:reflex_xp change:dexterity_xp change:intelligence_xp change:will_xp change:charisma_xp change:edge_xp", ({ + sourceAttribute, + newValue +}) => { + attributeValue = Math.floor(newValue / 100); attributeLinkedValue = calculateLinkedAttributeValue(attributeValue); let attributesToSet = {}; - switch (attributeName) { - case STRENGTH_XP: attributesToSet = { - strength: attributeValue, - strength_link: attributeLinkedValue - }; - break; - case BODY_XP: attributesToSet = { - body: attributeValue, - body_link: attributeLinkedValue - }; - break; - case REFLEX_XP: attributesToSet = { - reflex: attributeValue, - reflex_link: attributeLinkedValue - }; - break; - case DEXTERITY_XP: attributesToSet = { - dexterity: attributeValue, - dexterity_link: attributeLinkedValue - }; - break; - case INTELLIGENCE_XP: attributesToSet = { - intelligence: attributeValue, - intelligence_link: attributeLinkedValue - }; - break; - case WILL_XP: attributesToSet = { - will: attributeValue, - will_link: attributeLinkedValue - }; - break; - case CHARISMA_XP: attributesToSet = { - charisma: attributeValue, - charisma_link: attributeLinkedValue - }; - break; - case EDGE_XP: attributesToSet = { - edge: attributeValue, - edge_link: attributeLinkedValue - }; - break; - default: console.error("unknown attribute type passed in to basic attribute on change function"); - return; + switch (sourceAttribute) { + case STRENGTH_XP: + attributesToSet = { + strength: attributeValue, + strength_link: attributeLinkedValue + }; + break; + case BODY_XP: + attributesToSet = { + body: attributeValue, + body_link: attributeLinkedValue + }; + break; + case REFLEX_XP: + attributesToSet = { + reflex: attributeValue, + reflex_link: attributeLinkedValue + }; + break; + case DEXTERITY_XP: + attributesToSet = { + dexterity: attributeValue, + dexterity_link: attributeLinkedValue + }; + break; + case INTELLIGENCE_XP: + attributesToSet = { + intelligence: attributeValue, + intelligence_link: attributeLinkedValue + }; + break; + case WILL_XP: + attributesToSet = { + will: attributeValue, + will_link: attributeLinkedValue + }; + break; + case CHARISMA_XP: + attributesToSet = { + charisma: attributeValue, + charisma_link: attributeLinkedValue + }; + break; + case EDGE_XP: + attributesToSet = { + edge: attributeValue, + edge_link: attributeLinkedValue + }; + break; + default: + console.error("unknown attribute type passed in to basic attribute on change function"); + return; } setAttrs(attributesToSet, {}, recalculateSkills) @@ -858,7 +873,7 @@ on("change:strength_xp change:body_xp change:reflex_xp change:dexterity_xp chang function calculateLinkedAttributeValue(attribute) { if (attribute > 10) { - return Math.floor(attribute/3); + return Math.floor(attribute / 3); } else { if (attribute < 1) return -4; @@ -875,16 +890,26 @@ function calculateLinkedAttributeValue(attribute) { } } -on("change:repeating_skills:skill change:repeating_skills:sub_skill", eventInfo => { - getAttrs(["repeating_skills_skill_xp"], values => { - skillXPChanged({newValue: values["repeating_skills_skill_xp"]}); +on("change:repeating_skills:skill change:repeating_skills:sub_skill", _ => { + getAttrs(["repeating_skills_skill_xp"], ({ + repeating_skills_skill_xp + }) => { + skillXPChanged({ + newValue: repeating_skills_skill_xp + }); }); }); // calculate skill modifiers when skill xp changes -on("change:repeating_skills:skill_xp", skillXPChanged); +on("change:repeating_skills:skill_xp", skillXPChanged) -function skillXPChanged(eventInfo) { +// recalculate all skills when learning speed modifier changes +on("change:learning_speed", recalculateSkills) + +function skillXPChanged({ + id, + newValue +}) { const tieredSkills = [ "art", "computers", @@ -896,20 +921,20 @@ function skillXPChanged(eventInfo) { // When the sheet is opened, or data migrations are run, the skills are retrieved via ID, and thus must have their // ID included in the attribute name when setting attributes. - const id = eventInfo.id !== undefined ? eventInfo.id + "_" : "" + const skillId = id !== undefined ? id + "_" : "" - const skill = "repeating_skills_" + id + "skill"; - const skillLevel = "repeating_skills_" + id + "skill_level"; - const subSkillAttrName = "repeating_skills_" + id + "sub_skill"; + const skill = "repeating_skills_" + skillId + "skill"; + const skillLevel = "repeating_skills_" + skillId + "skill_level"; + const subSkillAttrName = "repeating_skills_" + skillId + "sub_skill"; - getAttrs([skill, subSkillAttrName], values => { + getAttrs([skill, subSkillAttrName, "learning_speed"], values => { const skillName = values[skill] const skillNameLookupKey = skillName.split("_")[0] const subSkill = values[subSkillAttrName] let skillAttributesToSet = {}; - skillAttributesToSet[skillLevel] = calculateSkillLevel(Number(eventInfo.newValue), 0); + skillAttributesToSet[skillLevel] = calculateSkillLevel(Number(newValue), values.learning_speed); const skillData = getSkill( skillNameLookupKey, @@ -917,22 +942,22 @@ function skillXPChanged(eventInfo) { tieredSkills.includes(skillNameLookupKey) ); - skillAttributesToSet["repeating_skills_" + id + "skill_tnc"] = skillData.targetNumber + "/" + skillData.complexity; - skillAttributesToSet["repeating_skills_" + id + "target_number"] = skillData.targetNumber; + skillAttributesToSet["repeating_skills_" + skillId + "skill_tnc"] = skillData.targetNumber + "/" + skillData.complexity; + skillAttributesToSet["repeating_skills_" + skillId + "target_number"] = skillData.targetNumber; getAttrs(skillData.linkedAttributes, values => { - skillAttributesToSet["repeating_skills_" + id + "skill_modifier"] = Object.values(values) + skillAttributesToSet["repeating_skills_" + skillId + "skill_modifier"] = Object.values(values) .reduce((total, num) => Number(total) + Number(num)) + Number(skillAttributesToSet[skillLevel]); - skillAttributesToSet["repeating_skills_" + id + "skill_linked_attributes"] = skillData.linkedAttributes + skillAttributesToSet["repeating_skills_" + skillId + "skill_linked_attributes"] = skillData.linkedAttributes .map(linkAtr => getLinkedAttributeDisplayName(linkAtr) + " (" + values[linkAtr] + ")") .join(" + "); - skillAttributesToSet["repeating_skills_" + id + "skill_roll_value"] = + skillAttributesToSet["repeating_skills_" + skillId + "skill_roll_value"] = getSkillDisplayName(skillData, skillName, subSkill) + " Skill Check:\n" + "Margin of Success: [[2d6 + @{skill_modifier}[MOD] - @{target_number}[TN]]]"; - setAttrs(skillAttributesToSet); + setAttrs(skillAttributesToSet, {}, () => {}); }); }); } @@ -977,7 +1002,7 @@ function getSkillDisplayName(skill, skillDisplayNameKey, subSkill) { const skillsList = { "acrobatics": { - "linkedAttributes": [ REFLEX_LINK ], + "linkedAttributes": [REFLEX_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -987,7 +1012,7 @@ const skillsList = { } }, "acting": { - "linkedAttributes": [ CHARISMA_LINK ], + "linkedAttributes": [CHARISMA_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": false, @@ -996,7 +1021,7 @@ const skillsList = { } }, "administration": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1005,7 +1030,7 @@ const skillsList = { } }, "animal": { - "linkedAttributes": [ WILL_LINK ], + "linkedAttributes": [WILL_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1016,7 +1041,7 @@ const skillsList = { } }, "appraisal": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": false, @@ -1025,7 +1050,7 @@ const skillsList = { } }, "archery": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1034,7 +1059,7 @@ const skillsList = { } }, "artillery": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1043,7 +1068,7 @@ const skillsList = { } }, "career": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": true, @@ -1052,7 +1077,7 @@ const skillsList = { } }, "climbing": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1061,7 +1086,7 @@ const skillsList = { } }, "communications": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1072,7 +1097,7 @@ const skillsList = { } }, "cryptography": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1081,7 +1106,7 @@ const skillsList = { } }, "demolitions": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1090,7 +1115,7 @@ const skillsList = { } }, "disguise": { - "linkedAttributes": [ CHARISMA_LINK ], + "linkedAttributes": [CHARISMA_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1099,7 +1124,7 @@ const skillsList = { } }, "driving": { - "linkedAttributes": [ REFLEX_LINK, DEXTERITY_LINK ], + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1110,7 +1135,7 @@ const skillsList = { } }, "escape": { - "linkedAttributes": [ STRENGTH_LINK, DEXTERITY_LINK ], + "linkedAttributes": [STRENGTH_LINK, DEXTERITY_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1119,7 +1144,7 @@ const skillsList = { } }, "forgery": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1128,7 +1153,7 @@ const skillsList = { } }, "gunnery": { - "linkedAttributes": [ REFLEX_LINK, DEXTERITY_LINK ], + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1145,7 +1170,7 @@ const skillsList = { } }, "interrogation": { - "linkedAttributes": [ WILL_LINK, CHARISMA_LINK ], + "linkedAttributes": [WILL_LINK, CHARISMA_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1154,7 +1179,7 @@ const skillsList = { } }, "investigation": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1163,7 +1188,7 @@ const skillsList = { } }, "language": { - "linkedAttributes": [ INTELLIGENCE_LINK, CHARISMA_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, CHARISMA_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": true, @@ -1172,7 +1197,7 @@ const skillsList = { } }, "leadership": { - "linkedAttributes": [ WILL_LINK, CHARISMA_LINK ], + "linkedAttributes": [WILL_LINK, CHARISMA_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1181,7 +1206,7 @@ const skillsList = { } }, "medtech": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1191,7 +1216,7 @@ const skillsList = { } }, "navigation": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1204,7 +1229,7 @@ const skillsList = { } }, "negotiation": { - "linkedAttributes": [ CHARISMA_LINK ], + "linkedAttributes": [CHARISMA_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": false, @@ -1213,7 +1238,7 @@ const skillsList = { } }, "perception": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1222,7 +1247,7 @@ const skillsList = { } }, "piloting": { - "linkedAttributes": [ REFLEX_LINK, DEXTERITY_LINK ], + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1239,7 +1264,7 @@ const skillsList = { } }, "protocol": { - "linkedAttributes": [ WILL_LINK, CHARISMA_LINK ], + "linkedAttributes": [WILL_LINK, CHARISMA_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": true, @@ -1248,7 +1273,7 @@ const skillsList = { } }, "running": { - "linkedAttributes": [ REFLEX_LINK ], + "linkedAttributes": [REFLEX_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1257,7 +1282,7 @@ const skillsList = { } }, "science": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": true, @@ -1266,7 +1291,7 @@ const skillsList = { } }, "security": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1276,7 +1301,7 @@ const skillsList = { } }, "sensor": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1285,7 +1310,7 @@ const skillsList = { } }, "small": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1294,7 +1319,7 @@ const skillsList = { } }, "stealth": { - "linkedAttributes": [ REFLEX_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [REFLEX_LINK, INTELLIGENCE_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1303,7 +1328,7 @@ const skillsList = { } }, "strategy": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1312,7 +1337,7 @@ const skillsList = { } }, "streetwise": { - "linkedAttributes": [ CHARISMA_LINK ], + "linkedAttributes": [CHARISMA_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": true, @@ -1321,7 +1346,7 @@ const skillsList = { } }, "support": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1330,7 +1355,7 @@ const skillsList = { } }, "surgery": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1340,7 +1365,7 @@ const skillsList = { } }, "survival": { - "linkedAttributes": [ BODY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [BODY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": true, @@ -1349,7 +1374,7 @@ const skillsList = { } }, "swimming": { - "linkedAttributes": [ STRENGTH_LINK ], + "linkedAttributes": [STRENGTH_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1358,7 +1383,7 @@ const skillsList = { } }, "tactics": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1371,7 +1396,7 @@ const skillsList = { } }, "technician": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1387,7 +1412,7 @@ const skillsList = { } }, "thrown": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1397,7 +1422,7 @@ const skillsList = { } }, "tracking": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1407,7 +1432,7 @@ const skillsList = { } }, "training": { - "linkedAttributes": [ INTELLIGENCE_LINK, CHARISMA_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, CHARISMA_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1416,7 +1441,7 @@ const skillsList = { } }, "zero": { - "linkedAttributes": [ REFLEX_LINK ], + "linkedAttributes": [REFLEX_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1428,7 +1453,7 @@ const skillsList = { const tieredSkillsBasic = { "art": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": true, @@ -1437,7 +1462,7 @@ const tieredSkillsBasic = { } }, "computers": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": false, @@ -1446,7 +1471,7 @@ const tieredSkillsBasic = { } }, "interest": { - "linkedAttributes": [ INTELLIGENCE_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK], "targetNumber": 8, "complexity": "CB", "subSkills": true, @@ -1455,7 +1480,7 @@ const tieredSkillsBasic = { } }, "martial": { - "linkedAttributes": [ REFLEX_LINK ], + "linkedAttributes": [REFLEX_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1464,7 +1489,7 @@ const tieredSkillsBasic = { } }, "melee": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1473,7 +1498,7 @@ const tieredSkillsBasic = { } }, "prestidigitation": { - "linkedAttributes": [ DEXTERITY_LINK ], + "linkedAttributes": [DEXTERITY_LINK], "targetNumber": 7, "complexity": "SB", "subSkills": false, @@ -1487,7 +1512,7 @@ const tieredSkillsBasic = { const tieredSkillsAdvanced = { "art": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": true, @@ -1496,7 +1521,7 @@ const tieredSkillsAdvanced = { } }, "computers": { - "linkedAttributes": [ DEXTERITY_LINK, INTELLIGENCE_LINK ], + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": false, @@ -1505,7 +1530,7 @@ const tieredSkillsAdvanced = { } }, "interest": { - "linkedAttributes": [ INTELLIGENCE_LINK, WILL_LINK ], + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], "targetNumber": 9, "complexity": "CA", "subSkills": true, @@ -1514,7 +1539,7 @@ const tieredSkillsAdvanced = { } }, "martial": { - "linkedAttributes": [ REFLEX_LINK, DEXTERITY_LINK ], + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1523,7 +1548,7 @@ const tieredSkillsAdvanced = { } }, "melee": { - "linkedAttributes": [ REFLEX_LINK, DEXTERITY_LINK ], + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1532,7 +1557,7 @@ const tieredSkillsAdvanced = { } }, "prestidigitation": { - "linkedAttributes": [ REFLEX_LINK, DEXTERITY_LINK ], + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], "targetNumber": 8, "complexity": "SA", "subSkills": false, @@ -1592,15 +1617,15 @@ function sheetMigration() { return; } - switch(sheetVersion) { - case 1: - migrateFrom1To2(); - break; - case 2: - migrateFrom2To3(); - break; - default: - break; + switch (sheetVersion) { + case 1: + migrateFrom1To2(); + break; + case 2: + migrateFrom2To3(); + break; + default: + break; } }); } @@ -1641,7 +1666,7 @@ function migrateFrom1To2() { } } - console.debug("New attributes: "+ JSON.stringify(newAttributes)); + console.debug("New attributes: " + JSON.stringify(newAttributes)); setAttrs(newAttributes, {}, () => { setAttrs({ @@ -1650,7 +1675,7 @@ function migrateFrom1To2() { console.log("Sheet data migration completed"); }); }); - }); + }); } // Migrate skills data from version 2 to version 3 @@ -1736,15 +1761,17 @@ function migrateFrom2To3() { newSkillValues[newSkillDataNames.skill] = skillName; if (displaySubSkill && oldSkillSubSkillName !== undefined) { - newSkillValues[newSkillDataNames.subSkill] = oldSkillSubSkillName; - } + newSkillValues[newSkillDataNames.subSkill] = oldSkillSubSkillName; + } newSkillValues[newSkillDataNames.xp] = oldSkillValues[oldSkillDataNames.xp]; setAttrs(newSkillValues, {}, () => { skillsUpdated++; if (skillsUpdated === ids.length) { - setAttrs({btatow_sheet_version: 3}, {}, () => { + setAttrs({ + btatow_sheet_version: 3 + }, {}, () => { console.log("Sheet migrated from version 2 to version 3."); recalculateSkills(); }); diff --git a/BattleTech-A-Time-of-War/development/.eslintrc b/BattleTech-A-Time-of-War/development/.eslintrc new file mode 100644 index 0000000000..597ff9f5f9 --- /dev/null +++ b/BattleTech-A-Time-of-War/development/.eslintrc @@ -0,0 +1,10 @@ +{ + "extends": "eslint:recommended", + "env": { + "es6": true + }, + "parserOptions": { + "ecmaVersion": 6 + }, + "rules": {} +} \ No newline at end of file diff --git a/BattleTech-A-Time-of-War/development/.jsbeautifyrc b/BattleTech-A-Time-of-War/development/.jsbeautifyrc new file mode 100644 index 0000000000..9c3d8bfcbd --- /dev/null +++ b/BattleTech-A-Time-of-War/development/.jsbeautifyrc @@ -0,0 +1,3 @@ +{ + "jslint_happy": true +} \ No newline at end of file diff --git a/BattleTech-A-Time-of-War/development/sheet-worker.js b/BattleTech-A-Time-of-War/development/sheet-worker.js new file mode 100644 index 0000000000..a4c0f5f700 --- /dev/null +++ b/BattleTech-A-Time-of-War/development/sheet-worker.js @@ -0,0 +1,1019 @@ +// run any data migrations +on("sheet:opened", () => { + sheetMigration(); + + getAttrs(["btatow_sheet_version"], ({ + btatow_sheet_version + }) => { + if (btatow_sheet_version >= 3) { + recalculateSkills(); + } + }); +}); + +const recalculateSkills = () => { + getSectionIDs("skills", ids => { + ids.forEach(id => getAttrs(["repeating_skills_" + id + "_skill_xp"], values => { + skillXPChanged({ + newValue: values["repeating_skills_" + id + "_skill_xp"], + id + }) + })); + }); +} + +const STRENGTH_XP = "strength_xp"; +const BODY_XP = "body_xp"; +const REFLEX_XP = "reflex_xp"; +const DEXTERITY_XP = "dexterity_xp"; +const INTELLIGENCE_XP = "intelligence_xp"; +const WILL_XP = "will_xp"; +const CHARISMA_XP = "charisma_xp"; +const EDGE_XP = "edge_xp"; + +const STRENGTH_LINK = "strength_link"; +const BODY_LINK = "body_link"; +const REFLEX_LINK = "reflex_link"; +const DEXTERITY_LINK = "dexterity_link"; +const INTELLIGENCE_LINK = "intelligence_link"; +const WILL_LINK = "will_link"; +const CHARISMA_LINK = "charisma_link"; +const EDGE_LINK = "edge_link"; + +// calculate stat values when XP amount changes +on("change:strength_xp change:body_xp change:reflex_xp change:dexterity_xp change:intelligence_xp change:will_xp change:charisma_xp change:edge_xp", ({ + sourceAttribute, + newValue +}) => { + attributeValue = Math.floor(newValue / 100); + attributeLinkedValue = calculateLinkedAttributeValue(attributeValue); + + let attributesToSet = {}; + + switch (sourceAttribute) { + case STRENGTH_XP: + attributesToSet = { + strength: attributeValue, + strength_link: attributeLinkedValue + }; + break; + case BODY_XP: + attributesToSet = { + body: attributeValue, + body_link: attributeLinkedValue + }; + break; + case REFLEX_XP: + attributesToSet = { + reflex: attributeValue, + reflex_link: attributeLinkedValue + }; + break; + case DEXTERITY_XP: + attributesToSet = { + dexterity: attributeValue, + dexterity_link: attributeLinkedValue + }; + break; + case INTELLIGENCE_XP: + attributesToSet = { + intelligence: attributeValue, + intelligence_link: attributeLinkedValue + }; + break; + case WILL_XP: + attributesToSet = { + will: attributeValue, + will_link: attributeLinkedValue + }; + break; + case CHARISMA_XP: + attributesToSet = { + charisma: attributeValue, + charisma_link: attributeLinkedValue + }; + break; + case EDGE_XP: + attributesToSet = { + edge: attributeValue, + edge_link: attributeLinkedValue + }; + break; + default: + console.error("unknown attribute type passed in to basic attribute on change function"); + return; + } + + setAttrs(attributesToSet, {}, recalculateSkills) +}); + +function calculateLinkedAttributeValue(attribute) { + if (attribute > 10) { + return Math.floor(attribute / 3); + } else { + if (attribute < 1) + return -4; + else if (attribute < 2) + return -2; + else if (attribute < 4) + return -1; + else if (attribute < 7) + return 0; + else if (attribute < 10) + return 1; + else + return 2; + } +} + +on("change:repeating_skills:skill change:repeating_skills:sub_skill", _ => { + getAttrs(["repeating_skills_skill_xp"], ({ + repeating_skills_skill_xp + }) => { + skillXPChanged({ + newValue: repeating_skills_skill_xp + }); + }); +}); + +// calculate skill modifiers when skill xp changes +on("change:repeating_skills:skill_xp", skillXPChanged) + +// recalculate all skills when learning speed modifier changes +on("change:learning_speed", recalculateSkills) + +function skillXPChanged({ + id, + newValue +}) { + const tieredSkills = [ + "art", + "computers", + "interest", + "martial", + "melee", + "prestidigitation" + ]; + + // When the sheet is opened, or data migrations are run, the skills are retrieved via ID, and thus must have their + // ID included in the attribute name when setting attributes. + const skillId = id !== undefined ? id + "_" : "" + + const skill = "repeating_skills_" + skillId + "skill"; + const skillLevel = "repeating_skills_" + skillId + "skill_level"; + const subSkillAttrName = "repeating_skills_" + skillId + "sub_skill"; + + getAttrs([skill, subSkillAttrName, "learning_speed"], values => { + const skillName = values[skill] + const skillNameLookupKey = skillName.split("_")[0] + const subSkill = values[subSkillAttrName] + + let skillAttributesToSet = {}; + + skillAttributesToSet[skillLevel] = calculateSkillLevel(Number(newValue), values.learning_speed); + + const skillData = getSkill( + skillNameLookupKey, + skillAttributesToSet[skillLevel], + tieredSkills.includes(skillNameLookupKey) + ); + + skillAttributesToSet["repeating_skills_" + skillId + "skill_tnc"] = skillData.targetNumber + "/" + skillData.complexity; + skillAttributesToSet["repeating_skills_" + skillId + "target_number"] = skillData.targetNumber; + + getAttrs(skillData.linkedAttributes, values => { + skillAttributesToSet["repeating_skills_" + skillId + "skill_modifier"] = Object.values(values) + .reduce((total, num) => Number(total) + Number(num)) + Number(skillAttributesToSet[skillLevel]); + + skillAttributesToSet["repeating_skills_" + skillId + "skill_linked_attributes"] = skillData.linkedAttributes + .map(linkAtr => getLinkedAttributeDisplayName(linkAtr) + " (" + values[linkAtr] + ")") + .join(" + "); + + skillAttributesToSet["repeating_skills_" + skillId + "skill_roll_value"] = + getSkillDisplayName(skillData, skillName, subSkill) + " Skill Check:\n" + + "Margin of Success: [[2d6 + @{skill_modifier}[MOD] - @{target_number}[TN]]]"; + + setAttrs(skillAttributesToSet, {}, () => {}); + }); + }); +} + +function calculateSkillLevel(skillXP, learningSpeedModifier) { + skillXP = Number(skillXP); + learningSpeedModifier = Number(learningSpeedModifier); + + if (skillXP < (30 + (learningSpeedModifier * 3))) { + return 0; + } + + if (skillXP >= (570 + (learningSpeedModifier * 57))) { + return 10; + } + + let currentSkillXP = skillXP - (20 + (learningSpeedModifier * 2)); + + for (i = 1; i < 11; i++) { + if (currentSkillXP < i * (10 + learningSpeedModifier)) { + return i - 1; + } + + currentSkillXP -= (i * (10 + learningSpeedModifier)); + } + + console.error("should never hit this line, this means the skill XP is not less than level 1, not greater than level 10, but also not within a level range."); + return undefined; +} + +function getSkillDisplayName(skill, skillDisplayNameKey, subSkill) { + if (skill.subSkills === true) { + return skill.displayName[skillDisplayNameKey] + "/" + subSkill; + } + + if (typeof subSkill === "string" && subSkill != "") { + return skill.displayName[skillDisplayNameKey] + " (" + subSkill + ")"; + } + + return skill.displayName[skillDisplayNameKey]; +} + +const skillsList = { + "acrobatics": { + "linkedAttributes": [REFLEX_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "acrobatics_free_fall": "Acrobatics/Free-Fall", + "acrobatics_gymnastics": "Acrobatics/Gymnastics" + } + }, + "acting": { + "linkedAttributes": [CHARISMA_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": false, + "displayName": { + "acting": "Acting" + } + }, + "administration": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "administration": "Administration" + } + }, + "animal": { + "linkedAttributes": [WILL_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "animal_handling_herding": "Animal Handling/Herding", + "animal_handling_riding": "Animal Handling/Riding", + "animal_handling_training": "Animal Handling/Training" + } + }, + "appraisal": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": false, + "displayName": { + "appraisal": "Appraisal" + } + }, + "archery": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "archery": "Archery" + } + }, + "artillery": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "artillery": "Artillery" + } + }, + "career": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": true, + "displayName": { + "career": "Career" + } + }, + "climbing": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "climbing": "Climbing" + } + }, + "communications": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "communications_black_box": "Communications/Black Box", + "communications_conventional_em": "Communications/Conventional (EM)", + "communications_hyperpulse_generator": "Communications/Hyperpulse Generator" + } + }, + "cryptography": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "cryptography": "Cryptography" + } + }, + "demolitions": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "demolitions": "Demolitions" + } + }, + "disguise": { + "linkedAttributes": [CHARISMA_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "disguise": "Disguise" + } + }, + "driving": { + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "driving_ground_vehicles": "Driving/Ground Vehicles", + "driving_rail_vehicles": "Driving/Rail Vehicles", + "driving_sea_vehicles": "Driving/Sea Vehicles" + } + }, + "escape": { + "linkedAttributes": [STRENGTH_LINK, DEXTERITY_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "escape_artist": "Escape Artist" + } + }, + "forgery": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "forgery": "Forgery" + } + }, + "gunnery": { + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "gunnery_aerospace": "Gunnery/Aerospace", + "gunnery_air_vehicle": "Gunnery/Air Vehicle", + "gunnery_battlesuit": "Gunnery/Battlesuit", + "gunnery_ground_vehicle": "Gunnery/Ground Vehicle", + "gunnery_mech": "Gunnery/'Mech", + "gunnery_protomech": "Gunnery/ProtoMech", + "gunnery_sea_vehicle": "Gunnery/Sea Vehicle", + "gunnery_spacecraft": "Gunnery/Spacecraft", + "gunnery_turret": "Gunnery/Turret" + } + }, + "interrogation": { + "linkedAttributes": [WILL_LINK, CHARISMA_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "interrogation": "Interrogation" + } + }, + "investigation": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "investigation": "Investigation" + } + }, + "language": { + "linkedAttributes": [INTELLIGENCE_LINK, CHARISMA_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": true, + "displayName": { + "language": "Language" + } + }, + "leadership": { + "linkedAttributes": [WILL_LINK, CHARISMA_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "leadership": "Leadership" + } + }, + "medtech": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "medtech_general": "MedTech/General", + "medtech_veterinary": "MedTech/Veterinary" + } + }, + "navigation": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "navigation_air": "Navigation/Air", + "navigation_ground": "Navigation/Ground", + "navigation_kf_jump": "Navigation/K-F Jump", + "navigation_sea": "Navigation/Sea", + "navigation_space": "Navigation/Space" + } + }, + "negotiation": { + "linkedAttributes": [CHARISMA_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": false, + "displayName": { + "negotiation": "Negotiation" + } + }, + "perception": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "perception": "Perception" + } + }, + "piloting": { + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "piloting_aerospace": "Piloting/Aerospace", + "piloting_air_vehicle": "Piloting/Air Vehicle", + "piloting_battlesuit": "Piloting/Battlesuit", + "piloting_ground_vehicle": "Piloting/Ground Vehicle", + "piloting_mech": "Piloting/'Mech", + "piloting_protomech": "Piloting/ProtoMech", + "piloting_rail_vehicle": "Piloting/Rail Vehicle", + "piloting_sea_vehicle": "Piloting/Sea Vehicle", + "piloting_spacecraft": "Piloting/Spacecraft" + } + }, + "protocol": { + "linkedAttributes": [WILL_LINK, CHARISMA_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": true, + "displayName": { + "protocol": "Protocol" + } + }, + "running": { + "linkedAttributes": [REFLEX_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "running": "Running" + } + }, + "science": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": true, + "displayName": { + "science": "Science" + } + }, + "security": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "security_systems_electronic": "Security Systems/Electronic", + "security_systems_mechanical": "Security Systems/Mechanical" + } + }, + "sensor": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "sensor_operations": "Sensor Operations" + } + }, + "small": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "small_arms": "Small Arms" + } + }, + "stealth": { + "linkedAttributes": [REFLEX_LINK, INTELLIGENCE_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "stealth": "Stealth" + } + }, + "strategy": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "strategy": "Strategy" + } + }, + "streetwise": { + "linkedAttributes": [CHARISMA_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": true, + "displayName": { + "streetwise": "Streetwise" + } + }, + "support": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "support_weapons": "Support Weapons" + } + }, + "surgery": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "surgery_general": "Surgery/General", + "surgery_veterinary": "Surgery/Veterinary" + } + }, + "survival": { + "linkedAttributes": [BODY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": true, + "displayName": { + "survival": "Survival" + } + }, + "swimming": { + "linkedAttributes": [STRENGTH_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "swimming": "Swimming" + } + }, + "tactics": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "tactics_air": "Tactics/Air", + "tactics_infantry": "Tactics/Infantry", + "tactics_land": "Tactics/Land", + "tactics_sea": "Tactics/Sea", + "tactics_space": "Tactics/Space" + } + }, + "technician": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "technician_aeronautics": "Technician/Aeronautics", + "technician_cybernetics": "Technician/Cybernetics", + "technician_electronic": "Technician/Electronic", + "technician_jets": "Technician/Jets", + "technician_mechanical": "Technician/Mechanical", + "technician_myomer": "Technician/Myomer", + "technician_nuclear": "Technician/Nuclear", + "technician_weapons": "Technician/Weapons" + } + }, + "thrown": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "thrown_weapons_blades": "Thrown Weapons/Blades", + "thrown_weapons_blunt_weapons": "Thrown Weapons/Blunt Weapons" + } + }, + "tracking": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "tracking_urban": "Tracking/Urban", + "tracking_wilds": "Tracking/Wilds" + } + }, + "training": { + "linkedAttributes": [INTELLIGENCE_LINK, CHARISMA_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "training": "Training" + } + }, + "zero": { + "linkedAttributes": [REFLEX_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "zero_g_operations": "Zero-G Operations" + } + } +}; + +const tieredSkillsBasic = { + "art": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": true, + "displayName": { + "art": "Art" + } + }, + "computers": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": false, + "displayName": { + "computers": "Computers" + } + }, + "interest": { + "linkedAttributes": [INTELLIGENCE_LINK], + "targetNumber": 8, + "complexity": "CB", + "subSkills": true, + "displayName": { + "interest": "Interest" + } + }, + "martial": { + "linkedAttributes": [REFLEX_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "martial_arts": "Martial Arts" + } + }, + "melee": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "melee_weapons": "Melee Weapons" + } + }, + "prestidigitation": { + "linkedAttributes": [DEXTERITY_LINK], + "targetNumber": 7, + "complexity": "SB", + "subSkills": false, + "displayName": { + "prestidigitation_pick_pocket": "Prestidigitation/Pick Pocket", + "prestidigitation_quickdraw": "Prestidigitation/Quickdraw", + "prestidigitation_sleight_of_hand": "Prestidigitation/Sleight of Hand" + } + } +}; + +const tieredSkillsAdvanced = { + "art": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": true, + "displayName": { + "art": "Art" + } + }, + "computers": { + "linkedAttributes": [DEXTERITY_LINK, INTELLIGENCE_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": false, + "displayName": { + "computers": "Computers" + } + }, + "interest": { + "linkedAttributes": [INTELLIGENCE_LINK, WILL_LINK], + "targetNumber": 9, + "complexity": "CA", + "subSkills": true, + "displayName": { + "interest": "Interest" + } + }, + "martial": { + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "martial_arts": "Martial Arts" + } + }, + "melee": { + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "melee_weapons": "Melee Weapons" + } + }, + "prestidigitation": { + "linkedAttributes": [REFLEX_LINK, DEXTERITY_LINK], + "targetNumber": 8, + "complexity": "SA", + "subSkills": false, + "displayName": { + "prestidigitation_pick_pocket": "Prestidigitation/Pick Pocket", + "prestidigitation_quickdraw": "Prestidigitation/Quickdraw", + "prestidigitation_sleight_of_hand": "Prestidigitation/Sleight of Hand" + } + } +}; + +function getSkill(skillName, skillLevel, tieredSkill) { + skillName = String(skillName); + skillLevel = Number(skillLevel); + + if (tieredSkill) { + if (skillLevel <= 3) { + return tieredSkillsBasic[skillName] + } else { + return tieredSkillsAdvanced[skillName] + } + } else { + return skillsList[skillName] + } +} + +function getLinkedAttributeDisplayName(linkedAttributeName) { + const linkedAttributeDisplayNames = {}; + linkedAttributeDisplayNames[STRENGTH_LINK] = "STR"; + linkedAttributeDisplayNames[BODY_LINK] = "BOD"; + linkedAttributeDisplayNames[REFLEX_LINK] = "RFL"; + linkedAttributeDisplayNames[DEXTERITY_LINK] = "DEX"; + linkedAttributeDisplayNames[INTELLIGENCE_LINK] = "INT"; + linkedAttributeDisplayNames[WILL_LINK] = "WIL"; + linkedAttributeDisplayNames[CHARISMA_LINK] = "CHA"; + linkedAttributeDisplayNames[EDGE_LINK] = "EDG"; + + return linkedAttributeDisplayNames[linkedAttributeName]; +} + +function sheetMigration() { + console.log("Running sheet data migration"); + + let sheetVersionAttributeName = "btatow_sheet_version"; + let currentVersion = 3; + + getAttrs([sheetVersionAttributeName], sheetVersionAttribute => { + let sheetVersion = Number(sheetVersionAttribute[sheetVersionAttributeName]) + // Check if we should do the migration + if (isNaN(sheetVersion)) { + console.error("No sheet version defined, unable to determine if sheet data needs to be migrated."); + return; + } + + if (sheetVersion === currentVersion) { + console.log("No migration needed"); + return; + } + + switch (sheetVersion) { + case 1: + migrateFrom1To2(); + break; + case 2: + migrateFrom2To3(); + break; + default: + break; + } + }); +} + +// Migrate attribute data from version 1 to version 2 +function migrateFrom1To2() { + const oldAttributeNames = [ + "STRXP", + "BODXP", + "RFLXP", + "DEXXP", + "INTXP", + "WILXP", + "CHAXP", + "EDGXP" + ]; + const newAttributeNames = [ + "strength_xp", + "body_xp", + "reflex_xp", + "dexterity_xp", + "intelligence_xp", + "will_xp", + "charisma_xp", + "edge_xp" + ]; + + let newAttributes = {}; + + console.log("Starting migration from version 1 to version 2."); + + getAttrs(oldAttributeNames, attributes => { + console.debug("Attributes fetched: " + JSON.stringify(attributes)); + for (let i = 0; i < oldAttributeNames.length; i++) { + if (attributes[oldAttributeNames[i]] !== undefined) { + console.debug("Attributes[" + oldAttributeNames[i] + "]: " + attributes[oldAttributeNames[i]]); + newAttributes[newAttributeNames[i]] = attributes[oldAttributeNames[i]]; + } + } + + console.debug("New attributes: " + JSON.stringify(newAttributes)); + + setAttrs(newAttributes, {}, () => { + setAttrs({ + btatow_sheet_version: "2" + }, {}, () => { + console.log("Sheet data migration completed"); + }); + }); + }); +} + +// Migrate skills data from version 2 to version 3 +function migrateFrom2To3() { + console.log("Starting migration from version 2 to version 3."); + + getSectionIDs("skills", ids => { + let skillsUpdated = 0; + + ids.forEach(id => { + const oldSkillDataNames = { + skill: "repeating_skills_" + id + "_skill", + subSkill: "repeating_skills_" + id + "_skillNotes", + xp: "repeating_skills_" + id + "_skillXP" + }; + const newSkillDataNames = { + skill: "repeating_skills_" + id + "_skill", + subSkill: "repeating_skills_" + id + "_sub_skill", + xp: "repeating_skills_" + id + "_skill_xp" + }; + getAttrs(Object.values(oldSkillDataNames), oldSkillValues => { + const oldSkillName = oldSkillValues[oldSkillDataNames.skill]; + const oldSkillSubSkillName = oldSkillValues[oldSkillDataNames.subSkill]; + + if (oldSkillName === undefined || oldSkillValues[oldSkillDataNames.xp] === undefined) { + console.error("Old skill data is missing values, unable to migrate.\n" + JSON.stringify(oldSkillValues)); + skillsUpdate++; + return; + } + + let parsedSkillName = oldSkillName.toLowerCase() + .replace(" ", "_") + .replace("-", "_"); + + const skillFirstName = parsedSkillName.split("_")[0]; + + let skillData = {}; + const newSkillValues = {}; + + if (skillFirstName in skillsList) { + skillData = skillsList[skillFirstName]; + } else if (skillFirstName in tieredSkillsBasic) { + skillData = tieredSkillsBasic[skillFirstName]; + } else if (skillFirstName.substring(0, skillFirstName.length - 1) in tieredSkillsBasic) { + skillData = tieredSkillsBasic[skillFirstName.substring(0, skillFirstName.length - 1)]; + parsedSkillName = parsedSkillName.substring(0, parsedSkillName.length - 1); + } else { + console.error("Unable to migrate skill data. Old values are: " + JSON.stringify(oldSkillValues)); + skillsUpdated++; + return; + } + + let skillName = ""; + let displaySubSkill = true; + + if (parsedSkillName in skillData.displayName) { + skillName = parsedSkillName; + } else if (parsedSkillName.substring(0, parsedSkillName.length - 1) in skillData.displayName) { + skillName = parsedSkillName.substring(0, parsedSkillName.length - 1); + } else if (oldSkillSubSkillName === undefined) { + console.error("Unable to identify skill. Old skill name: " + oldSkillName); + skillsUpdated++; + return; + } else { + const parsedSubSkillName = oldSkillSubSkillName.trim() + .replace("\'", "") + .replace("`", "") + .replace("-", "_") + .replace("/", "_") + .toLowerCase(); + + const skillWithSubSkill = parsedSkillName + "_" + parsedSubSkillName; + + if (skillWithSubSkill in skillData.displayName) { + skillName = skillWithSubSkill; + displaySubSkill = false; + } else { + console.error("Unable to identify skill. Old skill name and notes: " + oldSkillName + " " + oldSkillSubSkillName); + skillsUpdated++; + return; + } + } + + newSkillValues[newSkillDataNames.skill] = skillName; + if (displaySubSkill && oldSkillSubSkillName !== undefined) { + newSkillValues[newSkillDataNames.subSkill] = oldSkillSubSkillName; + } + newSkillValues[newSkillDataNames.xp] = oldSkillValues[oldSkillDataNames.xp]; + + setAttrs(newSkillValues, {}, () => { + skillsUpdated++; + + if (skillsUpdated === ids.length) { + setAttrs({ + btatow_sheet_version: 3 + }, {}, () => { + console.log("Sheet migrated from version 2 to version 3."); + recalculateSkills(); + }); + } + }); + }); + }); + }); +} \ No newline at end of file