From 1e863fd01560966d5ec714d076adc118b5f24651 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sat, 21 May 2022 22:38:43 -0700 Subject: [PATCH 01/30] Create common.js for more common stuff (Item class, expandItem func) --- js/common.js | 123 ++++++++++++++++++++++++++++++++++++++++ js/computation_graph.js | 48 ++++++++++++++-- js/display.js | 54 ------------------ js/display_constants.js | 95 ------------------------------- 4 files changed, 165 insertions(+), 155 deletions(-) create mode 100644 js/common.js diff --git a/js/common.js b/js/common.js new file mode 100644 index 0000000..079922c --- /dev/null +++ b/js/common.js @@ -0,0 +1,123 @@ +let nonRolledIDs = [ + "name", + "lore", + "displayName", + "tier", + "set", + "slots", + "type", + "material", + "drop", + "quest", + "restrict", + "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", + "atkSpd", + "hp", + "fDef", "wDef", "aDef", "tDef", "eDef", + "lvl", + "classReq", + "strReq", "dexReq", "intReq", "defReq", "agiReq", + "str", "dex", "int", "agi", "def", + "fixID", + "category", + "id", + "skillpoints", + "reqs", + "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", + "majorIds"]; +let rolledIDs = [ + "hprPct", + "mr", + "sdPct", + "mdPct", + "ls", + "ms", + "xpb", + "lb", + "ref", + "thorns", + "expd", + "spd", + "atkTier", + "poison", + "hpBonus", + "spRegen", + "eSteal", + "hprRaw", + "sdRaw", + "mdRaw", + "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", + "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", + "spPct1", "spRaw1", + "spPct2", "spRaw2", + "spPct3", "spRaw3", + "spPct4", "spRaw4", + "rainbowRaw", + "sprint", + "sprintReg", + "jh", + "lq", + "gXp", + "gSpd" +]; + +/** + * Take an item with id list and turn it into a set of minrolls and maxrolls. + */ +function expandItem(item) { + let minRolls = new Map(); + let maxRolls = new Map(); + let expandedItem = new Map(); + if (item.fixID) { //The item has fixed IDs. + expandedItem.set("fixID",true); + for (const id of rolledIDs) { //all rolled IDs are numerical + let val = (item[id] || 0); + minRolls.set(id,val); + maxRolls.set(id,val); + } + } else { //The item does not have fixed IDs. + for (const id of rolledIDs) { + let val = (item[id] || 0); + if (val > 0) { // positive rolled IDs + if (reversedIDs.includes(id)) { + maxRolls.set(id,idRound(val*0.3)); + minRolls.set(id,idRound(val*1.3)); + } else { + maxRolls.set(id,idRound(val*1.3)); + minRolls.set(id,idRound(val*0.3)); + } + } else if (val < 0) { //negative rolled IDs + if (reversedIDs.includes(id)) { + maxRolls.set(id,idRound(val*1.3)); + minRolls.set(id,idRound(val*0.7)); + } + else { + maxRolls.set(id,idRound(val*0.7)); + minRolls.set(id,idRound(val*1.3)); + } + } + else { // if val == 0 + // NOTE: DO NOT remove this case! idRound behavior does not round to 0! + maxRolls.set(id,0); + minRolls.set(id,0); + } + } + } + for (const id of nonRolledIDs) { + expandedItem.set(id,item[id]); + } + expandedItem.set("minRolls",minRolls); + expandedItem.set("maxRolls",maxRolls); + expandedItem.set("powders", powders); + return expandedItem; +} + +class Item { + constructor(item_obj) { + this.statMap = ; //can use the statMap as an expanded Item + this.atkSpd = attackSpeed; + this.hash = "CR-" + hash; + this.initCraftStats(); + this.statMap.set("hash", this.hash); + } +} diff --git a/js/computation_graph.js b/js/computation_graph.js index 4e193c6..975a22d 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -18,6 +18,7 @@ class ComputeNode { this.name = name; this.update_task = null; this.update_time = Date.now(); + this.fail_cb = false; // Set to true to force updates even if parent failed. } /*** @@ -33,9 +34,11 @@ class ComputeNode { for (const input of this.inputs) { value_map.set(input.name, input.get_value()); } - this.value = this.compute_func(); + this.value = this.compute_func(value_map); for (const child of this.children) { - child.update(); + if (this.value || child.fail_cb) { + child.update(); + } } } @@ -49,7 +52,7 @@ class ComputeNode { /*** * Abstract method for computing something. Return value is set into this.value */ - compute_func() { + compute_func(input_map) { throw "no compute func specified"; } @@ -94,10 +97,14 @@ class ItemStats extends ComputeNode { this.none_item = none_item; } - compute_func() { + compute_func(input_map) { // built on the assumption of no one will type in CI/CR letter by letter let item_text = this.input_field.value; + if (!item_text) { + return this.none_item; + } + let item; if (item_text.slice(0, 3) == "CI-") { @@ -113,10 +120,39 @@ class ItemStats extends ComputeNode { item = tomeMap.get(item_text); } - if (!item) { - return this.none_item; + if (!item || item.) { + return null; } return item; } } +/*** + * Node for updating item input fields from parsed items. + */ +class ItemInputDisplay extends ComputeNode { + + constructor(name, item_input_field, item_image) { + super(name); + this.input_field = item_input_field; + this.image = item_image; + this.fail_cb = true; + } + + compute_func(input_map) { + if (input_map.size !== 1) { + throw "ItemInputDisplay accepts exactly one input (item)"; + } + + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + this.input_field.classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set', 'Crafted', 'Custom'); + this.input_field.classList.add("text-light"); + this.image.classList.remove('Normal-shadow', 'Unique-shadow', 'Rare-shadow', 'Legendary-shadow', 'Fabled-shadow', 'Mythic-shadow', 'Set-shadow', 'Crafted-shadow', 'Custom-shadow'); + + if (!item) { + this.input_field.classList.add("is-invalid"); + return null; + } + } +} diff --git a/js/display.js b/js/display.js index fcf9f5e..707efe3 100644 --- a/js/display.js +++ b/js/display.js @@ -27,60 +27,6 @@ function applyArmorPowdersOnce(expandedItem, powders) { } } -/** - * Take an item with id list and turn it into a set of minrolls and maxrolls. - * Also applies powders to armor. - */ -function expandItem(item, powders) { - let minRolls = new Map(); - let maxRolls = new Map(); - let expandedItem = new Map(); - if (item.fixID) { //The item has fixed IDs. - expandedItem.set("fixID",true); - for (const id of rolledIDs) { //all rolled IDs are numerical - let val = (item[id] || 0); - minRolls.set(id,val); - maxRolls.set(id,val); - } - } else { //The item does not have fixed IDs. - for (const id of rolledIDs) { - let val = (item[id] || 0); - if (val > 0) { // positive rolled IDs - if (reversedIDs.includes(id)) { - maxRolls.set(id,idRound(val*0.3)); - minRolls.set(id,idRound(val*1.3)); - } else { - maxRolls.set(id,idRound(val*1.3)); - minRolls.set(id,idRound(val*0.3)); - } - } else if (val < 0) { //negative rolled IDs - if (reversedIDs.includes(id)) { - maxRolls.set(id,idRound(val*1.3)); - minRolls.set(id,idRound(val*0.7)); - } - else { - maxRolls.set(id,idRound(val*0.7)); - minRolls.set(id,idRound(val*1.3)); - } - } - else { // if val == 0 - // NOTE: DO NOT remove this case! idRound behavior does not round to 0! - maxRolls.set(id,0); - minRolls.set(id,0); - } - } - } - for (const id of nonRolledIDs) { - expandedItem.set(id,item[id]); - } - expandedItem.set("minRolls",minRolls); - expandedItem.set("maxRolls",maxRolls); - expandedItem.set("powders", powders); - if (item.category === "armor") { - applyArmorPowders(expandedItem, powders); - } - return expandedItem; -} /* Takes in an ingredient object and returns an equivalent Map(). */ diff --git a/js/display_constants.js b/js/display_constants.js index f686899..d8c26dd 100644 --- a/js/display_constants.js +++ b/js/display_constants.js @@ -1,98 +1,3 @@ -let nonRolledIDs = [ - "name", - "lore", - "displayName", - "tier", - "set", - "slots", - "type", - "material", - "drop", - "quest", - "restrict", - "nDam", - "fDam", - "wDam", - "aDam", - "tDam", - "eDam", - "atkSpd", - "hp", - "fDef", - "wDef", - "aDef", - "tDef", - "eDef", - "lvl", - "classReq", - "strReq", - "dexReq", - "intReq", - "defReq", - "agiReq","str", - "dex", - "int", - "agi", - "def", - "fixID", - "category", - "id", - "skillpoints", - "reqs", - "nDam_", - "fDam_", - "wDam_", - "aDam_", - "tDam_", - "eDam_", - "majorIds"]; -let rolledIDs = [ - "hprPct", - "mr", - "sdPct", - "mdPct", - "ls", - "ms", - "xpb", - "lb", - "ref", - "thorns", - "expd", - "spd", - "atkTier", - "poison", - "hpBonus", - "spRegen", - "eSteal", - "hprRaw", - "sdRaw", - "mdRaw", - "fDamPct", - "wDamPct", - "aDamPct", - "tDamPct", - "eDamPct", - "fDefPct", - "wDefPct", - "aDefPct", - "tDefPct", - "eDefPct", - "spPct1", - "spRaw1", - "spPct2", - "spRaw2", - "spPct3", - "spRaw3", - "spPct4", - "spRaw4", - "rainbowRaw", - "sprint", - "sprintReg", - "jh", - "lq", - "gXp", - "gSpd" -]; let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ]; let colorMap = new Map( [ From 02341012628b52d46ab7b27f0c394368dcc6973a Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 22 May 2022 00:14:20 -0700 Subject: [PATCH 02/30] Remove common (we already have build_utils), edit instances of expandItems --- js/build_utils.js | 119 ++++++++++++++++++++++++++++++++++++++ js/common.js | 123 ---------------------------------------- js/computation_graph.js | 56 ++++++++++++------ js/customizer.js | 2 +- js/items_2.js | 2 +- js/sq2items.js | 2 +- 6 files changed, 160 insertions(+), 144 deletions(-) delete mode 100644 js/common.js diff --git a/js/build_utils.js b/js/build_utils.js index 16bacb5..e60474b 100644 --- a/js/build_utils.js +++ b/js/build_utils.js @@ -76,3 +76,122 @@ for (const [k, v] of translations) { reversetranslations.set(v, k); } +let nonRolledIDs = [ + "name", + "lore", + "displayName", + "tier", + "set", + "slots", + "type", + "material", + "drop", + "quest", + "restrict", + "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", + "atkSpd", + "hp", + "fDef", "wDef", "aDef", "tDef", "eDef", + "lvl", + "classReq", + "strReq", "dexReq", "intReq", "defReq", "agiReq", + "str", "dex", "int", "agi", "def", + "fixID", + "category", + "id", + "skillpoints", + "reqs", + "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", + "majorIds"]; +let rolledIDs = [ + "hprPct", + "mr", + "sdPct", + "mdPct", + "ls", + "ms", + "xpb", + "lb", + "ref", + "thorns", + "expd", + "spd", + "atkTier", + "poison", + "hpBonus", + "spRegen", + "eSteal", + "hprRaw", + "sdRaw", + "mdRaw", + "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", + "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", + "spPct1", "spRaw1", + "spPct2", "spRaw2", + "spPct3", "spRaw3", + "spPct4", "spRaw4", + "rainbowRaw", + "sprint", + "sprintReg", + "jh", + "lq", + "gXp", + "gSpd" +]; + +/** + * Take an item with id list and turn it into a set of minrolls and maxrolls. + */ +function expandItem(item) { + let minRolls = new Map(); + let maxRolls = new Map(); + let expandedItem = new Map(); + if (item.fixID) { //The item has fixed IDs. + expandedItem.set("fixID",true); + for (const id of rolledIDs) { //all rolled IDs are numerical + let val = (item[id] || 0); + minRolls.set(id,val); + maxRolls.set(id,val); + } + } else { //The item does not have fixed IDs. + for (const id of rolledIDs) { + let val = (item[id] || 0); + if (val > 0) { // positive rolled IDs + if (reversedIDs.includes(id)) { + maxRolls.set(id,idRound(val*0.3)); + minRolls.set(id,idRound(val*1.3)); + } else { + maxRolls.set(id,idRound(val*1.3)); + minRolls.set(id,idRound(val*0.3)); + } + } else if (val < 0) { //negative rolled IDs + if (reversedIDs.includes(id)) { + maxRolls.set(id,idRound(val*1.3)); + minRolls.set(id,idRound(val*0.7)); + } + else { + maxRolls.set(id,idRound(val*0.7)); + minRolls.set(id,idRound(val*1.3)); + } + } + else { // if val == 0 + // NOTE: DO NOT remove this case! idRound behavior does not round to 0! + maxRolls.set(id,0); + minRolls.set(id,0); + } + } + } + for (const id of nonRolledIDs) { + expandedItem.set(id,item[id]); + } + expandedItem.set("minRolls",minRolls); + expandedItem.set("maxRolls",maxRolls); + expandedItem.set("powders", powders); + return expandedItem; +} + +class Item { + constructor(item_obj) { + this.statMap = expandItem(item_obj); + } +} diff --git a/js/common.js b/js/common.js deleted file mode 100644 index 079922c..0000000 --- a/js/common.js +++ /dev/null @@ -1,123 +0,0 @@ -let nonRolledIDs = [ - "name", - "lore", - "displayName", - "tier", - "set", - "slots", - "type", - "material", - "drop", - "quest", - "restrict", - "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", - "atkSpd", - "hp", - "fDef", "wDef", "aDef", "tDef", "eDef", - "lvl", - "classReq", - "strReq", "dexReq", "intReq", "defReq", "agiReq", - "str", "dex", "int", "agi", "def", - "fixID", - "category", - "id", - "skillpoints", - "reqs", - "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", - "majorIds"]; -let rolledIDs = [ - "hprPct", - "mr", - "sdPct", - "mdPct", - "ls", - "ms", - "xpb", - "lb", - "ref", - "thorns", - "expd", - "spd", - "atkTier", - "poison", - "hpBonus", - "spRegen", - "eSteal", - "hprRaw", - "sdRaw", - "mdRaw", - "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", - "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", - "spPct1", "spRaw1", - "spPct2", "spRaw2", - "spPct3", "spRaw3", - "spPct4", "spRaw4", - "rainbowRaw", - "sprint", - "sprintReg", - "jh", - "lq", - "gXp", - "gSpd" -]; - -/** - * Take an item with id list and turn it into a set of minrolls and maxrolls. - */ -function expandItem(item) { - let minRolls = new Map(); - let maxRolls = new Map(); - let expandedItem = new Map(); - if (item.fixID) { //The item has fixed IDs. - expandedItem.set("fixID",true); - for (const id of rolledIDs) { //all rolled IDs are numerical - let val = (item[id] || 0); - minRolls.set(id,val); - maxRolls.set(id,val); - } - } else { //The item does not have fixed IDs. - for (const id of rolledIDs) { - let val = (item[id] || 0); - if (val > 0) { // positive rolled IDs - if (reversedIDs.includes(id)) { - maxRolls.set(id,idRound(val*0.3)); - minRolls.set(id,idRound(val*1.3)); - } else { - maxRolls.set(id,idRound(val*1.3)); - minRolls.set(id,idRound(val*0.3)); - } - } else if (val < 0) { //negative rolled IDs - if (reversedIDs.includes(id)) { - maxRolls.set(id,idRound(val*1.3)); - minRolls.set(id,idRound(val*0.7)); - } - else { - maxRolls.set(id,idRound(val*0.7)); - minRolls.set(id,idRound(val*1.3)); - } - } - else { // if val == 0 - // NOTE: DO NOT remove this case! idRound behavior does not round to 0! - maxRolls.set(id,0); - minRolls.set(id,0); - } - } - } - for (const id of nonRolledIDs) { - expandedItem.set(id,item[id]); - } - expandedItem.set("minRolls",minRolls); - expandedItem.set("maxRolls",maxRolls); - expandedItem.set("powders", powders); - return expandedItem; -} - -class Item { - constructor(item_obj) { - this.statMap = ; //can use the statMap as an expanded Item - this.atkSpd = attackSpeed; - this.hash = "CR-" + hash; - this.initCraftStats(); - this.statMap.set("hash", this.hash); - } -} diff --git a/js/computation_graph.js b/js/computation_graph.js index 975a22d..cdd1212 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -1,7 +1,7 @@ let _ALL_NODES = new Map(); class ComputeNode { - /*** + /** * Make a generic compute node. * Adds the node to the global map of nodenames to nodes (for calling from html listeners). * @@ -21,7 +21,7 @@ class ComputeNode { this.fail_cb = false; // Set to true to force updates even if parent failed. } - /*** + /** * Request update of this compute node. Pushes updates to children. */ update(timestamp) { @@ -42,14 +42,14 @@ class ComputeNode { } } - /*** + /** * Get value of this compute node. Can't trigger update cascades (push based update, not pull based.) */ get_value() { return this.value } - /*** + /** * Abstract method for computing something. Return value is set into this.value */ compute_func(input_map) { @@ -62,7 +62,7 @@ class ComputeNode { } } -/*** +/** * Schedule a ComputeNode to be updated. * * @param node_name : ComputeNode name to schedule an update for. @@ -79,11 +79,11 @@ function calcSchedule(node_name) { }, 500); } -/*** +/** * Node for getting an item's stats from an item input field. */ -class ItemStats extends ComputeNode { - /*** +class ItemInputNode extends ComputeNode { + /** * Make an item stat pulling compute node. * * @param name: Name of this node. @@ -94,7 +94,7 @@ class ItemStats extends ComputeNode { super(name); this.input_field.setAttribute("onInput", "calcSchedule('"+name+"');"); this.input_field = item_input_field; - this.none_item = none_item; + this.none_item = expandItem(none_item); } compute_func(input_map) { @@ -114,23 +114,23 @@ class ItemStats extends ComputeNode { item = getCraftFromHash(item_text); } else if (itemMap.has(item_text)) { - item = itemMap.get(item_text); + item = Item(itemMap.get(item_text)); } else if (tomeMap.has(item_text)) { - item = tomeMap.get(item_text); + item = Item(tomeMap.get(item_text)); } - if (!item || item.) { + if (!item || item.statMap.get('type') !== this.none_item.statMap.get('type')) { return null; } return item; } } -/*** +/** * Node for updating item input fields from parsed items. */ -class ItemInputDisplay extends ComputeNode { +class ItemInputDisplayNode extends ComputeNode { constructor(name, item_input_field, item_image) { super(name); @@ -140,10 +140,7 @@ class ItemInputDisplay extends ComputeNode { } compute_func(input_map) { - if (input_map.size !== 1) { - throw "ItemInputDisplay accepts exactly one input (item)"; - } - + if (input_map.size !== 1) { throw "ItemInputDisplayNode accepts exactly one input (item)"; } const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element this.input_field.classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set', 'Crafted', 'Custom'); @@ -154,5 +151,28 @@ class ItemInputDisplay extends ComputeNode { this.input_field.classList.add("is-invalid"); return null; } + + const tier = item.statMap.get('tier'); + this.input_field.classList.add(tier); + this.image.classList.add(tier + "-shadow"); + } +} + +/** + * Change the weapon to match correct type. + */ +class WeaponDisplayNode extends ComputeNode { + + constructor(name, image_field) { + super(name); + this.image = image_field; + } + + compute_func(input_map) { + if (input_map.size !== 1) { throw "WeaponDisplayNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + const type = item.statMap.get('type'); + this.image_field.setAttribute('src', '../media/items/new/generic-'+type+'.png'); } } diff --git a/js/customizer.js b/js/customizer.js index 84c665e..85ffbb0 100644 --- a/js/customizer.js +++ b/js/customizer.js @@ -369,7 +369,7 @@ function useBaseItem(elem) { //Check items db. for (const [name,itemObj] of itemMap) { if (itemName === name) { - baseItem = expandItem(itemObj, []); + baseItem = expandItem(itemObj); break; } } diff --git a/js/items_2.js b/js/items_2.js index a670ee3..220b447 100644 --- a/js/items_2.js +++ b/js/items_2.js @@ -239,7 +239,7 @@ function init_items2() { const itemListFooter = document.getElementById('item-list-footer'); // compile the search db from the item db - const searchDb = items.filter(i => !i.remapID).map(i => [i, expandItem(i, [])]); + const searchDb = items.filter(i => !i.remapID).map(i => [i, expandItem(i)]); // init item list elements const ITEM_LIST_SIZE = 64; diff --git a/js/sq2items.js b/js/sq2items.js index 59f16ba..8531721 100644 --- a/js/sq2items.js +++ b/js/sq2items.js @@ -250,7 +250,7 @@ function resetItemSearch() { } function init_items() { - items_expanded = items.filter( (i) => !("remapID" in i) ).map( (i) => expandItem(i, []) ); + items_expanded = items.filter( (i) => !("remapID" in i) ).map( (i) => expandItem(i) ); } load_init(init_items); From 06f745c158b8da65c0201bba1f85b3a6cbad47b5 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 22 May 2022 00:57:47 -0700 Subject: [PATCH 03/30] Consolidate constants/utils files, start working on new builder file --- builder/index.html | 4 +- js/build_constants.js | 106 +++++++ js/build_utils.js | 3 +- js/builder.js | 607 +++++++++++++++++++--------------------- js/computation_graph.js | 13 +- js/display_constants.js | 1 - js/sq2bs.js | 14 +- js/sq2builder.js | 21 +- 8 files changed, 405 insertions(+), 364 deletions(-) create mode 100644 js/build_constants.js diff --git a/builder/index.html b/builder/index.html index c49ccb4..7a89a26 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1379,6 +1379,7 @@ + @@ -1399,7 +1400,8 @@ - + + diff --git a/js/build_constants.js b/js/build_constants.js new file mode 100644 index 0000000..fbcbb3f --- /dev/null +++ b/js/build_constants.js @@ -0,0 +1,106 @@ +/** + * I kinda lied. Theres some listener stuff in here + * but its mostly constants for builder page specifically. + */ + +const url_tag = location.hash.slice(1); + +const BUILD_VERSION = "7.0.19"; + +let player_build; + + +// THIS IS SUPER DANGEROUS, WE SHOULD NOT BE KEEPING THIS IN SO MANY PLACES +let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison", + "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", + "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", + "hprRaw", "hprPct", "hpBonus", "atkTier", + "spPct1", "spRaw1", "spPct2", "spRaw2", + "spPct3", "spRaw3", "spPct4", "spRaw4" ]; + +let editable_elems = []; + +for (let i of editable_item_fields) { + let elem = document.getElementById(i); + elem.addEventListener("change", (event) => { + elem.classList.add("highlight"); + }); + editable_elems.push(elem); +} + +for (let i of skp_order) { + let elem = document.getElementById(i+"-skp"); + elem.addEventListener("change", (event) => { + elem.classList.add("highlight"); + }); + editable_elems.push(elem); +} + +function clear_highlights() { + for (let i of editable_elems) { + i.classList.remove("highlight"); + } +} + + +let equipment_fields = [ + "helmet", + "chestplate", + "leggings", + "boots", + "ring1", + "ring2", + "bracelet", + "necklace", + "weapon" +]; +let tome_fields = [ + "weaponTome1", + "weaponTome2", + "armorTome1", + "armorTome2", + "armorTome3", + "armorTome4", + "guildTome1", +] +let equipment_names = [ + "Helmet", + "Chestplate", + "Leggings", + "Boots", + "Ring 1", + "Ring 2", + "Bracelet", + "Necklace", + "Weapon" +]; + +let tome_names = [ + "Weapon Tome", + "Weapon Tome", + "Armor Tome", + "Armor Tome", + "Armor Tome", + "Armor Tome", + "Guild Tome", +] +let equipmentInputs = equipment_fields.map(x => x + "-choice"); +let buildFields = equipment_fields.map(x => x+"-tooltip").concat(tome_fields.map(x => x + "-tooltip")); +let tomeInputs = tome_fields.map(x => x + "-choice"); + +let powderInputs = [ + "helmet-powder", + "chestplate-powder", + "leggings-powder", + "boots-powder", + "weapon-powder", +]; + +let weapon_keys = ['dagger', 'wand', 'bow', 'relik', 'spear']; +let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots']; +let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace']; +let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon']; +let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'].concat(tome_keys); + +let spell_disp = ['spell0-info', 'spell1-info', 'spell2-info', 'spell3-info']; +let other_disp = ['build-order', 'set-info', 'int-info']; diff --git a/js/build_utils.js b/js/build_utils.js index e60474b..9381035 100644 --- a/js/build_utils.js +++ b/js/build_utils.js @@ -138,6 +138,7 @@ let rolledIDs = [ "gXp", "gSpd" ]; +let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ]; /** * Take an item with id list and turn it into a set of minrolls and maxrolls. @@ -186,7 +187,7 @@ function expandItem(item) { } expandedItem.set("minRolls",minRolls); expandedItem.set("maxRolls",maxRolls); - expandedItem.set("powders", powders); + expandedItem.set("powders", []); return expandedItem; } diff --git a/js/builder.js b/js/builder.js index a91ef19..3455ee2 100644 --- a/js/builder.js +++ b/js/builder.js @@ -1,182 +1,3 @@ -const url_tag = location.hash.slice(1); -// console.log(url_base); -// console.log(url_tag); - - -const BUILD_VERSION = "7.0.19"; - -function setTitle() { - let text; - if (url_base.includes("hppeng-wynn")) { - text = "WynnBuilder UNSTABLE version "+BUILD_VERSION+" (db version "+DB_VERSION+")"; - } - else { - text = "WynnBuilder version "+BUILD_VERSION+" (db version "+DB_VERSION+")"; - document.getElementById("header").classList.add("funnynumber"); - } - document.getElementById("header").textContent = text; -} - -setTitle(); - -let player_build; - -let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "hprRaw", "hprPct", "hpBonus", "atkTier", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ]; -let editable_elems = []; - -for (let i of editable_item_fields) { - let elem = document.getElementById(i); - elem.addEventListener("change", (event) => { - elem.classList.add("highlight"); - }); - editable_elems.push(elem); -} - -for (let i of skp_order) { - let elem = document.getElementById(i+"-skp"); - elem.addEventListener("change", (event) => { - elem.classList.add("highlight"); - }); - editable_elems.push(elem); -} - -function clear_highlights() { - for (let i of editable_elems) { - i.classList.remove("highlight"); - } -} - - -let equipment_fields = [ - "helmet", - "chestplate", - "leggings", - "boots", - "ring1", - "ring2", - "bracelet", - "necklace", - "weapon" -]; -let equipment_names = [ - "Helmet", - "Chestplate", - "Leggings", - "Boots", - "Ring 1", - "Ring 2", - "Bracelet", - "Necklace", - "Weapon" -]; -let equipmentInputs = equipment_fields.map(x => x + "-choice"); -let buildFields = equipment_fields.map(x => "build-"+x); - -let powderInputs = [ - "helmet-powder", - "chestplate-powder", - "leggings-powder", - "boots-powder", - "weapon-powder", -]; - - - -/* - * Function that takes an item list and populates its corresponding dropdown. - * Used for armors and bracelet/necklace. - */ -function populateItemList(type) { - let item_list = document.getElementById(type+"-items"); - for (const item of itemLists.get(type)) { - let item_obj = itemMap.get(item); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - let el = document.createElement("option"); - el.value = item; - item_list.appendChild(el); - } -} - -/* - * Populate dropdowns, add listeners, etc. - */ -function init() { - console.log("builder.js init"); - - for (const armorType of armorTypes) { - populateItemList(armorType); - // Add change listener to update armor slots. - document.getElementById(armorType+"-choice").addEventListener("change", (event) => { - let item_name = event.target.value; - let nSlots = undefined; - if (itemMap.has(item_name)) { - let item = itemMap.get(item_name); - nSlots = item["slots"]; - //console.log(item); - } - else { - let crafted_custom_item = getCraftFromHash(item_name) !== undefined ? getCraftFromHash(item_name) : (getCustomFromHash(item_name) !== undefined ? getCustomFromHash(item_name) : undefined); - if (crafted_custom_item !== undefined) { - nSlots = crafted_custom_item.statMap.get("slots"); - } - } - if (nSlots !== undefined) { - document.getElementById(armorType+"-slots").textContent = nSlots + " slots"; - } - else { - document.getElementById(armorType+"-slots").textContent = "X slots"; - } - }); - } - - let ring1_list = document.getElementById("ring1-items"); - let ring2_list = document.getElementById("ring2-items"); - for (const ring of itemLists.get("ring")) { - let item_obj = itemMap.get(ring); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - let el1 = document.createElement("option"); - let el2 = document.createElement("option"); - el1.value = ring; - el2.value = ring; - ring1_list.appendChild(el1); - ring2_list.appendChild(el2); - } - - populateItemList("bracelet"); - populateItemList("necklace"); - - let weapon_list = document.getElementById("weapon-items"); - for (const weaponType of weaponTypes) { - for (const weapon of itemLists.get(weaponType)) { - let item_obj = itemMap.get(weapon); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - let el = document.createElement("option"); - el.value = weapon; - weapon_list.appendChild(el); - } - } - - // Add change listener to update weapon slots. - document.getElementById("weapon-choice").addEventListener("change", (event) => { - let item_name = event.target.value; - let item = itemMap.has(item_name) ? itemMap.get(item_name) : (getCraftFromHash(item_name) ? getCraftFromHash(item_name) : (getCustomFromHash(item_name) ? getCustomFromHash(item_name) : undefined)); - if (item !== undefined && event.target.value !== "") { - document.getElementById("weapon-slots").textContent = (item["slots"] ? item["slots"] : (item.statMap !== undefined ? ( item.statMap.has("slots") ? item.statMap.get("slots") : 0): 0) )+ " slots"; - } else { - document.getElementById("weapon-slots").textContent = "X slots"; - } - }); - - decodeBuild(url_tag); - - populateBuildList(); -} function getItemNameFromID(id) { if (redirectMap.has(id)) { @@ -185,13 +6,20 @@ function getItemNameFromID(id) { return idMap.get(id); } +function getTomeNameFromID(id) { + if (tomeRedirectMap.has(id)) { + return getTomeNameFromID(tomeRedirectMap.get(id)); + } + return tomeIDMap.get(id); +} + function parsePowdering(powder_info) { // TODO: Make this run in linear instead of quadratic time... ew let powdering = []; for (let i = 0; i < 5; ++i) { let powders = ""; let n_blocks = Base64.toInt(powder_info.charAt(0)); - console.log(n_blocks + " blocks"); + // console.log(n_blocks + " blocks"); powder_info = powder_info.slice(1); for (let j = 0; j < n_blocks; ++j) { let block = powder_info.slice(0,5); @@ -205,7 +33,7 @@ function parsePowdering(powder_info) { } powdering[i] = powders; } - return powdering; + return [powdering, powder_info]; } /* @@ -213,14 +41,20 @@ function parsePowdering(powder_info) { */ function decodeBuild(url_tag) { if (url_tag) { + //default values let equipment = [null, null, null, null, null, null, null, null, null]; + let tomes = [null, null, null, null, null, null, null]; let powdering = ["", "", "", "", ""]; let info = url_tag.split("_"); let version = info[0]; let save_skp = false; let skillpoints = [0, 0, 0, 0, 0]; let level = 106; - if (version === "0" || version === "1" || version === "2" || version === "3") { + + version_number = parseInt(version) + //equipment (items) + // TODO: use filters + if (version_number < 4) { let equipments = info[1]; for (let i = 0; i < 9; ++i ) { let equipment_str = equipments.slice(i*3,i*3+3); @@ -228,7 +62,7 @@ function decodeBuild(url_tag) { } info[1] = equipments.slice(27); } - if (version === "4") { + else if (version_number == 4) { let info_str = info[1]; let start_idx = 0; for (let i = 0; i < 9; ++i ) { @@ -244,7 +78,7 @@ function decodeBuild(url_tag) { } info[1] = info_str.slice(start_idx); } - if (version === "5") { + else if (version_number <= 6) { let info_str = info[1]; let start_idx = 0; for (let i = 0; i < 9; ++i ) { @@ -263,10 +97,17 @@ function decodeBuild(url_tag) { } info[1] = info_str.slice(start_idx); } - if (version === "1") { + //constant in all versions + for (let i in equipment) { + setValue(equipmentInputs[i], equipment[i]); + } + + //level, skill point assignments, and powdering + if (version_number == 1) { let powder_info = info[1]; - powdering = parsePowdering(powder_info); - } else if (version === "2") { + let res = parsePowdering(powder_info); + powdering = res[0]; + } else if (version_number == 2) { save_skp = true; let skillpoint_info = info[1].slice(0, 10); for (let i = 0; i < 5; ++i ) { @@ -274,8 +115,9 @@ function decodeBuild(url_tag) { } let powder_info = info[1].slice(10); - powdering = parsePowdering(powder_info); - } else if (version === "3" || version === "4" || version === "5"){ + let res = parsePowdering(powder_info); + powdering = res[0]; + } else if (version_number <= 6){ level = Base64.toInt(info[1].slice(10,12)); setValue("level-choice",level); save_skp = true; @@ -286,126 +128,101 @@ function decodeBuild(url_tag) { let powder_info = info[1].slice(12); - powdering = parsePowdering(powder_info); + let res = parsePowdering(powder_info); + powdering = res[0]; + info[1] = res[1]; + } + // Tomes. + if (version == 6) { + //tome values do not appear in anything before v6. + for (let i = 0; i < 7; ++i) { + let tome_str = info[1].charAt(i); + for (let i in tomes) { + setValue(tomeInputs[i], getTomeNameFromID(Base64.toInt(tome_str))); + } + } + info[1] = info[1].slice(7); } for (let i in powderInputs) { setValue(powderInputs[i], powdering[i]); } - for (let i in equipment) { - setValue(equipmentInputs[i], equipment[i]); - } + calculateBuild(save_skp, skillpoints); } } -/* Stores the entire build in a string using B64 encryption and adds it to the URL. +/* Stores the entire build in a string using B64 encoding and adds it to the URL. */ function encodeBuild() { if (player_build) { let build_string; - if (player_build.customItems.length > 0) { //v5 encoding - build_string = "5_"; - let crafted_idx = 0; - let custom_idx = 0; - for (const item of player_build.items) { - - if (item.get("custom")) { - let custom = "CI-"+encodeCustom(player_build.customItems[custom_idx],true); - build_string += Base64.fromIntN(custom.length, 3) + custom; - custom_idx += 1; - } else if (item.get("crafted")) { - build_string += "CR-"+encodeCraft(player_build.craftedItems[crafted_idx]); - crafted_idx += 1; - } else { - build_string += Base64.fromIntN(item.get("id"), 3); - } - } - - for (const skp of skp_order) { - build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 - } - build_string += Base64.fromIntN(player_build.level, 2); - for (const _powderset of player_build.powders) { - let n_bits = Math.ceil(_powderset.length / 6); - build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. - // Slice copy. - let powderset = _powderset.slice(); - while (powderset.length != 0) { - let firstSix = powderset.slice(0,6).reverse(); - let powder_hash = 0; - for (const powder of firstSix) { - powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first. - } - build_string += Base64.fromIntN(powder_hash, 5); - powderset = powderset.slice(6); - } - } - } else { //v4 encoding - build_string = "4_"; - let crafted_idx = 0; - for (const item of player_build.items) { - if (item.get("crafted")) { - build_string += "-"+encodeCraft(player_build.craftedItems[crafted_idx]); - crafted_idx += 1; - } else { - build_string += Base64.fromIntN(item.get("id"), 3); - } - } - - for (const skp of skp_order) { - build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 - } - build_string += Base64.fromIntN(player_build.level, 2); - for (const _powderset of player_build.powders) { - let n_bits = Math.ceil(_powderset.length / 6); - build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. - // Slice copy. - let powderset = _powderset.slice(); - while (powderset.length != 0) { - let firstSix = powderset.slice(0,6).reverse(); - let powder_hash = 0; - for (const powder of firstSix) { - powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first. - } - build_string += Base64.fromIntN(powder_hash, 5); - powderset = powderset.slice(6); + + //V6 encoding - Tomes + build_version = 4; + build_string = ""; + tome_string = ""; + + let crafted_idx = 0; + let custom_idx = 0; + for (const item of player_build.items) { + + if (item.get("custom")) { + let custom = "CI-"+encodeCustom(player_build.customItems[custom_idx],true); + build_string += Base64.fromIntN(custom.length, 3) + custom; + custom_idx += 1; + build_version = Math.max(build_version, 5); + } else if (item.get("crafted")) { + build_string += "CR-"+encodeCraft(player_build.craftedItems[crafted_idx]); + crafted_idx += 1; + } else if (item.get("category") === "tome") { + let tome_id = item.get("id"); + if (tome_id <= 60) { + // valid normal tome. ID 61-63 is for NONE tomes. + build_version = Math.max(build_version, 6); } + tome_string += Base64.fromIntN(tome_id, 1); + } else { + build_string += Base64.fromIntN(item.get("id"), 3); } } - return build_string; + + for (const skp of skp_order) { + build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 + } + build_string += Base64.fromIntN(player_build.level, 2); + for (const _powderset of player_build.powders) { + let n_bits = Math.ceil(_powderset.length / 6); + build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. + // Slice copy. + let powderset = _powderset.slice(); + while (powderset.length != 0) { + let firstSix = powderset.slice(0,6).reverse(); + let powder_hash = 0; + for (const powder of firstSix) { + powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first. + } + build_string += Base64.fromIntN(powder_hash, 5); + powderset = powderset.slice(6); + } + } + build_string += tome_string; + + return build_version.toString() + "_" + build_string; } - // this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ]; - // let build_string = "3_" + Base64.fromIntN(player_build.helmet.get("id"), 3) + - // Base64.fromIntN(player_build.chestplate.get("id"), 3) + - // Base64.fromIntN(player_build.leggings.get("id"), 3) + - // Base64.fromIntN(player_build.boots.get("id"), 3) + - // Base64.fromIntN(player_build.ring1.get("id"), 3) + - // Base64.fromIntN(player_build.ring2.get("id"), 3) + - // Base64.fromIntN(player_build.bracelet.get("id"), 3) + - // Base64.fromIntN(player_build.necklace.get("id"), 3) + - // Base64.fromIntN(player_build.weapon.get("id"), 3); - return ""; } function calculateBuild(save_skp, skp){ try { - let specialNames = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"]; - for (const sName of specialNames) { - for (let i = 1; i < 6; i++) { - let elem = document.getElementById(sName + "-" + i); - let name = sName.replace("_", " "); - if (elem.classList.contains("toggleOn")) { //toggle the pressed button off - elem.classList.remove("toggleOn"); - } - } - } - if(player_build){ + resetEditableIDs(); + if (player_build) { + reset_powder_specials(); updateBoosts("skip", false); updatePowderSpecials("skip", false); } let weaponName = getValue(equipmentInputs[8]); + //bruh @hpp if (weaponName.startsWith("Morph-")) { let equipment = [ "Morph-Stardust", "Morph-Steel", "Morph-Iron", "Morph-Gold", "Morph-Topaz", "Morph-Emerald", "Morph-Amethyst", "Morph-Ruby", weaponName.substring(6) ]; for (let i in equipment) { @@ -440,7 +257,6 @@ function calculateBuild(save_skp, skp){ while (input) { let first = input.slice(0, 2); let powder = powderIDs.get(first); - console.log(powder); if (powder === undefined) { errorederrors.push(first); } else { @@ -457,11 +273,24 @@ function calculateBuild(save_skp, skp){ //console.log("POWDERING: " + powdering); powderings.push(powdering); } + let tomes = [ null, null, null, null, null, null, null]; + for (let i in tomes) { + let equip = getValue(tomeInputs[i]).trim(); + if (equip === "") { + equip = "No " + tome_names[i] + } + else { + setValue(tomeInputs[i], equip); + } + tomes[i] = equip; + } let level = document.getElementById("level-choice").value; - player_build = new Build(level, equipment, powderings, new Map(), errors); + player_build = new Build(level, equipment, powderings, new Map(), errors, tomes); console.log(player_build); + + //isn't this deprecated? for (let i of document.getElementsByClassName("hide-container-block")) { i.style.display = "block"; } @@ -470,21 +299,14 @@ function calculateBuild(save_skp, skp){ } console.log(player_build.toString()); - displayEquipOrder(document.getElementById("build-order"),player_build.equip_order); - - + displaysq2EquipOrder(document.getElementById("build-order"),player_build.equip_order); const assigned = player_build.base_skillpoints; const skillpoints = player_build.total_skillpoints; for (let i in skp_order){ //big bren - setText(skp_order[i] + "-skp-base", "Original Value: " + skillpoints[i]); + setText(skp_order[i] + "-skp-base", "Original: " + skillpoints[i]); } - for (let id of editable_item_fields) { - setValue(id, player_build.statMap.get(id)); - setText(id+"-base", "Original Value: " + player_build.statMap.get(id)); - } - if (save_skp) { // TODO: reduce duplicated code, @updateStats let skillpoints = player_build.total_skillpoints; @@ -499,14 +321,13 @@ function calculateBuild(save_skp, skp){ player_build.assigned_skillpoints += delta_total; } + updateEditableIDs(); calculateBuildStats(); - setTitle(); if (player_build.errored) throw new ListError(player_build.errors); - } catch (error) { - handleBuilderError(error); + console.log(error); } } @@ -586,7 +407,7 @@ function updateStats() { let delta_total = 0; for (let i in skp_order) { let value = document.getElementById(skp_order[i] + "-skp").value; - if (value === ""){value = "0"; setValue(skp_order[i] + "-skp", value)} + if (value === ""){value = 0; setValue(skp_order[i] + "-skp", value)} let manual_assigned = 0; if (value.includes("+")) { let skp = value.split("+"); @@ -604,15 +425,58 @@ function updateStats() { player_build.assigned_skillpoints += delta_total; if(player_build){ updatePowderSpecials("skip", false); + updateArmorPowderSpecials("skip", false); updateBoosts("skip", false); - } - for (let id of editable_item_fields) { - player_build.statMap.set(id, parseInt(getValue(id))); + for (let id of editable_item_fields) { + player_build.statMap.set(id, parseInt(getValue(id))); + } } player_build.aggregateStats(); console.log(player_build.statMap); calculateBuildStats(); } + + +/* Updates all IDs in the edit IDs section. Resets each input and original value text to the correct text according to the current build. +*/ +function updateEditableIDs() { + if (player_build) { + for (const id of editable_item_fields) { + let edit_input = document.getElementById(id); + let val = player_build.statMap.get(id); + edit_input.value = val; + edit_input.placeholder = val; + + let value_label = document.getElementById(id + "-base"); + value_label.textContent = "Original Value: " + val; + //a hack to make resetting easier + value_label.value = val; + } + } +} + +/* Resets all IDs in the edit IDs section to their "original" values. +*/ +function resetEditableIDs() { + if (player_build) { + for (const id of editable_item_fields) { + let edit_input = document.getElementById(id); + let value_label = document.getElementById(id + "-base"); + + edit_input.value = value_label.value; + edit_input.placeholder = value_label.value; + } + } else { + //no player build, reset to 0 + for (const id of editable_item_fields) { + let edit_input = document.getElementById(id); + + edit_input.value = 0; + edit_input.placeholder = 0; + } + } +} + /* Updates all spell boosts */ function updateBoosts(buttonId, recalcStats) { @@ -655,7 +519,7 @@ function updateBoosts(buttonId, recalcStats) { } } -/* Updates all powder special boosts +/* Updates ACTIVE powder special boosts (weapons) */ function updatePowderSpecials(buttonId, recalcStats) { //console.log(player_build.statMap); @@ -740,8 +604,76 @@ function updatePowderSpecials(buttonId, recalcStats) { if (recalcStats) { calculateBuildStats(); } - displayPowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build); + displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true); } + + +/* Updates PASSIVE powder special boosts (armors) +*/ +function updateArmorPowderSpecials(elem_id, recalc_stats) { + //we only update the powder special + external stats if the player has a build + if (elem_id !== "skip") { + if (player_build !== undefined && player_build.weapon !== undefined && player_build.weapon.get("name") !== "No Weapon") { + let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi + + //update the label associated w/ the slider + let elem = document.getElementById(elem_id); + let label = document.getElementById(elem_id + "_label"); + let prev_label = document.getElementById(elem_id + "_prev"); + + let value = elem.value; + + //for use in editing build stats + let prev_value = prev_label.value; + let value_diff = value - prev_value; + + //update the "previous" label + prev_label.value = value; + + label.textContent = label.textContent.split(":")[0] + ": " + value; + + let dmg_id = elem_chars[skp_names.indexOf(wynn_elem)] + "DamPct"; + let new_dmgboost = player_build.externalStats.get(dmg_id) + value_diff; + + //update build external stats - the second one is the relevant one for damage calc purposes + player_build.externalStats.set(dmg_id, new_dmgboost); + player_build.externalStats.get("damageBonus")[skp_names.indexOf(wynn_elem)] = new_dmgboost; + + //update the slider's graphics + let bg_color = elem_colors[skp_names.indexOf(wynn_elem)]; + let pct = Math.round(100 * value / powderSpecialStats[skp_names.indexOf(wynn_elem)].cap); + elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`; + + } + } else { + if (player_build !== undefined) { + for (let i = 0; i < skp_names.length; ++i) { + skp_name = skp_names[i]; + skp_char = elem_chars[i]; + player_build.externalStats.set(skp_char + "DamPct", player_build.externalStats.get(skp_char + "DamPct") - document.getElementById(skp_name+"_boost_armor").value); + player_build.externalStats.get("damageBonus")[i] -= document.getElementById(skp_name+"_boost_armor").value; + } + } + } + + + if (recalc_stats && player_build) { + //calc build stats and display powder special + calculateBuildStats(); + // displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true); + } + +} + +function resetArmorPowderSpecials() { + for (const skp of skp_names) { + document.getElementById(skp + "_boost_armor").value = 0; + document.getElementById(skp + "_boost_armor_prev").value = 0; + document.getElementById(skp + "_boost_armor").style.background = `linear-gradient(to right, #AAAAAA, #AAAAAA 0%, #AAAAAA 100%)`; + document.getElementById(skp + "_boost_armor_label").textContent = `% ${capitalizeFirst(elem_names[skp_names.indexOf(skp)])} Damage Boost: 0` + } +} + /* Calculates all build statistics and updates the entire display. */ function calculateBuildStats() { @@ -749,27 +681,29 @@ function calculateBuildStats() { const skillpoints = player_build.total_skillpoints; let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."]; for (let i in skp_order){ //big bren - setText(skp_order[i] + "-skp-assign", "Manually Assigned: " + assigned[i]); + setText(skp_order[i] + "-skp-assign", "Assign: " + assigned[i]); setValue(skp_order[i] + "-skp", skillpoints[i]); let linebreak = document.createElement("br"); linebreak.classList.add("itemp"); document.getElementById(skp_order[i] + "-skp-label"); setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i])); + document.getElementById(skp_order[i]+"-warnings").textContent = '' if (assigned[i] > 100) { let skp_warning = document.createElement("p"); skp_warning.classList.add("warning"); - skp_warning.textContent += "WARNING: Cannot assign " + assigned[i] + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually."; - document.getElementById(skp_order[i]+"-skp-pct").appendChild(skp_warning); + skp_warning.classList.add("small-text") + skp_warning.textContent += "Cannot assign " + assigned[i] + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually."; + document.getElementById(skp_order[i]+"-warnings").textContent = '' + document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning); } } let summarybox = document.getElementById("summary-box"); summarybox.textContent = ""; let skpRow = document.createElement("p"); - let td = document.createElement("p"); let remainingSkp = document.createElement("p"); - remainingSkp.classList.add("center"); + remainingSkp.classList.add("scaled-font"); let remainingSkpTitle = document.createElement("b"); remainingSkpTitle.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Remaining skillpoints: "; let remainingSkpContent = document.createElement("b"); @@ -783,13 +717,12 @@ function calculateBuildStats() { summarybox.append(skpRow); summarybox.append(remainingSkp); if(player_build.assigned_skillpoints > levelToSkillPoints(player_build.level)){ - let skpWarning = document.createElement("p"); + let skpWarning = document.createElement("span"); //skpWarning.classList.add("itemp"); skpWarning.classList.add("warning"); - skpWarning.classList.add("itemp"); skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!"; let skpCount = document.createElement("p"); - skpCount.classList.add("itemp"); + skpCount.classList.add("warning"); skpCount.textContent = "For level " + (player_build.level>101 ? "101+" : player_build.level) + ", there are only " + levelToSkillPoints(player_build.level) + " skill points available."; summarybox.append(skpWarning); summarybox.append(skpCount); @@ -826,7 +759,6 @@ function calculateBuildStats() { const bonus = sets[setName].bonuses[count-1]; // console.log(setName); if (bonus["illegal"]) { - console.log("mmmm"); let setWarning = document.createElement("p"); setWarning.classList.add("itemp"); setWarning.classList.add("warning"); @@ -836,30 +768,32 @@ function calculateBuildStats() { } for (let i in player_build.items) { - displayExpandedItem(player_build.items[i], buildFields[i]); + displaysq2ExpandedItem(player_build.items[i], buildFields[i]); + collapse_element("#"+equipment_keys[i]+"-tooltip"); } - displayBuildStats("build-overall-stats",player_build); - displaySetBonuses("set-info",player_build); - displayNextCosts("int-info",player_build); + displaysq2ArmorStats(player_build); + displaysq2BuildStats('overall-stats', player_build, build_all_display_commands); + displaysq2BuildStats("offensive-stats",player_build, build_offensive_display_commands); + displaysq2SetBonuses("set-info",player_build); + displaysq2WeaponStats(player_build); let meleeStats = player_build.getMeleeStats(); - displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); + displaysq2MeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); - displayDefenseStats(document.getElementById("build-defense-stats"),player_build); + displaysq2DefenseStats(document.getElementById("defensive-stats"),player_build); - displayPoisonDamage(document.getElementById("build-poison-stats"),player_build); + displaysq2PoisonDamage(document.getElementById("build-poison-stats"),player_build); let spells = spell_table[player_build.weapon.get("type")]; for (let i = 0; i < 4; ++i) { let parent_elem = document.getElementById("spell"+i+"-info"); let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); - displaySpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1); + displaysq2SpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1); } location.hash = encodeBuild(); clear_highlights(); - updateOGP(); } function copyBuild() { @@ -971,7 +905,21 @@ function toggleID() { } } +function toggleButton(button_id) { + let button = document.getElementById(button_id); + if (button) { + if (button.classList.contains("toggleOn")) { + button.classList.remove("toggleOn"); + } else { + button.classList.add("toggleOn"); + } + } +} + function optimizeStrDex() { + if (!player_build) { + return; + } const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints; const base_skillpoints = player_build.base_skillpoints; const max_str_boost = 100 - base_skillpoints[0]; @@ -1061,7 +1009,6 @@ function optimizeStrDex() { try { calculateBuildStats(); - setTitle(); if (player_build.errored) throw new ListError(player_build.errors); } @@ -1071,8 +1018,20 @@ function optimizeStrDex() { } // TODO: Learn and use await +function init() { + console.log("builder.js init"); + init_autocomplete(); + decodeBuild(url_tag); + for (const i of equipment_keys) { + update_field(i); + } +} function init2() { load_ing_init(init); } -load_init(init2); -updateOGP(); \ No newline at end of file +function init3() { + load_tome_init(init2) +} + + +load_init(init3); diff --git a/js/computation_graph.js b/js/computation_graph.js index cdd1212..9039685 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -1,5 +1,3 @@ -let _ALL_NODES = new Map(); - class ComputeNode { /** * Make a generic compute node. @@ -8,10 +6,6 @@ class ComputeNode { * @param name : Name of the node (string). Must be unique. Must "fit in" a JS string (terminated by single quotes). */ constructor(name) { - if (_ALL_NODES.has(name)) { - throw 'Duplicate node name: ' + name; - } - _ALL_NODES.set(name, this) this.inputs = []; this.children = []; this.value = 0; @@ -65,10 +59,9 @@ class ComputeNode { /** * Schedule a ComputeNode to be updated. * - * @param node_name : ComputeNode name to schedule an update for. + * @param node : ComputeNode to schedule an update for. */ -function calcSchedule(node_name) { - node = _ALL_NODES.get(node_name); +function calcSchedule(node) { if (node.update_task !== null) { clearTimeout(node.update_task); } @@ -92,7 +85,7 @@ class ItemInputNode extends ComputeNode { */ constructor(name, item_input_field, none_item) { super(name); - this.input_field.setAttribute("onInput", "calcSchedule('"+name+"');"); + this.input_field.setAttribute("input", () => calcSchedule(this)); this.input_field = item_input_field; this.none_item = expandItem(none_item); } diff --git a/js/display_constants.js b/js/display_constants.js index d8c26dd..93f77e6 100644 --- a/js/display_constants.js +++ b/js/display_constants.js @@ -1,4 +1,3 @@ -let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ]; let colorMap = new Map( [ ["Normal", "#fff"], diff --git a/js/sq2bs.js b/js/sq2bs.js index 25ac2d2..94f1c22 100644 --- a/js/sq2bs.js +++ b/js/sq2bs.js @@ -1,13 +1,3 @@ -let weapon_keys = ['dagger', 'wand', 'bow', 'relik', 'spear']; -let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots']; -let skp_keys = ['str', 'dex', 'int', 'def', 'agi']; -let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace']; -let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon']; -let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'].concat(tome_keys); -let powder_keys = ['e', 't', 'w', 'f', 'a']; - -let spell_disp = ['spell0-info', 'spell1-info', 'spell2-info', 'spell3-info']; -let other_disp = ['build-order', 'set-info', 'int-info']; document.addEventListener('DOMContentLoaded', function() { @@ -202,7 +192,7 @@ function update_field(field) { document.querySelector("#"+field+"-powder").classList.add("is-invalid"); } else { for (i = 0; i < powder_string.length / 2; i++) { - if (powder_keys.includes(powder_string.substring(i*2, i*2+2).split("")[0]) == false || isNaN(powder_string.substring(i*2, i*2+2).split("")[1]) || parseInt(powder_string.substring(i*2, i*2+2).split("")[1]) < 1 || parseInt(powder_string.substring(i*2, i*2+2).split("")[1]) > 6) { + if (skp_elements.includes(powder_string.substring(i*2, i*2+2).split("")[0]) == false || isNaN(powder_string.substring(i*2, i*2+2).split("")[1]) || parseInt(powder_string.substring(i*2, i*2+2).split("")[1]) < 1 || parseInt(powder_string.substring(i*2, i*2+2).split("")[1]) > 6) { document.querySelector("#"+field+"-powder").classList.add("is-invalid"); } } @@ -239,7 +229,7 @@ function toggle_spell_tab(tab) { } function toggle_boost_tab(tab) { - for (const i of skp_keys) { + for (const i of skp_order) { document.querySelector("#"+i+"-boost").style.display = "none"; document.getElementById(i + "-boost-tab").classList.remove("selected-btn"); } diff --git a/js/sq2builder.js b/js/sq2builder.js index fc42f6d..e63cdb0 100644 --- a/js/sq2builder.js +++ b/js/sq2builder.js @@ -5,25 +5,16 @@ const url_tag = location.hash.slice(1); const BUILD_VERSION = "7.0.19"; -// function setTitle() { -// let text; -// if (url_base.includes("hppeng-wynn")) { -// text = "WynnBuilder UNSTABLE version "+BUILD_VERSION+" (db version "+DB_VERSION+")"; -// } -// else { -// text = "WynnBuilder version "+BUILD_VERSION+" (db version "+DB_VERSION+")"; -// document.getElementById("header").classList.add("funnynumber"); -// } -// document.getElementById("header").textContent = text; -// } -// -// setTitle(); - let player_build; // THIS IS SUPER DANGEROUS, WE SHOULD NOT BE KEEPING THIS IN SO MANY PLACES -let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "hprRaw", "hprPct", "hpBonus", "atkTier", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ]; +let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison", + "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", + "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", + "hprRaw", "hprPct", "hpBonus", "atkTier", + "spPct1", "spRaw1", "spPct2", "spRaw2", + "spPct3", "spRaw3", "spPct4", "spRaw4" ]; let editable_elems = []; From 0f4dba258f3cc65ce07ff81c5ad0f80c84555f1a Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 22 May 2022 03:21:34 -0700 Subject: [PATCH 04/30] Proof of concept (compute nodes to get items from names and highlight) --- builder/index.html | 3 +- js/builder.js | 691 +--------------------------------------- js/builder_graph.js | 21 ++ js/computation_graph.js | 71 ++++- js/load.js | 86 ++--- js/optimize.js | 101 ++++++ js/utils.js | 27 +- 7 files changed, 227 insertions(+), 773 deletions(-) create mode 100644 js/builder_graph.js create mode 100644 js/optimize.js diff --git a/builder/index.html b/builder/index.html index 7a89a26..d7a802c 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1402,10 +1402,11 @@ + - + diff --git a/js/builder.js b/js/builder.js index 3455ee2..1b18b96 100644 --- a/js/builder.js +++ b/js/builder.js @@ -1,4 +1,3 @@ - function getItemNameFromID(id) { if (redirectMap.has(id)) { return getItemNameFromID(redirectMap.get(id)); @@ -51,7 +50,7 @@ function decodeBuild(url_tag) { let skillpoints = [0, 0, 0, 0, 0]; let level = 106; - version_number = parseInt(version) + let version_number = parseInt(version) //equipment (items) // TODO: use filters if (version_number < 4) { @@ -147,8 +146,6 @@ function decodeBuild(url_tag) { for (let i in powderInputs) { setValue(powderInputs[i], powdering[i]); } - - calculateBuild(save_skp, skillpoints); } } @@ -213,589 +210,6 @@ function encodeBuild() { } } -function calculateBuild(save_skp, skp){ - try { - resetEditableIDs(); - if (player_build) { - reset_powder_specials(); - updateBoosts("skip", false); - updatePowderSpecials("skip", false); - } - let weaponName = getValue(equipmentInputs[8]); - //bruh @hpp - if (weaponName.startsWith("Morph-")) { - let equipment = [ "Morph-Stardust", "Morph-Steel", "Morph-Iron", "Morph-Gold", "Morph-Topaz", "Morph-Emerald", "Morph-Amethyst", "Morph-Ruby", weaponName.substring(6) ]; - for (let i in equipment) { - setValue(equipmentInputs[i], equipment[i]); - } - } - - //updatePowderSpecials("skip"); //jank pt 1 - save_skp = (typeof save_skp !== 'undefined') ? save_skp : false; - /* TODO: implement level changing - Make this entire function prettier - */ - let equipment = [ null, null, null, null, null, null, null, null, null ]; - for (let i in equipment) { - let equip = getValue(equipmentInputs[i]).trim(); - if (equip === "") { - equip = "No " + equipment_names[i] - } - else { - setValue(equipmentInputs[i], equip); - } - equipment[i] = equip; - } - let powderings = []; - let errors = []; - for (const i in powderInputs) { - // read in two characters at a time. - // TODO: make this more robust. - let input = getValue(powderInputs[i]).trim(); - let powdering = []; - let errorederrors = []; - while (input) { - let first = input.slice(0, 2); - let powder = powderIDs.get(first); - if (powder === undefined) { - errorederrors.push(first); - } else { - powdering.push(powder); - } - input = input.slice(2); - } - if (errorederrors.length > 0) { - if (errorederrors.length > 1) - errors.push(new IncorrectInput(errorederrors.join(""), "t6w6", powderInputs[i])); - else - errors.push(new IncorrectInput(errorederrors[0], "t6 or e3", powderInputs[i])); - } - //console.log("POWDERING: " + powdering); - powderings.push(powdering); - } - let tomes = [ null, null, null, null, null, null, null]; - for (let i in tomes) { - let equip = getValue(tomeInputs[i]).trim(); - if (equip === "") { - equip = "No " + tome_names[i] - } - else { - setValue(tomeInputs[i], equip); - } - tomes[i] = equip; - } - - - let level = document.getElementById("level-choice").value; - player_build = new Build(level, equipment, powderings, new Map(), errors, tomes); - console.log(player_build); - - //isn't this deprecated? - for (let i of document.getElementsByClassName("hide-container-block")) { - i.style.display = "block"; - } - for (let i of document.getElementsByClassName("hide-container-grid")) { - i.style.display = "grid"; - } - - console.log(player_build.toString()); - displaysq2EquipOrder(document.getElementById("build-order"),player_build.equip_order); - - const assigned = player_build.base_skillpoints; - const skillpoints = player_build.total_skillpoints; - for (let i in skp_order){ //big bren - setText(skp_order[i] + "-skp-base", "Original: " + skillpoints[i]); - } - - if (save_skp) { - // TODO: reduce duplicated code, @updateStats - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; - for (let i in skp_order) { - let manual_assigned = skp[i]; - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; - } - player_build.assigned_skillpoints += delta_total; - } - - updateEditableIDs(); - calculateBuildStats(); - if (player_build.errored) - throw new ListError(player_build.errors); - } - catch (error) { - console.log(error); - } -} - -function handleBuilderError(error) { - if (error instanceof ListError) { - for (let i of error.errors) { - if (i instanceof ItemNotFound) { - i.element.textContent = i.message; - } else if (i instanceof IncorrectInput) { - if (document.getElementById(i.id) !== null) { - document.getElementById(i.id).parentElement.querySelectorAll("p.error")[0].textContent = i.message; - } - } else { - let msg = i.stack; - let lines = msg.split("\n"); - let header = document.getElementById("header"); - header.textContent = ""; - for (const line of lines) { - let p = document.createElement("p"); - p.classList.add("itemp"); - p.textContent = line; - header.appendChild(p); - } - let p2 = document.createElement("p"); - p2.textContent = "If you believe this is an error, contact hppeng on forums or discord."; - header.appendChild(p2); - } - } - } else { - let msg = error.stack; - let lines = msg.split("\n"); - let header = document.getElementById("header"); - header.textContent = ""; - for (const line of lines) { - let p = document.createElement("p"); - p.classList.add("itemp"); - p.textContent = line; - header.appendChild(p); - } - let p2 = document.createElement("p"); - p2.textContent = "If you believe this is an error, contact hppeng on forums or discord."; - header.appendChild(p2); - } -} - -/* Updates all build statistics based on (for now) the skillpoint input fields and then calculates build stats. -*/ -function updateStats() { - - let specialNames = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"]; - for (const sName of specialNames) { - for (let i = 1; i < 6; i++) { - let elem = document.getElementById(sName + "-" + i); - let name = sName.replace("_", " "); - if (elem.classList.contains("toggleOn")) { //toggle the pressed button off - elem.classList.remove("toggleOn"); - let special = powderSpecialStats[specialNames.indexOf(sName)]; - console.log(special); - if (special["weaponSpecialEffects"].has("Damage Boost")) { - if (name === "Courage" || name === "Curse") { //courage is universal damage boost - //player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100; - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1]; - } - } - } - } - } - - - - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; - for (let i in skp_order) { - let value = document.getElementById(skp_order[i] + "-skp").value; - if (value === ""){value = 0; setValue(skp_order[i] + "-skp", value)} - let manual_assigned = 0; - if (value.includes("+")) { - let skp = value.split("+"); - for (const s of skp) { - manual_assigned += parseInt(s,10); - } - } else { - manual_assigned = parseInt(value,10); - } - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; - } - player_build.assigned_skillpoints += delta_total; - if(player_build){ - updatePowderSpecials("skip", false); - updateArmorPowderSpecials("skip", false); - updateBoosts("skip", false); - for (let id of editable_item_fields) { - player_build.statMap.set(id, parseInt(getValue(id))); - } - } - player_build.aggregateStats(); - console.log(player_build.statMap); - calculateBuildStats(); -} - - -/* Updates all IDs in the edit IDs section. Resets each input and original value text to the correct text according to the current build. -*/ -function updateEditableIDs() { - if (player_build) { - for (const id of editable_item_fields) { - let edit_input = document.getElementById(id); - let val = player_build.statMap.get(id); - edit_input.value = val; - edit_input.placeholder = val; - - let value_label = document.getElementById(id + "-base"); - value_label.textContent = "Original Value: " + val; - //a hack to make resetting easier - value_label.value = val; - } - } -} - -/* Resets all IDs in the edit IDs section to their "original" values. -*/ -function resetEditableIDs() { - if (player_build) { - for (const id of editable_item_fields) { - let edit_input = document.getElementById(id); - let value_label = document.getElementById(id + "-base"); - - edit_input.value = value_label.value; - edit_input.placeholder = value_label.value; - } - } else { - //no player build, reset to 0 - for (const id of editable_item_fields) { - let edit_input = document.getElementById(id); - - edit_input.value = 0; - edit_input.placeholder = 0; - } - } -} - -/* Updates all spell boosts -*/ -function updateBoosts(buttonId, recalcStats) { - let elem = document.getElementById(buttonId); - let name = buttonId.split("-")[0]; - if(buttonId !== "skip") { - if (elem.classList.contains("toggleOn")) { - player_build.damageMultiplier -= damageMultipliers.get(name); - if (name === "warscream") { - player_build.defenseMultiplier -= .20; - } - if (name === "vanish") { - player_build.defenseMultiplier -= .15; - } - elem.classList.remove("toggleOn"); - }else{ - player_build.damageMultiplier += damageMultipliers.get(name); - if (name === "warscream") { - player_build.defenseMultiplier += .20; - } - if (name === "vanish") { - player_build.defenseMultiplier += .15; - } - elem.classList.add("toggleOn"); - } - updatePowderSpecials("skip", false); //jank pt 1 - } else { - for (const [key, value] of damageMultipliers) { - let elem = document.getElementById(key + "-boost") - if (elem.classList.contains("toggleOn")) { - elem.classList.remove("toggleOn"); - player_build.damageMultiplier -= value; - if (key === "warscream") { player_build.defenseMultiplier -= .20 } - if (key === "vanish") { player_build.defenseMultiplier -= .15 } - } - } - } - if (recalcStats) { - calculateBuildStats(); - } -} - -/* Updates ACTIVE powder special boosts (weapons) -*/ -function updatePowderSpecials(buttonId, recalcStats) { - //console.log(player_build.statMap); - - let name = (buttonId).split("-")[0]; - let power = (buttonId).split("-")[1]; // [1, 5] - let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"]; - let powderSpecials = []; // [ [special, power], [special, power]] - - - if(name !== "skip"){ - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { //toggle the pressed button off - elem.classList.remove("toggleOn"); - let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))]; - if (special.weaponSpecialEffects.has("Damage Boost")) { - name = name.replace("_", " "); - if (name === "Courage" || name === "Curse") { //courage and curse are universal damage boost - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - //poison? - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[power-1]; - } - } - } else { - for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off - //name is same, power is i - if(document.getElementById(name.replace(" ", "_") + "-" + i).classList.contains("toggleOn")) { - document.getElementById(name.replace(" ", "_") + "-" + i).classList.remove("toggleOn"); - let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))]; - if (special.weaponSpecialEffects.has("Damage Boost")) { - name = name.replace("_", " "); //might be redundant - if (name === "Courage" || name === "Curse") { //courage is universal damage boost - //player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100; - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1]; - } - } - } - } - //toggle the pressed button on - elem.classList.add("toggleOn"); - } - } - - for (const sName of specialNames) { - for (let i = 1;i < 6; i++) { - if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) { - let powderSpecial = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))]; - powderSpecials.push([powderSpecial, i]); - break; - } - } - } - - - if (name !== "skip") { - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { - let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))]; - if (special["weaponSpecialEffects"].has("Damage Boost")) { - let name = special["weaponSpecialName"]; - if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.get("damageBonus")[4] += special.weaponSpecialEffects.get("Damage Boost")[power-1]; - } - } - } - } - - if (recalcStats) { - calculateBuildStats(); - } - displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true); -} - - -/* Updates PASSIVE powder special boosts (armors) -*/ -function updateArmorPowderSpecials(elem_id, recalc_stats) { - //we only update the powder special + external stats if the player has a build - if (elem_id !== "skip") { - if (player_build !== undefined && player_build.weapon !== undefined && player_build.weapon.get("name") !== "No Weapon") { - let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi - - //update the label associated w/ the slider - let elem = document.getElementById(elem_id); - let label = document.getElementById(elem_id + "_label"); - let prev_label = document.getElementById(elem_id + "_prev"); - - let value = elem.value; - - //for use in editing build stats - let prev_value = prev_label.value; - let value_diff = value - prev_value; - - //update the "previous" label - prev_label.value = value; - - label.textContent = label.textContent.split(":")[0] + ": " + value; - - let dmg_id = elem_chars[skp_names.indexOf(wynn_elem)] + "DamPct"; - let new_dmgboost = player_build.externalStats.get(dmg_id) + value_diff; - - //update build external stats - the second one is the relevant one for damage calc purposes - player_build.externalStats.set(dmg_id, new_dmgboost); - player_build.externalStats.get("damageBonus")[skp_names.indexOf(wynn_elem)] = new_dmgboost; - - //update the slider's graphics - let bg_color = elem_colors[skp_names.indexOf(wynn_elem)]; - let pct = Math.round(100 * value / powderSpecialStats[skp_names.indexOf(wynn_elem)].cap); - elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`; - - } - } else { - if (player_build !== undefined) { - for (let i = 0; i < skp_names.length; ++i) { - skp_name = skp_names[i]; - skp_char = elem_chars[i]; - player_build.externalStats.set(skp_char + "DamPct", player_build.externalStats.get(skp_char + "DamPct") - document.getElementById(skp_name+"_boost_armor").value); - player_build.externalStats.get("damageBonus")[i] -= document.getElementById(skp_name+"_boost_armor").value; - } - } - } - - - if (recalc_stats && player_build) { - //calc build stats and display powder special - calculateBuildStats(); - // displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true); - } - -} - -function resetArmorPowderSpecials() { - for (const skp of skp_names) { - document.getElementById(skp + "_boost_armor").value = 0; - document.getElementById(skp + "_boost_armor_prev").value = 0; - document.getElementById(skp + "_boost_armor").style.background = `linear-gradient(to right, #AAAAAA, #AAAAAA 0%, #AAAAAA 100%)`; - document.getElementById(skp + "_boost_armor_label").textContent = `% ${capitalizeFirst(elem_names[skp_names.indexOf(skp)])} Damage Boost: 0` - } -} - -/* Calculates all build statistics and updates the entire display. -*/ -function calculateBuildStats() { - const assigned = player_build.base_skillpoints; - const skillpoints = player_build.total_skillpoints; - let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."]; - for (let i in skp_order){ //big bren - setText(skp_order[i] + "-skp-assign", "Assign: " + assigned[i]); - setValue(skp_order[i] + "-skp", skillpoints[i]); - let linebreak = document.createElement("br"); - linebreak.classList.add("itemp"); - document.getElementById(skp_order[i] + "-skp-label"); - setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i])); - document.getElementById(skp_order[i]+"-warnings").textContent = '' - if (assigned[i] > 100) { - let skp_warning = document.createElement("p"); - skp_warning.classList.add("warning"); - skp_warning.classList.add("small-text") - skp_warning.textContent += "Cannot assign " + assigned[i] + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually."; - document.getElementById(skp_order[i]+"-warnings").textContent = '' - document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning); - } - } - - let summarybox = document.getElementById("summary-box"); - summarybox.textContent = ""; - let skpRow = document.createElement("p"); - - let remainingSkp = document.createElement("p"); - remainingSkp.classList.add("scaled-font"); - let remainingSkpTitle = document.createElement("b"); - remainingSkpTitle.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Remaining skillpoints: "; - let remainingSkpContent = document.createElement("b"); - remainingSkpContent.textContent = "" + (levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints); - remainingSkpContent.classList.add(levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints < 0 ? "negative" : "positive"); - - remainingSkp.appendChild(remainingSkpTitle); - remainingSkp.appendChild(remainingSkpContent); - - - summarybox.append(skpRow); - summarybox.append(remainingSkp); - if(player_build.assigned_skillpoints > levelToSkillPoints(player_build.level)){ - let skpWarning = document.createElement("span"); - //skpWarning.classList.add("itemp"); - skpWarning.classList.add("warning"); - skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!"; - let skpCount = document.createElement("p"); - skpCount.classList.add("warning"); - skpCount.textContent = "For level " + (player_build.level>101 ? "101+" : player_build.level) + ", there are only " + levelToSkillPoints(player_build.level) + " skill points available."; - summarybox.append(skpWarning); - summarybox.append(skpCount); - } - let lvlWarning; - for (const item of player_build.items) { - let item_lvl; - if (item.get("crafted")) { - //item_lvl = item.get("lvlLow") + "-" + item.get("lvl"); - item_lvl = item.get("lvlLow"); - } - else { - item_lvl = item.get("lvl"); - } - - if (player_build.level < item_lvl) { - if (!lvlWarning) { - lvlWarning = document.createElement("p"); - lvlWarning.classList.add("itemp"); - lvlWarning.classList.add("warning"); - lvlWarning.textContent = "WARNING: A level " + player_build.level + " player cannot use some piece(s) of this build." - } - let baditem = document.createElement("p"); - baditem.classList.add("nocolor"); - baditem.classList.add("itemp"); - baditem.textContent = item.get("displayName") + " requires level " + item.get("lvl") + " to use."; - lvlWarning.appendChild(baditem); - } - } - if(lvlWarning){ - summarybox.append(lvlWarning); - } - for (const [setName, count] of player_build.activeSetCounts) { - const bonus = sets[setName].bonuses[count-1]; - // console.log(setName); - if (bonus["illegal"]) { - let setWarning = document.createElement("p"); - setWarning.classList.add("itemp"); - setWarning.classList.add("warning"); - setWarning.textContent = "WARNING: illegal item combination: " + setName - summarybox.append(setWarning); - } - } - - for (let i in player_build.items) { - displaysq2ExpandedItem(player_build.items[i], buildFields[i]); - collapse_element("#"+equipment_keys[i]+"-tooltip"); - } - - displaysq2ArmorStats(player_build); - displaysq2BuildStats('overall-stats', player_build, build_all_display_commands); - displaysq2BuildStats("offensive-stats",player_build, build_offensive_display_commands); - displaysq2SetBonuses("set-info",player_build); - displaysq2WeaponStats(player_build); - - let meleeStats = player_build.getMeleeStats(); - displaysq2MeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); - - displaysq2DefenseStats(document.getElementById("defensive-stats"),player_build); - - displaysq2PoisonDamage(document.getElementById("build-poison-stats"),player_build); - - let spells = spell_table[player_build.weapon.get("type")]; - for (let i = 0; i < 4; ++i) { - let parent_elem = document.getElementById("spell"+i+"-info"); - let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); - displaysq2SpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1); - } - - location.hash = encodeBuild(); - clear_highlights(); -} - function copyBuild() { if (player_build) { copyTextToClipboard(url_base+location.hash); @@ -916,107 +330,6 @@ function toggleButton(button_id) { } } -function optimizeStrDex() { - if (!player_build) { - return; - } - const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints; - const base_skillpoints = player_build.base_skillpoints; - const max_str_boost = 100 - base_skillpoints[0]; - const max_dex_boost = 100 - base_skillpoints[1]; - if (Math.min(remaining, max_str_boost, max_dex_boost) < 0) return; // Unwearable - - const base_total_skillpoints = player_build.total_skillpoints; - let str_bonus = remaining; - let dex_bonus = 0; - let best_skillpoints = player_build.total_skillpoints; - let best_damage = 0; - for (let i = 0; i <= remaining; ++i) { - let total_skillpoints = base_total_skillpoints.slice(); - total_skillpoints[0] += Math.min(max_str_boost, str_bonus); - total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus); - - // Calculate total 3rd spell damage - let spell = spell_table[player_build.weapon.get("type")][2]; - const stats = player_build.statMap; - let critChance = skillPointsToPercentage(total_skillpoints[1]); - let save_damages = []; - let spell_parts; - if (spell.parts) { - spell_parts = spell.parts; - } - else { - spell_parts = spell.variants.DEFAULT; - for (const majorID of stats.get("activeMajorIDs")) { - if (majorID in spell.variants) { - spell_parts = spell.variants[majorID]; - break; - } - } - } - let total_damage = 0; - for (const part of spell_parts) { - if (part.type === "damage") { - let _results = calculateSpellDamage(stats, part.conversion, - stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"), - part.multiplier / 100, player_build.weapon, total_skillpoints, - player_build.damageMultiplier, player_build.externalStats); - let totalDamNormal = _results[0]; - let totalDamCrit = _results[1]; - let results = _results[2]; - let tooltipinfo = _results[3]; - - for (let i = 0; i < 6; ++i) { - for (let j in results[i]) { - results[i][j] = results[i][j].toFixed(2); - } - } - let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0; - let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; - let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; - - save_damages.push(averageDamage); - if (part.summary == true) { - total_damage = averageDamage; - } - } else if (part.type === "total") { - total_damage = 0; - for (let i in part.factors) { - total_damage += save_damages[i] * part.factors[i]; - } - } - } // END Calculate total 3rd spell damage (total_damage) - if (total_damage > best_damage) { - best_damage = total_damage; - best_skillpoints = total_skillpoints.slice(); - } - - str_bonus -= 1; - dex_bonus += 1; - - } - // TODO: reduce duplicated code, @calculateBuild - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; - for (let i in skp_order) { - let manual_assigned = best_skillpoints[i]; - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; - } - player_build.assigned_skillpoints += delta_total; - - try { - calculateBuildStats(); - if (player_build.errored) - throw new ListError(player_build.errors); - } - catch (error) { - handleBuilderError(error); - } -} - // TODO: Learn and use await function init() { console.log("builder.js init"); @@ -1026,6 +339,7 @@ function init() { update_field(i); } } + function init2() { load_ing_init(init); } @@ -1033,5 +347,4 @@ function init3() { load_tome_init(init2) } - load_init(init3); diff --git a/js/builder_graph.js b/js/builder_graph.js new file mode 100644 index 0000000..7b652ba --- /dev/null +++ b/js/builder_graph.js @@ -0,0 +1,21 @@ + + +let item_nodes = []; + +document.addEventListener('DOMContentLoaded', function() { + for (const [eq, none_item] of zip(equipment_fields, none_items)) { + let input_field = document.getElementById(eq+"-choice"); + let item_image = document.getElementById(eq+"-img"); + + let item_input = new ItemInputNode(eq+'-input', input_field, none_item); + item_nodes.push(item_input); + new ItemInputDisplayNode(eq+'-display', input_field, item_image).link_to(item_input); + new PrintNode(eq+'-debug').link_to(item_input); + //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); + + } + let weapon_image = document.getElementById("weapon-img"); + new WeaponDisplayNode('weapon-type', weapon_image).link_to(item_nodes[8]); + console.log("Set up graph"); + +}); diff --git a/js/computation_graph.js b/js/computation_graph.js index 9039685..417c9bf 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -13,6 +13,7 @@ class ComputeNode { this.update_task = null; this.update_time = Date.now(); this.fail_cb = false; // Set to true to force updates even if parent failed. + this.calc_inputs = new Map(); } /** @@ -23,19 +24,40 @@ class ComputeNode { return; } this.update_time = timestamp; + this.set_value(this.compute_func(this.calc_inputs)); + } - let value_map = Map(); - for (const input of this.inputs) { - value_map.set(input.name, input.get_value()); - } - this.value = this.compute_func(value_map); + /** + * Set this node's value directly. Notifies children. + */ + set_value(value) { + let timestamp = Date.now(); + this.update_time = timestamp; + this.value = value; for (const child of this.children) { - if (this.value || child.fail_cb) { - child.update(); + child.set_input(this.name, this.value, timestamp); + } + } + + /** + * Set an input value. Propagates calculation if all inputs are present. + */ + set_input(input_name, value, timestamp) { + if (value || this.fail_cb) { + this.calc_inputs.set(input_name, value) + if (this.calc_inputs.size === this.inputs.length) { + this.update(timestamp) } } } + /** + * Remove cached input values to this calculation. + */ + clear_cache() { + this.calc_inputs = new Map(); + } + /** * Get value of this compute node. Can't trigger update cascades (push based update, not pull based.) */ @@ -72,6 +94,19 @@ function calcSchedule(node) { }, 500); } +class PrintNode extends ComputeNode { + + constructor(name) { + super(name); + this.fail_cb = true; + } + + compute_func(input_map) { + console.log([this.name, input_map]); + return null; + } +} + /** * Node for getting an item's stats from an item input field. */ @@ -85,9 +120,9 @@ class ItemInputNode extends ComputeNode { */ constructor(name, item_input_field, none_item) { super(name); - this.input_field.setAttribute("input", () => calcSchedule(this)); this.input_field = item_input_field; - this.none_item = expandItem(none_item); + this.input_field.addEventListener("input", () => calcSchedule(this)); + this.none_item = new Item(none_item); } compute_func(input_map) { @@ -107,16 +142,22 @@ class ItemInputNode extends ComputeNode { item = getCraftFromHash(item_text); } else if (itemMap.has(item_text)) { - item = Item(itemMap.get(item_text)); + item = new Item(itemMap.get(item_text)); } else if (tomeMap.has(item_text)) { - item = Item(tomeMap.get(item_text)); + item = new Item(tomeMap.get(item_text)); } - if (!item || item.statMap.get('type') !== this.none_item.statMap.get('type')) { - return null; + if (item) { + let type_match; + if (this.none_item.statMap.get('category') === 'weapon') { + type_match = item.statMap.get('category') === 'weapon'; + } else { + type_match = item.statMap.get('type') === this.none_item.statMap.get('type'); + } + if (type_match) { return item; } } - return item; + return null; } } @@ -166,6 +207,6 @@ class WeaponDisplayNode extends ComputeNode { const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element const type = item.statMap.get('type'); - this.image_field.setAttribute('src', '../media/items/new/generic-'+type+'.png'); + this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png'); } } diff --git a/js/load.js b/js/load.js index 265cc3c..461e1d7 100644 --- a/js/load.js +++ b/js/load.js @@ -201,59 +201,61 @@ function load_init(init_func) { } } +// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. +for (const it of itemTypes) { + itemLists.set(it, []); +} + +let none_items = [ + ["armor", "helmet", "No Helmet"], + ["armor", "chestplate", "No Chestplate"], + ["armor", "leggings", "No Leggings"], + ["armor", "boots", "No Boots"], + ["accessory", "ring", "No Ring 1"], + ["accessory", "ring", "No Ring 2"], + ["accessory", "bracelet", "No Bracelet"], + ["accessory", "necklace", "No Necklace"], + ["weapon", "dagger", "No Weapon"], +]; +for (let i = 0; i < none_items.length; i++) { + let item = Object(); + item.slots = 0; + item.category = none_items[i][0]; + item.type = none_items[i][1]; + item.name = none_items[i][2]; + item.displayName = item.name; + item.set = null; + item.quest = null; + item.skillpoints = [0, 0, 0, 0, 0]; + item.has_negstat = false; + item.reqs = [0, 0, 0, 0, 0]; + item.fixID = true; + item.tier = "Normal"; + item.id = 10000 + i; + item.nDam = "0-0"; + item.eDam = "0-0"; + item.tDam = "0-0"; + item.wDam = "0-0"; + item.fDam = "0-0"; + item.aDam = "0-0"; + clean_item(item); + + none_items[i] = item; +} + function init_maps() { //warp itemMap = new Map(); /* Mapping from item names to set names. */ idMap = new Map(); redirectMap = new Map(); - for (const it of itemTypes) { - itemLists.set(it, []); - } - - let noneItems = [ - ["armor", "helmet", "No Helmet"], - ["armor", "chestplate", "No Chestplate"], - ["armor", "leggings", "No Leggings"], - ["armor", "boots", "No Boots"], - ["accessory", "ring", "No Ring 1"], - ["accessory", "ring", "No Ring 2"], - ["accessory", "bracelet", "No Bracelet"], - ["accessory", "necklace", "No Necklace"], - ["weapon", "dagger", "No Weapon"], - ]; - for (let i = 0; i < noneItems.length; i++) { - let item = Object(); - item.slots = 0; - item.category = noneItems[i][0]; - item.type = noneItems[i][1]; - item.name = noneItems[i][2]; - item.displayName = item.name; - item.set = null; - item.quest = null; - item.skillpoints = [0, 0, 0, 0, 0]; - item.has_negstat = false; - item.reqs = [0, 0, 0, 0, 0]; - item.fixID = true; - item.tier = "Normal"; - item.id = 10000 + i; - item.nDam = "0-0"; - item.eDam = "0-0"; - item.tDam = "0-0"; - item.wDam = "0-0"; - item.fDam = "0-0"; - item.aDam = "0-0"; - clean_item(item); - - noneItems[i] = item; - } - items = items.concat(noneItems); + items = items.concat(none_items); //console.log(items); for (const item of items) { if (item.remapID === undefined) { itemLists.get(item.type).push(item.displayName); itemMap.set(item.displayName, item); - if (noneItems.includes(item)) { + if (none_items.includes(item)) { idMap.set(item.id, ""); } else { diff --git a/js/optimize.js b/js/optimize.js new file mode 100644 index 0000000..6185edd --- /dev/null +++ b/js/optimize.js @@ -0,0 +1,101 @@ +function optimizeStrDex() { + if (!player_build) { + return; + } + const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints; + const base_skillpoints = player_build.base_skillpoints; + const max_str_boost = 100 - base_skillpoints[0]; + const max_dex_boost = 100 - base_skillpoints[1]; + if (Math.min(remaining, max_str_boost, max_dex_boost) < 0) return; // Unwearable + + const base_total_skillpoints = player_build.total_skillpoints; + let str_bonus = remaining; + let dex_bonus = 0; + let best_skillpoints = player_build.total_skillpoints; + let best_damage = 0; + for (let i = 0; i <= remaining; ++i) { + let total_skillpoints = base_total_skillpoints.slice(); + total_skillpoints[0] += Math.min(max_str_boost, str_bonus); + total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus); + + // Calculate total 3rd spell damage + let spell = spell_table[player_build.weapon.get("type")][2]; + const stats = player_build.statMap; + let critChance = skillPointsToPercentage(total_skillpoints[1]); + let save_damages = []; + let spell_parts; + if (spell.parts) { + spell_parts = spell.parts; + } + else { + spell_parts = spell.variants.DEFAULT; + for (const majorID of stats.get("activeMajorIDs")) { + if (majorID in spell.variants) { + spell_parts = spell.variants[majorID]; + break; + } + } + } + let total_damage = 0; + for (const part of spell_parts) { + if (part.type === "damage") { + let _results = calculateSpellDamage(stats, part.conversion, + stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"), + part.multiplier / 100, player_build.weapon, total_skillpoints, + player_build.damageMultiplier, player_build.externalStats); + let totalDamNormal = _results[0]; + let totalDamCrit = _results[1]; + let results = _results[2]; + let tooltipinfo = _results[3]; + + for (let i = 0; i < 6; ++i) { + for (let j in results[i]) { + results[i][j] = results[i][j].toFixed(2); + } + } + let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0; + let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; + let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; + + save_damages.push(averageDamage); + if (part.summary == true) { + total_damage = averageDamage; + } + } else if (part.type === "total") { + total_damage = 0; + for (let i in part.factors) { + total_damage += save_damages[i] * part.factors[i]; + } + } + } // END Calculate total 3rd spell damage (total_damage) + if (total_damage > best_damage) { + best_damage = total_damage; + best_skillpoints = total_skillpoints.slice(); + } + + str_bonus -= 1; + dex_bonus += 1; + + } + // TODO: reduce duplicated code, @calculateBuild + let skillpoints = player_build.total_skillpoints; + let delta_total = 0; + for (let i in skp_order) { + let manual_assigned = best_skillpoints[i]; + let delta = manual_assigned - skillpoints[i]; + skillpoints[i] = manual_assigned; + player_build.base_skillpoints[i] += delta; + delta_total += delta; + } + player_build.assigned_skillpoints += delta_total; + + try { + calculateBuildStats(); + if (player_build.errored) + throw new ListError(player_build.errors); + } + catch (error) { + handleBuilderError(error); + } +} + diff --git a/js/utils.js b/js/utils.js index fed7df8..164543d 100644 --- a/js/utils.js +++ b/js/utils.js @@ -3,31 +3,6 @@ const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.sp const zip = (a, b) => a.map((k, i) => [k, b[i]]); -//updates all the OGP tags for a webpage. Should be called when build changes -function updateOGP() { - //update the embed URL - let url_elem = document.getElementById("ogp-url"); - if (url_elem) { - url_elem.content = url_base+location.hash; - } - - //update the embed text content - let build_elem = document.getElementById("ogp-build-list"); - if (build_elem && player_build) { - let text = "WynnBuilder build:\n"+ - "> "+player_build.helmet.get("displayName")+"\n"+ - "> "+player_build.chestplate.get("displayName")+"\n"+ - "> "+player_build.leggings.get("displayName")+"\n"+ - "> "+player_build.boots.get("displayName")+"\n"+ - "> "+player_build.ring1.get("displayName")+"\n"+ - "> "+player_build.ring2.get("displayName")+"\n"+ - "> "+player_build.bracelet.get("displayName")+"\n"+ - "> "+player_build.necklace.get("displayName")+"\n"+ - "> "+player_build.weapon.get("displayName")+" ["+player_build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]"; - build_elem.content = text; - } -} - function clamp(num, low, high){ return Math.min(Math.max(num, low), high); } @@ -412,4 +387,4 @@ async function hardReload() { function capitalizeFirst(str) { return str[0].toUpperCase() + str.substring(1); -} \ No newline at end of file +} From adcf1ee3621393fb191acffa71b9c2ba816933bb Mon Sep 17 00:00:00 2001 From: reschan Date: Mon, 23 May 2022 13:20:03 +0700 Subject: [PATCH 05/30] re-enable melee breakdown --- builder/index.html | 2 +- js/sq2bs.js | 2 ++ js/sq2display.js | 26 +++++++------------------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/builder/index.html b/builder/index.html index 2569df8..806d3e5 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1240,7 +1240,7 @@
-
melee
+
melee
diff --git a/js/sq2bs.js b/js/sq2bs.js index dce7eba..cb571d3 100644 --- a/js/sq2bs.js +++ b/js/sq2bs.js @@ -29,6 +29,8 @@ document.addEventListener('DOMContentLoaded', function() { document.querySelector("#"+i+"Avg").setAttribute("onclick", "toggle_spell_tab('"+i+"')"); } + document.querySelector("#build-melee-statsAvg").setAttribute("onclick", "toggle_spell_tab('build-melee-stats')"); + document.querySelector("#level-choice").setAttribute("oninput", "calcBuildSchedule()") document.querySelector("#weapon-choice").setAttribute("oninput", document.querySelector("#weapon-choice").getAttribute("oninput") + "resetArmorPowderSpecials();"); // document.querySelector("#edit-IDs-button").setAttribute("onclick", "toggle_edit_id_tab()"); diff --git a/js/sq2display.js b/js/sq2display.js index 07715d5..7dd1d27 100644 --- a/js/sq2display.js +++ b/js/sq2display.js @@ -692,7 +692,6 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ title_elem.classList.add("title"); title_elem.textContent = "Melee Stats"; parent_elem.append(title_elem); - parent_elem.append(document.createElement("br")); //overall title let title_elemavg = document.createElement("b"); @@ -703,9 +702,6 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ let averageDamage = document.createElement("p"); averageDamage.classList.add("left"); averageDamage.textContent = "Average DPS: " + stats[10]; - tooltiptext = `= ((${stats[8]} * ${(stats[6][2]).toFixed(2)}) + (${stats[9]} * ${(stats[7][2]).toFixed(2)}))` - tooltip = createTooltip(tooltip, "p", tooltiptext, averageDamage, ["melee-tooltip"]); - averageDamage.appendChild(tooltip); parent_elem.append(averageDamage); //overall average DPS @@ -751,8 +747,6 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ dmg.textContent = stats[i][0] + " \u2013 " + stats[i][1]; dmg.classList.add(damageClasses[i]); dmg.classList.add("itemp"); - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(0,2).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, dmg, ["melee-tooltip"]); nonCritStats.append(dmg); } } @@ -767,15 +761,11 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ arr2.push(stats[i][1]); } } - tooltiptext = tooltiparr[0] + arr.join(" + ") + "\n" + tooltiparr[1] + arr2.join(" + "); - tooltip = createTooltip(tooltip, "p", tooltiptext, normalDamage, ["melee-tooltip"]); nonCritStats.append(normalDamage); let normalDPS = document.createElement("p"); normalDPS.textContent = "Normal DPS: " + stats[8]; normalDPS.classList.add("tooltip"); - tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, normalDPS, ["melee-tooltip"]); nonCritStats.append(normalDPS); //overall average DPS @@ -785,9 +775,6 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ let singleHitDamageSecond = document.createElement("span"); singleHitDamageSecond.classList.add("Damage"); singleHitDamageSecond.textContent = stats[12].toFixed(2); - tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${stats[6][2].toFixed(2)} + ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${stats[7][2].toFixed(2)}`; - // tooltip = createTooltip(tooltip, "p", tooltiptext, singleHitDamage, ["melee-tooltip", "summary-tooltip"]); - singleHitDamage.appendChild(singleHitDamageFirst); singleHitDamage.appendChild(singleHitDamageSecond); overallparent_elem.append(singleHitDamage); @@ -812,8 +799,6 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ dmg.textContent = stats[i][2] + " \u2013 " + stats[i][3]; dmg.classList.add(damageClasses[i]); dmg.classList.add("itemp"); - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(2,4).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, dmg, ["melee-tooltip"]); critStats.append(dmg); } } @@ -827,15 +812,11 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ arr2.push(stats[i][3]); } } - tooltiptext = tooltiparr[0] + arr.join(" + ") + "\n" + tooltiparr[1] + arr2.join(" + "); - tooltip = createTooltip(tooltip, "p", tooltiptext, critDamage, ["melee-tooltip"]); critStats.append(critDamage); let critDPS = document.createElement("p"); critDPS.textContent = "Crit DPS: " + stats[9]; - tooltiptext = ` = ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, critDPS, ["melee-tooltip"]); critStats.append(critDPS); let critChance = document.createElement("p"); @@ -845,6 +826,13 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ critStats.append(critChance); parent_elem.append(critStats); + + //up and down arrow - done ugly + let arrow = document.createElement("img"); + arrow.id = "arrow_" + overallparent_elem.id; + arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem"; + arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png"; + overallparent_elem.appendChild(arrow); } function displaysq2ArmorStats(build) { From 4f7f0f9cfc20a48b51bcb7f9aa4c1e63d753ffd4 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 12 Jun 2022 07:45:48 -0700 Subject: [PATCH 06/30] Clean up loading code, much cleaner invocation during init (and less prone to races) --- js/builder.js | 17 ++-- js/builder_graph.js | 194 ++++++++++++++++++++++++++++++++++++++++++++ js/load.js | 166 ++++++++++++++++--------------------- js/load_ing.js | 143 +++++++++++++++++--------------- js/load_tome.js | 128 ++++++++++++++--------------- 5 files changed, 411 insertions(+), 237 deletions(-) diff --git a/js/builder.js b/js/builder.js index 1b18b96..1e51e31 100644 --- a/js/builder.js +++ b/js/builder.js @@ -335,16 +335,11 @@ function init() { console.log("builder.js init"); init_autocomplete(); decodeBuild(url_tag); - for (const i of equipment_keys) { - update_field(i); - } } -function init2() { - load_ing_init(init); -} -function init3() { - load_tome_init(init2) -} - -load_init(init3); +//load_init(init3); +(async function() { + let load_promises = [ load_init(), load_ing_init(), load_tome_init() ]; + await Promise.all(load_promises); + init(); +})(); diff --git a/js/builder_graph.js b/js/builder_graph.js index 7b652ba..fd0de13 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -19,3 +19,197 @@ document.addEventListener('DOMContentLoaded', function() { console.log("Set up graph"); }); + +// autocomplete initialize +function init_autocomplete() { + console.log("autocomplete init"); + console.log(itemLists) + let dropdowns = new Map(); + for (const eq of equipment_keys) { + if (tome_keys.includes(eq)) { + continue; + } + // build dropdown + let item_arr = []; + if (eq == 'weapon') { + for (const weaponType of weapon_keys) { + for (const weapon of itemLists.get(weaponType)) { + let item_obj = itemMap.get(weapon); + if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { + continue; + } + if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { + continue; + } + item_arr.push(weapon); + } + } + } else { + for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) { + let item_obj = itemMap.get(item); + if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { + continue; + } + if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { + continue; + } + item_arr.push(item) + } + } + + // create dropdown + dropdowns.set(eq, new autoComplete({ + data: { + src: item_arr + }, + selector: "#"+ eq +"-choice", + wrapper: false, + resultsList: { + maxResults: 1000, + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + // dynamic result loc + let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); + list.style.top = position.bottom + window.scrollY +"px"; + list.style.left = position.x+"px"; + list.style.width = position.width+"px"; + list.style.maxHeight = position.height * 2 +"px"; + + if (!data.results.length) { + message = document.createElement('li'); + message.classList.add('scaled-font'); + message.textContent = "No results found!"; + list.prepend(message); + } + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + element: (item, data) => { + item.classList.add(itemMap.get(data.value).tier); + }, + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; + } + }, + }, + } + })); + } + + for (const eq of tome_keys) { + // build dropdown + let tome_arr = []; + for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) { + let tome_obj = tomeMap.get(tome); + if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") { + continue; + } + //this should suffice for tomes - jank + if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) { + continue; + } + let tome_name = tome; + tome_arr.push(tome_name); + } + + // create dropdown + dropdowns.set(eq, new autoComplete({ + data: { + src: tome_arr + }, + selector: "#"+ eq +"-choice", + wrapper: false, + resultsList: { + maxResults: 1000, + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + // dynamic result loc + let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); + list.style.top = position.bottom + window.scrollY +"px"; + list.style.left = position.x+"px"; + list.style.width = position.width+"px"; + list.style.maxHeight = position.height * 2 +"px"; + + if (!data.results.length) { + message = document.createElement('li'); + message.classList.add('scaled-font'); + message.textContent = "No results found!"; + list.prepend(message); + } + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + element: (tome, data) => { + tome.classList.add(tomeMap.get(data.value).tier); + }, + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; + } + }, + }, + } + })); + } + + let filter_loc = ["filter1", "filter2", "filter3", "filter4"]; + for (const i of filter_loc) { + dropdowns.set(i+"-choice", new autoComplete({ + data: { + src: sq2ItemFilters, + }, + selector: "#"+i+"-choice", + wrapper: false, + resultsList: { + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + // dynamic result loc + console.log(i); + list.style.zIndex = "100"; + let position = document.getElementById(i+"-dropdown").getBoundingClientRect(); + window_pos = document.getElementById("search-container").getBoundingClientRect(); + list.style.top = position.bottom - window_pos.top + 5 +"px"; + list.style.left = position.x - window_pos.x +"px"; + list.style.width = position.width+"px"; + + if (!data.results.length) { + message = document.createElement('li'); + message.classList.add('scaled-font'); + message.textContent = "No filters found!"; + list.prepend(message); + } + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; + } + }, + }, + } + })); + } +} diff --git a/js/load.js b/js/load.js index 461e1d7..4f6e4b2 100644 --- a/js/load.js +++ b/js/load.js @@ -14,42 +14,35 @@ let itemLists = new Map(); /* * Load item set from local DB. Calls init() on success. */ -async function load_local(init_func) { - let get_tx = db.transaction(['item_db', 'set_db'], 'readonly'); - let sets_store = get_tx.objectStore('set_db'); - let get_store = get_tx.objectStore('item_db'); - let request = get_store.getAll(); - request.onerror = function(event) { - console.log("Could not read local item db..."); - } - request.onsuccess = function(event) { - console.log("Successfully read local item db."); - items = request.result; - //console.log(items); - let request2 = sets_store.openCursor(); +async function load_local() { + return new Promise(function(resolve, reject) { + let get_tx = db.transaction(['item_db', 'set_db'], 'readonly'); + let sets_store = get_tx.objectStore('set_db'); + let get_store = get_tx.objectStore('item_db'); + let request = get_store.getAll(); + let request2 = sets_store.getAll(); + request.onerror = function(event) { + reject("Could not read local item db..."); + } + request.onsuccess = function(event) { + console.log("Successfully read local item db."); + } - sets = {}; request2.onerror = function(event) { - console.log("Could not read local set db..."); + reject("Could not read local set db..."); } - request2.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - sets[cursor.primaryKey] = cursor.value; - cursor.continue(); - } - else { - console.log("Successfully read local set db."); - //console.log(sets); - init_maps(); - init_func(); - load_complete = true; - } + console.log("Successfully read local set db."); } - } - await get_tx.complete; - db.close(); + get_tx.oncomplete = function(event) { + items = request.result; + sets = request2.result; + init_maps(); + load_complete = true; + db.close(); + resolve(); + } + }); } /* @@ -91,7 +84,7 @@ function clean_item(item) { /* * Load item set from remote DB (aka a big json file). Calls init() on success. */ -async function load(init_func) { +async function load() { let getUrl = window.location; let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1]; @@ -101,16 +94,6 @@ async function load(init_func) { items = result.items; sets = result.sets; - - -// let clear_tx = db.transaction(['item_db', 'set_db'], 'readwrite'); -// let clear_items = clear_tx.objectStore('item_db'); -// let clear_sets = clear_tx.objectStore('item_db'); -// -// await clear_items.clear(); -// await clear_sets.clear(); -// await clear_tx.complete; - let add_tx = db.transaction(['item_db', 'set_db'], 'readwrite'); add_tx.onabort = function(e) { console.log(e); @@ -131,74 +114,67 @@ async function load(init_func) { add_promises.push(sets_store.add(sets[set], set)); } add_promises.push(add_tx.complete); - Promise.all(add_promises).then((values) => { - init_maps(); - init_func(); - load_complete = true; - }); - // DB not closed? idfk man + + await Promise.all(add_promises); + init_maps(); + load_complete = true; + db.close(); } -function load_init(init_func) { - if (load_complete) { - console.log("Item db already loaded, skipping load sequence"); - init_func(); - return; - } - let request = window.indexedDB.open('item_db', DB_VERSION); +async function load_init() { + return new Promise((resolve, reject) => { + let request = window.indexedDB.open('item_db', DB_VERSION); - request.onerror = function() { - console.log("DB failed to open..."); - }; + request.onerror = function() { + reject("DB failed to open..."); + }; - request.onsuccess = function() { - (async function() { + request.onsuccess = async function() { db = request.result; - if (!reload) { - console.log("Using stored data...") - load_local(init_func); + if (load_in_progress) { + while (!load_complete) { + await sleep(100); + } + console.log("Skipping load...") } else { - if (load_in_progress) { - while (!load_complete) { - await sleep(100); - } - console.log("Skipping load...") - init_func(); + load_in_progress = true + if (reload) { + console.log("Using new data...") + await load(); } else { - // Not 100% safe... whatever! - load_in_progress = true - console.log("Using new data...") - load(init_func); + console.log("Using stored data...") + await load_local(); } } - })() - } + resolve(); + }; - request.onupgradeneeded = function(e) { - reload = true; + request.onupgradeneeded = function(e) { + reload = true; - let db = e.target.result; - - try { - db.deleteObjectStore('item_db'); - } - catch (error) { - console.log("Could not delete item DB. This is probably fine"); - } - try { - db.deleteObjectStore('set_db'); - } - catch (error) { - console.log("Could not delete set DB. This is probably fine"); - } + let db = e.target.result; + + try { + db.deleteObjectStore('item_db'); + } + catch (error) { + console.log("Could not delete item DB. This is probably fine"); + } + try { + db.deleteObjectStore('set_db'); + } + catch (error) { + console.log("Could not delete set DB. This is probably fine"); + } - db.createObjectStore('item_db'); - db.createObjectStore('set_db'); + db.createObjectStore('item_db'); + db.createObjectStore('set_db'); - console.log("DB setup complete..."); - } + console.log("DB setup complete..."); + }; + }); } // List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. diff --git a/js/load_ing.js b/js/load_ing.js index 1a6144e..ffc4c05 100644 --- a/js/load_ing.js +++ b/js/load_ing.js @@ -4,6 +4,7 @@ const ING_DB_VERSION = 13; let idb; let ireload = false; +let iload_in_progress = false; let iload_complete = false; let ings; let recipes; @@ -20,32 +21,34 @@ let recipeIDMap; /* * Load item set from local DB. Calls init() on success. */ -async function ing_load_local(init_func) { - console.log("IngMap is: \n " + ingMap); - let get_tx = idb.transaction(['ing_db', 'recipe_db'], 'readonly'); - let ings_store = get_tx.objectStore('ing_db'); - let recipes_store = get_tx.objectStore('recipe_db'); - let request3 = ings_store.getAll(); - request3.onerror = function(event) { - console.log("Could not read local ingredient db..."); - } - request3.onsuccess = function(event) { - console.log("Successfully read local ingredient db."); - ings = request3.result; +async function ing_load_local() { + return new Promise(function(resolve, reject) { + let get_tx = idb.transaction(['ing_db', 'recipe_db'], 'readonly'); + let ings_store = get_tx.objectStore('ing_db'); + let recipes_store = get_tx.objectStore('recipe_db'); + let request3 = ings_store.getAll(); + request3.onerror = function(event) { + reject("Could not read local ingredient db..."); + } + request3.onsuccess = function(event) { + console.log("Successfully read local ingredient db."); + } let request4 = recipes_store.getAll(); request4.onerror = function(event) { - console.log("Could not read local recipe db..."); + reject("Could not read local recipe db..."); } request4.onsuccess = function(event) { console.log("Successfully read local recipe db."); + } + get_tx.oncomplete = function(event) { + ings = request3.result; recipes = request4.result; init_ing_maps(); - init_func(); iload_complete = true; + idb.close(); + resolve() } - } - await get_tx.complete; - idb.close(); + }); } function clean_ing(ing) { @@ -59,11 +62,12 @@ function clean_ing(ing) { /* * Load item set from remote DB (aka a big json file). Calls init() on success. */ -async function load_ings(init_func) { +async function load_ings() { let getUrl = window.location; - let baseUrl = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1]; - let url = baseUrl + "/ingreds_compress.json"; + let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1]; + // "Random" string to prevent caching! + let url = baseUrl + "/ingreds_compress.json?"+new Date(); url = url.replace(/\w+.html/, "") ; let result = await (await fetch(url)).json(); @@ -97,59 +101,65 @@ async function load_ings(init_func) { } add_promises.push(add_tx2.complete); add_promises.push(add_tx3.complete); - Promise.all(add_promises).then((values) => { - init_ing_maps(); - init_func(); - iload_complete = true; - }); - // DB not closed? idfk man + + await Promise.all(add_promises); + init_ing_maps(); + iload_complete = true; + idb.close(); } -function load_ing_init(init_func) { - if (iload_complete) { - console.log("Ingredient db already loaded, skipping load sequence"); - init_func(); - return; - } - let request = window.indexedDB.open("ing_db", ING_DB_VERSION) - request.onerror = function() { - console.log("DB failed to open..."); - } +async function load_ing_init() { + return new Promise((resolve, reject) => { + let request = window.indexedDB.open("ing_db", ING_DB_VERSION) + request.onerror = function() { + reject("DB failed to open..."); + } - request.onsuccess = function() { - idb = request.result; - if (!ireload) { - console.log("Using stored data...") - ing_load_local(init_func); + request.onsuccess = async function() { + idb = request.result; + if (iload_in_progress) { + while (!iload_complete) { + await sleep(100); + } + console.log("Skipping load...") + } + else { + iload_in_progress = true + if (ireload) { + console.log("Using new data...") + await load_ings(); + } + else { + console.log("Using stored data...") + await ing_load_local(); + } + } + resolve(); } - else { - console.log("Using new data...") - load_ings(init_func); - } - } - request.onupgradeneeded = function(e) { - ireload = true; + request.onupgradeneeded = function(e) { + ireload = true; - let idb = e.target.result; - - try { - idb.deleteObjectStore('ing_db'); - } - catch (error) { - console.log("Could not delete ingredient DB. This is probably fine"); - } - try { - idb.deleteObjectStore('recipe_db'); - } - catch (error) { - console.log("Could not delete recipe DB. This is probably fine"); - } - idb.createObjectStore('ing_db'); - idb.createObjectStore('recipe_db'); + let idb = e.target.result; + + try { + idb.deleteObjectStore('ing_db'); + } + catch (error) { + console.log("Could not delete ingredient DB. This is probably fine"); + } + try { + idb.deleteObjectStore('recipe_db'); + } + catch (error) { + console.log("Could not delete recipe DB. This is probably fine"); + } + idb.createObjectStore('ing_db'); + idb.createObjectStore('recipe_db'); - console.log("DB setup complete..."); - } + console.log("DB setup complete..."); + } + }); } function init_ing_maps() { @@ -222,4 +232,5 @@ function init_ing_maps() { recipeList.push(recipe["name"]); recipeIDMap.set(recipe["id"],recipe["name"]); } + console.log(ingMap); } diff --git a/js/load_tome.js b/js/load_tome.js index afd9315..2186a60 100644 --- a/js/load_tome.js +++ b/js/load_tome.js @@ -13,29 +13,33 @@ let tomeLists = new Map(); /* * Load tome set from local DB. Calls init() on success. */ -async function load_tome_local(init_func) { - let get_tx = tdb.transaction(['tome_db'], 'readonly'); - let get_store = get_tx.objectStore('tome_db'); - let request = get_store.getAll(); - request.onerror = function(event) { - console.log("Could not read local tome db..."); - } - request.onsuccess = function(event) { - console.log("Successfully read local tome db."); - tomes = request.result; - - init_tome_maps(); - init_func(); - tload_complete = true; - } - await get_tx.complete; - tdb.close(); +async function load_tome_local() { + return new Promise(function(resolve, reject) { + let get_tx = tdb.transaction(['tome_db'], 'readonly'); + let get_store = get_tx.objectStore('tome_db'); + let request = get_store.getAll(); + request.onerror = function(event) { + reject("Could not read local tome db..."); + } + request.onsuccess = function(event) { + console.log("Successfully read local tome db."); + } + get_tx.oncomplete = function(event) { + console.log('b'); + console.log(request.readyState); + tomes = request.result; + init_tome_maps(); + tload_complete = true; + tdb.close(); + resolve(); + } + }); } /* * Load tome set from remote DB (json). Calls init() on success. */ -async function load_tome(init_func) { +async function load_tome() { let getUrl = window.location; let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1]; @@ -60,67 +64,61 @@ async function load_tome(init_func) { }; add_promises.push(req); } - Promise.all(add_promises).then((values) => { - init_tome_maps(); - init_func(); - tload_complete = true; - }); - // DB not closed? idfk man + add_promises.push(add_tx.complete); + + await Promise.all(add_promises); + init_tome_maps(); + tload_complete = true; + tdb.close(); } -function load_tome_init(init_func) { - if (tload_complete) { - console.log("Tome db already loaded, skipping load sequence"); - init_func(); - return; - } - let request = window.indexedDB.open('tome_db', TOME_DB_VERSION); +async function load_tome_init() { + return new Promise((resolve, reject) => { + let request = window.indexedDB.open('tome_db', TOME_DB_VERSION); - request.onerror = function() { - console.log("DB failed to open..."); - }; + request.onerror = function() { + reject("DB failed to open..."); + }; - request.onsuccess = function() { - (async function() { + request.onsuccess = async function() { tdb = request.result; - if (!treload) { - console.log("Using stored data...") - load_tome_local(init_func); + if (tload_in_progress) { + while (!tload_complete) { + await sleep(100); + } + console.log("Skipping load...") } else { - if (tload_in_progress) { - while (!tload_complete) { - await sleep(100); - } - console.log("Skipping load...") - init_func(); + tload_in_progress = true + if (treload) { + console.log("Using new data...") + await load_tome(); } else { - // Not 100% safe... whatever! - tload_in_progress = true - console.log("Using new data...") - load_tome(init_func); + console.log("Using stored data...") + await load_tome_local(); } } - })() - } - - request.onupgradeneeded = function(e) { - treload = true; - - let tdb = e.target.result; - - try { - tdb.deleteObjectStore('tome_db'); - } - catch (error) { - console.log("Could not delete tome DB. This is probably fine"); + resolve(); } - tdb.createObjectStore('tome_db'); + request.onupgradeneeded = function(e) { + treload = true; - console.log("DB setup complete..."); - } + let tdb = e.target.result; + + try { + tdb.deleteObjectStore('tome_db'); + } + catch (error) { + console.log("Could not delete tome DB. This is probably fine"); + } + + tdb.createObjectStore('tome_db'); + + console.log("DB setup complete..."); + } + }); } function init_tome_maps() { From 0166b7814d90f64d9c6ca2bfb1afd3f5e3261ac8 Mon Sep 17 00:00:00 2001 From: ferricles Date: Wed, 15 Jun 2022 12:53:51 -0700 Subject: [PATCH 07/30] crafted item version 4 build decode bug fix, crafted page slight bug fixes --- crafter/index.html | 2 +- js/crafter.js | 2 +- js/sq2builder.js | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crafter/index.html b/crafter/index.html index 645d2b6..76f6cf3 100644 --- a/crafter/index.html +++ b/crafter/index.html @@ -211,7 +211,7 @@
-
diff --git a/js/crafter.js b/js/crafter.js index dbbbfd6..66b9087 100644 --- a/js/crafter.js +++ b/js/crafter.js @@ -264,7 +264,7 @@ function populateFields() { */ function copyRecipeHash() { if (player_craft) { - copyTextToClipboard("CR-"+location.hash); + copyTextToClipboard("CR-"+location.hash.slice(1)); document.getElementById("copy-hash-button").textContent = "Copied!"; } } diff --git a/js/sq2builder.js b/js/sq2builder.js index b758e3b..2dc8e91 100644 --- a/js/sq2builder.js +++ b/js/sq2builder.js @@ -142,6 +142,7 @@ function parsePowdering(powder_info) { * Populate fields based on url, and calculate build. */ function decodeBuild(url_tag) { + console.log("decoding build"); if (url_tag) { //default values let equipment = [null, null, null, null, null, null, null, null, null]; @@ -168,11 +169,12 @@ function decodeBuild(url_tag) { let info_str = info[1]; let start_idx = 0; for (let i = 0; i < 9; ++i ) { - if (info_str.charAt(start_idx) === "-") { - equipment[i] = "CR-"+info_str.slice(start_idx+1, start_idx+18); - start_idx += 18; - } - else { + console.log(info_str.slice(start_idx,start_idx+3)); + if (info_str.slice(start_idx,start_idx+3) === "CR-") { + console.log(info_str.slice(start_idx, start_idx+20)); + equipment[i] = info_str.slice(start_idx, start_idx+20); + start_idx += 20; + } else { let equipment_str = info_str.slice(start_idx, start_idx+3); equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); start_idx += 3; From db7981024f04f2c7a1c938288f40b67e57b19167 Mon Sep 17 00:00:00 2001 From: ferricles Date: Wed, 15 Jun 2022 15:25:47 -0700 Subject: [PATCH 08/30] removed printouts --- js/craft.js | 1 - js/sq2builder.js | 3 --- 2 files changed, 4 deletions(-) diff --git a/js/craft.js b/js/craft.js index 81d632c..1d3ae41 100644 --- a/js/craft.js +++ b/js/craft.js @@ -190,7 +190,6 @@ class Craft{ let amounts = this.recipe.get("materials").map(x=> x.get("amount")); //Mat Multipliers - should work! matmult = (tierToMult[tiers[0]]*amounts[0] + tierToMult[tiers[1]]*amounts[1]) / (amounts[0]+amounts[1]); - console.log(matmult); let low = this.recipe.get("healthOrDamage")[0]; let high = this.recipe.get("healthOrDamage")[1]; diff --git a/js/sq2builder.js b/js/sq2builder.js index 2dc8e91..22f5f18 100644 --- a/js/sq2builder.js +++ b/js/sq2builder.js @@ -142,7 +142,6 @@ function parsePowdering(powder_info) { * Populate fields based on url, and calculate build. */ function decodeBuild(url_tag) { - console.log("decoding build"); if (url_tag) { //default values let equipment = [null, null, null, null, null, null, null, null, null]; @@ -169,9 +168,7 @@ function decodeBuild(url_tag) { let info_str = info[1]; let start_idx = 0; for (let i = 0; i < 9; ++i ) { - console.log(info_str.slice(start_idx,start_idx+3)); if (info_str.slice(start_idx,start_idx+3) === "CR-") { - console.log(info_str.slice(start_idx, start_idx+20)); equipment[i] = info_str.slice(start_idx, start_idx+20); start_idx += 20; } else { From 62a9a4f0c28f7ec995476542304b5054e3d3d5d1 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 00:42:49 -0700 Subject: [PATCH 09/30] Compute graph cleanup, prepping for full build calc (currently broke) --- builder/index.html | 7 +- js/build.js | 262 ++------------------------------------ js/build_encode_decode.js | 201 +++++++++++++++++++++++++++++ js/builder.js | 211 ++---------------------------- js/builder_graph.js | 56 ++++++++ js/computation_graph.js | 81 +++++++++--- 6 files changed, 346 insertions(+), 472 deletions(-) create mode 100644 js/build_encode_decode.js diff --git a/builder/index.html b/builder/index.html index d7a802c..d9c3e8f 100644 --- a/builder/index.html +++ b/builder/index.html @@ -313,10 +313,10 @@
- +
- +
@@ -1399,8 +1399,9 @@ - + + diff --git a/js/build.js b/js/build.js index 84ff86a..447162d 100644 --- a/js/build.js +++ b/js/build.js @@ -100,238 +100,12 @@ class Build{ * @param {Number[]} powders : Powder application. List of lists of integers (powder IDs). * In order: boots, Chestplate, Leggings, Boots, Weapon. * @param {Object[]} inputerrors : List of instances of error-like classes. + * + * @param {Object[]} tomes: List of tomes. + * In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome. + * 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building) */ - constructor(level,equipment, powders, externalStats, inputerrors=[]){ - - let errors = inputerrors; - //this contains the Craft objects, if there are any crafted items. this.boots, etc. will contain the statMap of the Craft (which is built to be an expandedItem). - this.craftedItems = []; - this.customItems = []; - // NOTE: powders is just an array of arrays of powder IDs. Not powder objects. - this.powders = powders; - if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") { - const helmet = itemMap.get(equipment[0]); - this.powders[0] = this.powders[0].slice(0,helmet.slots); - this.helmet = expandItem(helmet, this.powders[0]); - } else { - try { - let helmet = getCustomFromHash(equipment[0]) ? getCustomFromHash(equipment[0]) : (getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : undefined); - if (helmet.statMap.get("type") !== "helmet") { - throw new Error("Not a helmet"); - } - this.powders[0] = this.powders[0].slice(0,helmet.statMap.get("slots")); - helmet.statMap.set("powders",this.powders[0].slice()); - this.helmet = helmet.statMap; - applyArmorPowders(this.helmet, this.powders[0]); - if (this.helmet.get("custom")) { - this.customItems.push(helmet); - } else if (this.helmet.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(helmet); - } - - } catch (Error) { - const helmet = itemMap.get("No Helmet"); - this.powders[0] = this.powders[0].slice(0,helmet.slots); - this.helmet = expandItem(helmet, this.powders[0]); - errors.push(new ItemNotFound(equipment[0], "helmet", true)); - } - } - if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") { - const chestplate = itemMap.get(equipment[1]); - this.powders[1] = this.powders[1].slice(0,chestplate.slots); - this.chestplate = expandItem(chestplate, this.powders[1]); - } else { - try { - let chestplate = getCustomFromHash(equipment[1]) ? getCustomFromHash(equipment[1]) : (getCraftFromHash(equipment[1]) ? getCraftFromHash(equipment[1]) : undefined); - if (chestplate.statMap.get("type") !== "chestplate") { - throw new Error("Not a chestplate"); - } - this.powders[1] = this.powders[1].slice(0,chestplate.statMap.get("slots")); - chestplate.statMap.set("powders",this.powders[1].slice()); - this.chestplate = chestplate.statMap; - applyArmorPowders(this.chestplate, this.powders[1]); - if (this.chestplate.get("custom")) { - this.customItems.push(chestplate); - } else if (this.chestplate.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(chestplate); - } - } catch (Error) { - console.log(Error); - const chestplate = itemMap.get("No Chestplate"); - this.powders[1] = this.powders[1].slice(0,chestplate.slots); - this.chestplate = expandItem(chestplate, this.powders[1]); - errors.push(new ItemNotFound(equipment[1], "chestplate", true)); - } - } - if (itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") { - const leggings = itemMap.get(equipment[2]); - this.powders[2] = this.powders[2].slice(0,leggings.slots); - this.leggings = expandItem(leggings, this.powders[2]); - } else { - try { - let leggings = getCustomFromHash(equipment[2]) ? getCustomFromHash(equipment[2]) : (getCraftFromHash(equipment[2]) ? getCraftFromHash(equipment[2]) : undefined); - if (leggings.statMap.get("type") !== "leggings") { - throw new Error("Not a leggings"); - } - this.powders[2] = this.powders[2].slice(0,leggings.statMap.get("slots")); - leggings.statMap.set("powders",this.powders[2].slice()); - this.leggings = leggings.statMap; - applyArmorPowders(this.leggings, this.powders[2]); - if (this.leggings.get("custom")) { - this.customItems.push(leggings); - } else if (this.leggings.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(leggings); - } - } catch (Error) { - const leggings = itemMap.get("No Leggings"); - this.powders[2] = this.powders[2].slice(0,leggings.slots); - this.leggings = expandItem(leggings, this.powders[2]); - errors.push(new ItemNotFound(equipment[2], "leggings", true)); - } - } - if (itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") { - const boots = itemMap.get(equipment[3]); - this.powders[3] = this.powders[3].slice(0,boots.slots); - this.boots = expandItem(boots, this.powders[3]); - } else { - try { - let boots = getCustomFromHash(equipment[3]) ? getCustomFromHash(equipment[3]) : (getCraftFromHash(equipment[3]) ? getCraftFromHash(equipment[3]) : undefined); - if (boots.statMap.get("type") !== "boots") { - throw new Error("Not a boots"); - } - this.powders[3] = this.powders[3].slice(0,boots.statMap.get("slots")); - boots.statMap.set("powders",this.powders[3].slice()); - this.boots = boots.statMap; - applyArmorPowders(this.boots, this.powders[3]); - if (this.boots.get("custom")) { - this.customItems.push(boots); - } else if (this.boots.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(boots); - } - } catch (Error) { - const boots = itemMap.get("No Boots"); - this.powders[3] = this.powders[3].slice(0,boots.slots); - this.boots = expandItem(boots, this.powders[3]); - errors.push(new ItemNotFound(equipment[3], "boots", true)); - } - } - if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") { - const ring = itemMap.get(equipment[4]); - this.ring1 = expandItem(ring, []); - }else{ - try { - let ring = getCustomFromHash(equipment[4]) ? getCustomFromHash(equipment[4]) : (getCraftFromHash(equipment[4]) ? getCraftFromHash(equipment[4]) : undefined); - if (ring.statMap.get("type") !== "ring") { - throw new Error("Not a ring"); - } - this.ring1 = ring.statMap; - if (this.ring1.get("custom")) { - this.customItems.push(ring); - } else if (this.ring1.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(ring); - } - } catch (Error) { - const ring = itemMap.get("No Ring 1"); - this.ring1 = expandItem(ring, []); - errors.push(new ItemNotFound(equipment[4], "ring1", true, "ring")); - } - } - if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") { - const ring = itemMap.get(equipment[5]); - this.ring2 = expandItem(ring, []); - }else{ - try { - let ring = getCustomFromHash(equipment[5]) ? getCustomFromHash(equipment[5]) : (getCraftFromHash(equipment[5]) ? getCraftFromHash(equipment[5]) : undefined); - if (ring.statMap.get("type") !== "ring") { - throw new Error("Not a ring"); - } - this.ring2 = ring.statMap; - if (this.ring2.get("custom")) { - this.customItems.push(ring); - } else if (this.ring2.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(ring); - } - } catch (Error) { - const ring = itemMap.get("No Ring 2"); - this.ring2 = expandItem(ring, []); - errors.push(new ItemNotFound(equipment[5], "ring2", true, "ring")); - } - } - if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") { - const bracelet = itemMap.get(equipment[6]); - this.bracelet = expandItem(bracelet, []); - }else{ - try { - let bracelet = getCustomFromHash(equipment[6]) ? getCustomFromHash(equipment[6]) : (getCraftFromHash(equipment[6]) ? getCraftFromHash(equipment[6]) : undefined); - if (bracelet.statMap.get("type") !== "bracelet") { - throw new Error("Not a bracelet"); - } - this.bracelet = bracelet.statMap; - if (this.bracelet.get("custom")) { - this.customItems.push(bracelet); - } else if (this.bracelet.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(bracelet); - } - } catch (Error) { - const bracelet = itemMap.get("No Bracelet"); - this.bracelet = expandItem(bracelet, []); - errors.push(new ItemNotFound(equipment[6], "bracelet", true)); - } - } - if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") { - const necklace = itemMap.get(equipment[7]); - this.necklace = expandItem(necklace, []); - }else{ - try { - let necklace = getCustomFromHash(equipment[7]) ? getCustomFromHash(equipment[7]) : (getCraftFromHash(equipment[7]) ? getCraftFromHash(equipment[7]) : undefined); - if (necklace.statMap.get("type") !== "necklace") { - throw new Error("Not a necklace"); - } - this.necklace = necklace.statMap; - if (this.necklace.get("custom")) { - this.customItems.push(necklace); - } else if (this.necklace.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(necklace); - } - } catch (Error) { - const necklace = itemMap.get("No Necklace"); - this.necklace = expandItem(necklace, []); - errors.push(new ItemNotFound(equipment[7], "necklace", true)); - } - } - if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") { - const weapon = itemMap.get(equipment[8]); - this.powders[4] = this.powders[4].slice(0,weapon.slots); - this.weapon = expandItem(weapon, this.powders[4]); - if (equipment[8] !== "No Weapon") { - document.getElementsByClassName("powder-specials")[0].style.display = "grid"; - } else { - document.getElementsByClassName("powder-specials")[0].style.display = "none"; - } - }else{ - try { - let weapon = getCustomFromHash(equipment[8]) ? getCustomFromHash(equipment[8]) : (getCraftFromHash(equipment[8]) ? getCraftFromHash(equipment[8]) : undefined); - if (weapon.statMap.get("category") !== "weapon") { - throw new Error("Not a weapon"); - } - this.weapon = weapon.statMap; - if (this.weapon.get("custom")) { - this.customItems.push(weapon); - } else if (this.weapon.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(weapon); - } - this.powders[4] = this.powders[4].slice(0,this.weapon.get("slots")); - this.weapon.set("powders",this.powders[4].slice()); - document.getElementsByClassName("powder-specials")[0].style.display = "grid"; - } catch (Error) { - const weapon = itemMap.get("No Weapon"); - this.powders[4] = this.powders[4].slice(0,weapon.slots); - this.weapon = expandItem(weapon, this.powders[4]); - document.getElementsByClassName("powder-specials")[0].style.display = "none"; - errors.push(new ItemNotFound(equipment[8], "weapon", true)); - } - } - //console.log(this.craftedItems) + constructor(level, items, tomes, weapon){ if (level < 1) { //Should these be constants? this.level = 1; @@ -348,10 +122,12 @@ class Build{ document.getElementById("level-choice").value = this.level; this.availableSkillpoints = levelToSkillPoints(this.level); - this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ]; - this.items = this.equipment.concat([this.weapon]); + this.equipment = items; + this.tomes = tomes; + this.weapon = weapon; + this.items = this.equipment.concat([this.weapon]).concat(this.tomes); // return [equip_order, best_skillpoints, final_skillpoints, best_total]; - let result = calculate_skillpoints(this.equipment, this.weapon); + let result = calculate_skillpoints(this.equipment.concat(this.tomes), this.weapon); console.log(result); this.equip_order = result[0]; // How many skillpoints the player had to assign (5 number) @@ -361,28 +137,14 @@ class Build{ // How many skillpoints assigned (1 number, sum of base_skillpoints) this.assigned_skillpoints = result[3]; this.activeSetCounts = result[4]; - - // For strength boosts like warscream, vanish, etc. - this.damageMultiplier = 1.0; - this.defenseMultiplier = 1.0; - - // For other external boosts ;-; - this.externalStats = externalStats; this.initBuildStats(); - - // Remove every error before adding specific ones - for (let i of document.getElementsByClassName("error")) { - i.textContent = ""; - } - this.errors = errors; - if (errors.length > 0) this.errored = true; } /*Returns build in string format */ toString(){ - return [this.equipment,this.weapon].flat(); + return [this.equipment,this.weapon,this.tomes].flat(); } /* Getters */ @@ -392,7 +154,7 @@ class Build{ } getBaseSpellCost(spellIdx, cost) { - cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); + // old intelligence: cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); cost += this.statMap.get("spRaw"+spellIdx); return Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100)); } diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js new file mode 100644 index 0000000..9b168b1 --- /dev/null +++ b/js/build_encode_decode.js @@ -0,0 +1,201 @@ + +/* + * Populate fields based on url, and calculate build. + */ +function decodeBuild(url_tag) { + if (url_tag) { + //default values + let equipment = [null, null, null, null, null, null, null, null, null]; + let tomes = [null, null, null, null, null, null, null]; + let powdering = ["", "", "", "", ""]; + let info = url_tag.split("_"); + let version = info[0]; + let save_skp = false; + let skillpoints = [0, 0, 0, 0, 0]; + let level = 106; + + let version_number = parseInt(version) + //equipment (items) + // TODO: use filters + if (version_number < 4) { + let equipments = info[1]; + for (let i = 0; i < 9; ++i ) { + let equipment_str = equipments.slice(i*3,i*3+3); + equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); + } + info[1] = equipments.slice(27); + } + else if (version_number == 4) { + let info_str = info[1]; + let start_idx = 0; + for (let i = 0; i < 9; ++i ) { + if (info_str.charAt(start_idx) === "-") { + equipment[i] = "CR-"+info_str.slice(start_idx+1, start_idx+18); + start_idx += 18; + } + else { + let equipment_str = info_str.slice(start_idx, start_idx+3); + equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); + start_idx += 3; + } + } + info[1] = info_str.slice(start_idx); + } + else if (version_number <= 6) { + let info_str = info[1]; + let start_idx = 0; + for (let i = 0; i < 9; ++i ) { + if (info_str.slice(start_idx,start_idx+3) === "CR-") { + equipment[i] = info_str.slice(start_idx, start_idx+20); + start_idx += 20; + } else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") { + let len = Base64.toInt(info_str.slice(start_idx,start_idx+3)); + equipment[i] = info_str.slice(start_idx+3,start_idx+3+len); + start_idx += (3+len); + } else { + let equipment_str = info_str.slice(start_idx, start_idx+3); + equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); + start_idx += 3; + } + } + info[1] = info_str.slice(start_idx); + } + //constant in all versions + for (let i in equipment) { + setValue(equipmentInputs[i], equipment[i]); + } + + //level, skill point assignments, and powdering + if (version_number == 1) { + let powder_info = info[1]; + let res = parsePowdering(powder_info); + powdering = res[0]; + } else if (version_number == 2) { + save_skp = true; + let skillpoint_info = info[1].slice(0, 10); + for (let i = 0; i < 5; ++i ) { + skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); + } + + let powder_info = info[1].slice(10); + let res = parsePowdering(powder_info); + powdering = res[0]; + } else if (version_number <= 6){ + level = Base64.toInt(info[1].slice(10,12)); + setValue("level-choice",level); + save_skp = true; + let skillpoint_info = info[1].slice(0, 10); + for (let i = 0; i < 5; ++i ) { + skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); + } + + let powder_info = info[1].slice(12); + + let res = parsePowdering(powder_info); + powdering = res[0]; + info[1] = res[1]; + } + // Tomes. + if (version == 6) { + //tome values do not appear in anything before v6. + for (let i = 0; i < 7; ++i) { + let tome_str = info[1].charAt(i); + for (let i in tomes) { + setValue(tomeInputs[i], getTomeNameFromID(Base64.toInt(tome_str))); + } + } + info[1] = info[1].slice(7); + } + + for (let i in powderInputs) { + setValue(powderInputs[i], powdering[i]); + } + } +} + +/* Stores the entire build in a string using B64 encoding and adds it to the URL. +*/ +function encodeBuild(build) { + + if (build) { + let build_string; + + //V6 encoding - Tomes + build_version = 4; + build_string = ""; + tome_string = ""; + + let crafted_idx = 0; + let custom_idx = 0; + for (const item of build.items) { + + if (item.get("custom")) { + let custom = "CI-"+encodeCustom(build.customItems[custom_idx],true); + build_string += Base64.fromIntN(custom.length, 3) + custom; + custom_idx += 1; + build_version = Math.max(build_version, 5); + } else if (item.get("crafted")) { + build_string += "CR-"+encodeCraft(build.craftedItems[crafted_idx]); + crafted_idx += 1; + } else if (item.get("category") === "tome") { + let tome_id = item.get("id"); + if (tome_id <= 60) { + // valid normal tome. ID 61-63 is for NONE tomes. + build_version = Math.max(build_version, 6); + } + tome_string += Base64.fromIntN(tome_id, 1); + } else { + build_string += Base64.fromIntN(item.get("id"), 3); + } + } + + for (const skp of skp_order) { + build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 + } + build_string += Base64.fromIntN(build.level, 2); + for (const _powderset of build.powders) { + let n_bits = Math.ceil(_powderset.length / 6); + build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. + // Slice copy. + let powderset = _powderset.slice(); + while (powderset.length != 0) { + let firstSix = powderset.slice(0,6).reverse(); + let powder_hash = 0; + for (const powder of firstSix) { + powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first. + } + build_string += Base64.fromIntN(powder_hash, 5); + powderset = powderset.slice(6); + } + } + build_string += tome_string; + + return build_version.toString() + "_" + build_string; + } +} + +function copyBuild(build) { + if (build) { + copyTextToClipboard(url_base+location.hash); + document.getElementById("copy-button").textContent = "Copied!"; + } +} + +function shareBuild(build) { + if (build) { + let text = url_base+location.hash+"\n"+ + "WynnBuilder build:\n"+ + "> "+build.helmet.get("displayName")+"\n"+ + "> "+build.chestplate.get("displayName")+"\n"+ + "> "+build.leggings.get("displayName")+"\n"+ + "> "+build.boots.get("displayName")+"\n"+ + "> "+build.ring1.get("displayName")+"\n"+ + "> "+build.ring2.get("displayName")+"\n"+ + "> "+build.bracelet.get("displayName")+"\n"+ + "> "+build.necklace.get("displayName")+"\n"+ + "> "+build.weapon.get("displayName")+" ["+build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]"; + copyTextToClipboard(text); + document.getElementById("share-button").textContent = "Copied!"; + } +} + diff --git a/js/builder.js b/js/builder.js index 1e51e31..249bf32 100644 --- a/js/builder.js +++ b/js/builder.js @@ -35,206 +35,6 @@ function parsePowdering(powder_info) { return [powdering, powder_info]; } -/* - * Populate fields based on url, and calculate build. - */ -function decodeBuild(url_tag) { - if (url_tag) { - //default values - let equipment = [null, null, null, null, null, null, null, null, null]; - let tomes = [null, null, null, null, null, null, null]; - let powdering = ["", "", "", "", ""]; - let info = url_tag.split("_"); - let version = info[0]; - let save_skp = false; - let skillpoints = [0, 0, 0, 0, 0]; - let level = 106; - - let version_number = parseInt(version) - //equipment (items) - // TODO: use filters - if (version_number < 4) { - let equipments = info[1]; - for (let i = 0; i < 9; ++i ) { - let equipment_str = equipments.slice(i*3,i*3+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - } - info[1] = equipments.slice(27); - } - else if (version_number == 4) { - let info_str = info[1]; - let start_idx = 0; - for (let i = 0; i < 9; ++i ) { - if (info_str.charAt(start_idx) === "-") { - equipment[i] = "CR-"+info_str.slice(start_idx+1, start_idx+18); - start_idx += 18; - } - else { - let equipment_str = info_str.slice(start_idx, start_idx+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - start_idx += 3; - } - } - info[1] = info_str.slice(start_idx); - } - else if (version_number <= 6) { - let info_str = info[1]; - let start_idx = 0; - for (let i = 0; i < 9; ++i ) { - if (info_str.slice(start_idx,start_idx+3) === "CR-") { - equipment[i] = info_str.slice(start_idx, start_idx+20); - start_idx += 20; - } else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") { - let len = Base64.toInt(info_str.slice(start_idx,start_idx+3)); - equipment[i] = info_str.slice(start_idx+3,start_idx+3+len); - start_idx += (3+len); - } else { - let equipment_str = info_str.slice(start_idx, start_idx+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - start_idx += 3; - } - } - info[1] = info_str.slice(start_idx); - } - //constant in all versions - for (let i in equipment) { - setValue(equipmentInputs[i], equipment[i]); - } - - //level, skill point assignments, and powdering - if (version_number == 1) { - let powder_info = info[1]; - let res = parsePowdering(powder_info); - powdering = res[0]; - } else if (version_number == 2) { - save_skp = true; - let skillpoint_info = info[1].slice(0, 10); - for (let i = 0; i < 5; ++i ) { - skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); - } - - let powder_info = info[1].slice(10); - let res = parsePowdering(powder_info); - powdering = res[0]; - } else if (version_number <= 6){ - level = Base64.toInt(info[1].slice(10,12)); - setValue("level-choice",level); - save_skp = true; - let skillpoint_info = info[1].slice(0, 10); - for (let i = 0; i < 5; ++i ) { - skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); - } - - let powder_info = info[1].slice(12); - - let res = parsePowdering(powder_info); - powdering = res[0]; - info[1] = res[1]; - } - // Tomes. - if (version == 6) { - //tome values do not appear in anything before v6. - for (let i = 0; i < 7; ++i) { - let tome_str = info[1].charAt(i); - for (let i in tomes) { - setValue(tomeInputs[i], getTomeNameFromID(Base64.toInt(tome_str))); - } - } - info[1] = info[1].slice(7); - } - - for (let i in powderInputs) { - setValue(powderInputs[i], powdering[i]); - } - } -} - -/* Stores the entire build in a string using B64 encoding and adds it to the URL. -*/ -function encodeBuild() { - - if (player_build) { - let build_string; - - //V6 encoding - Tomes - build_version = 4; - build_string = ""; - tome_string = ""; - - let crafted_idx = 0; - let custom_idx = 0; - for (const item of player_build.items) { - - if (item.get("custom")) { - let custom = "CI-"+encodeCustom(player_build.customItems[custom_idx],true); - build_string += Base64.fromIntN(custom.length, 3) + custom; - custom_idx += 1; - build_version = Math.max(build_version, 5); - } else if (item.get("crafted")) { - build_string += "CR-"+encodeCraft(player_build.craftedItems[crafted_idx]); - crafted_idx += 1; - } else if (item.get("category") === "tome") { - let tome_id = item.get("id"); - if (tome_id <= 60) { - // valid normal tome. ID 61-63 is for NONE tomes. - build_version = Math.max(build_version, 6); - } - tome_string += Base64.fromIntN(tome_id, 1); - } else { - build_string += Base64.fromIntN(item.get("id"), 3); - } - } - - for (const skp of skp_order) { - build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 - } - build_string += Base64.fromIntN(player_build.level, 2); - for (const _powderset of player_build.powders) { - let n_bits = Math.ceil(_powderset.length / 6); - build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. - // Slice copy. - let powderset = _powderset.slice(); - while (powderset.length != 0) { - let firstSix = powderset.slice(0,6).reverse(); - let powder_hash = 0; - for (const powder of firstSix) { - powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first. - } - build_string += Base64.fromIntN(powder_hash, 5); - powderset = powderset.slice(6); - } - } - build_string += tome_string; - - return build_version.toString() + "_" + build_string; - } -} - -function copyBuild() { - if (player_build) { - copyTextToClipboard(url_base+location.hash); - document.getElementById("copy-button").textContent = "Copied!"; - } -} - -function shareBuild() { - if (player_build) { - let text = url_base+location.hash+"\n"+ - "WynnBuilder build:\n"+ - "> "+player_build.helmet.get("displayName")+"\n"+ - "> "+player_build.chestplate.get("displayName")+"\n"+ - "> "+player_build.leggings.get("displayName")+"\n"+ - "> "+player_build.boots.get("displayName")+"\n"+ - "> "+player_build.ring1.get("displayName")+"\n"+ - "> "+player_build.ring2.get("displayName")+"\n"+ - "> "+player_build.bracelet.get("displayName")+"\n"+ - "> "+player_build.necklace.get("displayName")+"\n"+ - "> "+player_build.weapon.get("displayName")+" ["+player_build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]"; - copyTextToClipboard(text); - document.getElementById("share-button").textContent = "Copied!"; - } -} - function populateBuildList() { const buildList = document.getElementById("build-choice"); const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds")); @@ -250,7 +50,7 @@ function saveBuild() { if (player_build) { const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds")); const saveName = document.getElementById("build-name").value; - const encodedBuild = encodeBuild(); + const encodedBuild = encodeBuild(player_build); if ((!Object.keys(savedBuilds).includes(saveName) || document.getElementById("saved-error").textContent !== "") && encodedBuild !== "") { savedBuilds[saveName] = encodedBuild.replace("#", ""); @@ -330,6 +130,15 @@ function toggleButton(button_id) { } } +// toggle tab +function toggle_tab(tab) { + if (document.querySelector("#"+tab).style.display == "none") { + document.querySelector("#"+tab).style.display = ""; + } else { + document.querySelector("#"+tab).style.display = "none"; + } +} + // TODO: Learn and use await function init() { console.log("builder.js init"); diff --git a/js/builder_graph.js b/js/builder_graph.js index fd0de13..111cd92 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1,5 +1,52 @@ +class BuildEncodeNode extends ComputeNode { + constructor() { + super("builder-encode"); + } + + compute_func(input_map) { + if (input_map.size !== 1) { throw "BuildEncodeNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + return encodeBuild(build); + } +} + +class URLUpdateNode extends ComputeNode { + constructor() { + super("builder-url-update"); + } + + compute_func(input_map) { + if (input_map.size !== 1) { throw "URLUpdateNode accepts exactly one input (build_str)"; } + const [build_str] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + location.hash = build_str; + } +} + +class BuildAssembleNode extends ComputeNode { + constructor() { + super("builder-make-build"); + } + + compute_func(input_map) { + let equipments = [ + input_map.get('helmet-input'), + input_map.get('chestplate-input'), + input_map.get('leggings-input'), + input_map.get('boots-input'), + input_map.get('ring1-input'), + input_map.get('ring2-input'), + input_map.get('bracelet-input'), + input_map.get('necklace-input') + ]; + let weapon = input_map.get('weapon-input'); + let level = input_map.get('level-input'); + console.log('build node run'); + return new Build(level, equipments, [], weapon); + } +} + let item_nodes = []; document.addEventListener('DOMContentLoaded', function() { @@ -16,6 +63,14 @@ document.addEventListener('DOMContentLoaded', function() { } let weapon_image = document.getElementById("weapon-img"); new WeaponDisplayNode('weapon-type', weapon_image).link_to(item_nodes[8]); + let level_input = new InputNode('level-input', document.getElementById('level-choice')); + new PrintNode('lvl-debug').link_to(level_input); + + let build_node = new BuildAssembleNode(); + for (const input of item_nodes) { + build_node.link_to(input); + } + build_node.link_to(level_input); console.log("Set up graph"); }); @@ -98,6 +153,7 @@ function init_autocomplete() { if (event.detail.selection.value) { event.target.value = event.detail.selection.value; } + event.target.dispatchEvent(new Event('input')); }, }, } diff --git a/js/computation_graph.js b/js/computation_graph.js index 417c9bf..155d95a 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -6,14 +6,16 @@ class ComputeNode { * @param name : Name of the node (string). Must be unique. Must "fit in" a JS string (terminated by single quotes). */ constructor(name) { - this.inputs = []; + this.inputs = []; // parent nodes this.children = []; this.value = 0; this.name = name; this.update_task = null; this.update_time = Date.now(); this.fail_cb = false; // Set to true to force updates even if parent failed. - this.calc_inputs = new Map(); + this.dirty = false; + this.inputs_dirty = new Map(); + this.inputs_dirty_count = 0; } /** @@ -24,7 +26,19 @@ class ComputeNode { return; } this.update_time = timestamp; - this.set_value(this.compute_func(this.calc_inputs)); + + if (this.inputs_dirty_count != 0) { + return; + } + let calc_inputs = new Map(); + for (const input of this.inputs) { + calc_inputs.set(input.name, input.value); + } + this.value = this.compute_func(calc_inputs); + this.dirty = false; + for (const child of this.children) { + child.mark_input_clean(this.name, this.value, timestamp); + } } /** @@ -40,22 +54,35 @@ class ComputeNode { } /** - * Set an input value. Propagates calculation if all inputs are present. + * Mark parent as not dirty. Propagates calculation if all inputs are present. */ - set_input(input_name, value, timestamp) { - if (value || this.fail_cb) { - this.calc_inputs.set(input_name, value) - if (this.calc_inputs.size === this.inputs.length) { - this.update(timestamp) + mark_input_clean(input_name, value, timestamp) { + if (value !== null || this.fail_cb) { + if (this.inputs_dirty.get(input_name)) { + this.inputs_dirty.set(input_name, false); + this.inputs_dirty_count -= 1; + } + if (this.inputs_dirty_count === 0) { + this.update(timestamp); } } } - /** - * Remove cached input values to this calculation. - */ - clear_cache() { - this.calc_inputs = new Map(); + mark_input_dirty(input_name) { + if (!this.inputs_dirty.get(input_name)) { + this.inputs_dirty.set(input_name, true); + this.inputs_dirty_count += 1; + } + } + + mark_dirty() { + if (!this.dirty) { + this.dirty = true; + for (const child of this.children) { + child.mark_input_dirty(this.name); + child.mark_dirty(); + } + } } /** @@ -74,6 +101,10 @@ class ComputeNode { link_to(parent_node) { this.inputs.push(parent_node) + this.inputs_dirty.set(parent_node.name, parent_node.dirty); + if (parent_node.dirty) { + this.inputs_dirty_count += 1; + } parent_node.children.push(this); } } @@ -87,6 +118,7 @@ function calcSchedule(node) { if (node.update_task !== null) { clearTimeout(node.update_task); } + node.mark_dirty(); node.update_task = setTimeout(function() { const timestamp = Date.now(); node.update(timestamp); @@ -107,10 +139,25 @@ class PrintNode extends ComputeNode { } } +/** + * Node for getting an input from an input field. + */ +class InputNode extends ComputeNode { + constructor(name, input_field) { + super(name); + this.input_field = input_field; + this.input_field.addEventListener("input", () => calcSchedule(this)); + } + + compute_func(input_map) { + return this.input_field.value; + } +} + /** * Node for getting an item's stats from an item input field. */ -class ItemInputNode extends ComputeNode { +class ItemInputNode extends InputNode { /** * Make an item stat pulling compute node. * @@ -119,9 +166,7 @@ class ItemInputNode extends ComputeNode { * @param none_item: Item object to use as the "none" for this field. */ constructor(name, item_input_field, none_item) { - super(name); - this.input_field = item_input_field; - this.input_field.addEventListener("input", () => calcSchedule(this)); + super(name, item_input_field); this.none_item = new Item(none_item); } From 8db34d68a75f6743308d19d928a756325c4426e8 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 09:49:04 -0700 Subject: [PATCH 10/30] Almost working spell damage calculation --- js/build.js | 45 +++++++-------- js/build_constants.js | 2 +- js/build_encode_decode.js | 26 ++++++++- js/builder.js | 38 +++++-------- js/builder_graph.js | 113 +++++++++++++++++++++++++++++++++++--- js/computation_graph.js | 29 +++++----- js/damage_calc.js | 19 +------ js/load.js | 21 +++++-- js/skillpoints.js | 9 +-- js/sq2display.js | 24 ++++---- 10 files changed, 212 insertions(+), 114 deletions(-) diff --git a/js/build.js b/js/build.js index 447162d..00300c1 100644 --- a/js/build.js +++ b/js/build.js @@ -127,7 +127,9 @@ class Build{ this.weapon = weapon; this.items = this.equipment.concat([this.weapon]).concat(this.tomes); // return [equip_order, best_skillpoints, final_skillpoints, best_total]; - let result = calculate_skillpoints(this.equipment.concat(this.tomes), this.weapon); + + // calc skillpoints requires statmaps only + let result = calculate_skillpoints(this.equipment.concat(this.tomes).map((x) => x.statMap), this.weapon.statMap); console.log(result); this.equip_order = result[0]; // How many skillpoints the player had to assign (5 number) @@ -165,8 +167,9 @@ class Build{ */ getMeleeStats(){ const stats = this.statMap; - if (this.weapon.get("tier") === "Crafted") { - stats.set("damageBases", [this.weapon.get("nDamBaseHigh"),this.weapon.get("eDamBaseHigh"),this.weapon.get("tDamBaseHigh"),this.weapon.get("wDamBaseHigh"),this.weapon.get("fDamBaseHigh"),this.weapon.get("aDamBaseHigh")]); + const weapon_stats = this.weapon.statMap; + if (weapon_stats.get("tier") === "Crafted") { + stats.set("damageBases", [weapon_stats.get("nDamBaseHigh"),weapon_stats.get("eDamBaseHigh"),weapon_stats.get("tDamBaseHigh"),weapon_stats.get("wDamBaseHigh"),weapon_stats.get("fDamBaseHigh"),weapon_stats.get("aDamBaseHigh")]); } let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier"); if(adjAtkSpd > 6){ @@ -176,13 +179,13 @@ class Build{ } let damage_mult = 1; - if (this.weapon.get("type") === "relik") { + if (weapon_stats.get("type") === "relik") { damage_mult = 0.99; // CURSE YOU WYNNCRAFT //One day we will create WynnWynn and no longer have shaman 99% melee injustice. //In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams. } // 0spellmult for melee damage. - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct") + this.externalStats.get("mdPct"), 0, this.weapon, this.total_skillpoints, damage_mult * this.damageMultiplier, this.externalStats); + let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct"), 0, this.weapon.statMap, this.total_skillpoints, damage_mult * this.damageMultiplier); let dex = this.total_skillpoints[1]; @@ -217,8 +220,8 @@ class Build{ defenseStats.push(totalHp); //EHP let ehp = [totalHp, totalHp]; - let defMult = classDefenseMultipliers.get(this.weapon.get("type")); - ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); + let defMult = classDefenseMultipliers.get(this.weapon.statMap.get("type")); + ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); ehp[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); defenseStats.push(ehp); //HPR @@ -259,26 +262,27 @@ class Build{ let major_ids = new Set(); for (const item of this.items){ - for (let [id, value] of item.get("maxRolls")) { + const item_stats = item.statMap; + for (let [id, value] of item_stats.get("maxRolls")) { if (staticIDs.includes(id)) { continue; } statMap.set(id,(statMap.get(id) || 0)+value); } for (const staticID of staticIDs) { - if (item.get(staticID)) { - statMap.set(staticID, statMap.get(staticID) + item.get(staticID)); + if (item_stats.get(staticID)) { + statMap.set(staticID, statMap.get(staticID) + item_stats.get(staticID)); } } - if (item.get("majorIds")) { - for (const major_id of item.get("majorIds")) { + if (item_stats.get("majorIds")) { + for (const major_id of item_stats.get("majorIds")) { major_ids.add(major_id); } } } statMap.set("activeMajorIDs", major_ids); for (const [setName, count] of this.activeSetCounts) { - const bonus = sets[setName].bonuses[count-1]; + const bonus = sets.get(setName).bonuses[count-1]; for (const id in bonus) { if (skp_order.includes(id)) { // pass. Don't include skillpoints in ids @@ -291,16 +295,8 @@ class Build{ statMap.set("poisonPct", 100); // The stuff relevant for damage calculation!!! @ferricles - statMap.set("atkSpd", this.weapon.get("atkSpd")); + statMap.set("atkSpd", this.weapon.statMap.get("atkSpd")); - for (const x of skp_elements) { - this.externalStats.set(x + "DamPct", 0); - } - this.externalStats.set("mdPct", 0); - this.externalStats.set("sdPct", 0); - this.externalStats.set("damageBonus", [0, 0, 0, 0, 0]); - this.externalStats.set("defBonus",[0, 0, 0, 0, 0]); - this.externalStats.set("poisonPct", 0); this.statMap = statMap; this.aggregateStats(); @@ -308,10 +304,11 @@ class Build{ aggregateStats() { let statMap = this.statMap; - statMap.set("damageRaw", [this.weapon.get("nDam"), this.weapon.get("eDam"), this.weapon.get("tDam"), this.weapon.get("wDam"), this.weapon.get("fDam"), this.weapon.get("aDam")]); + let weapon_stats = this.weapon.statMap; + statMap.set("damageRaw", [weapon_stats.get("nDam"), weapon_stats.get("eDam"), weapon_stats.get("tDam"), weapon_stats.get("wDam"), weapon_stats.get("fDam"), weapon_stats.get("aDam")]); statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]); statMap.set("defRaw", [statMap.get("eDef"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]); statMap.set("defBonus", [statMap.get("eDefPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]); - statMap.set("defMult", classDefenseMultipliers.get(this.weapon.get("type"))); + statMap.set("defMult", classDefenseMultipliers.get(weapon_stats.get("type"))); } } diff --git a/js/build_constants.js b/js/build_constants.js index fbcbb3f..2983328 100644 --- a/js/build_constants.js +++ b/js/build_constants.js @@ -88,7 +88,7 @@ let equipmentInputs = equipment_fields.map(x => x + "-choice"); let buildFields = equipment_fields.map(x => x+"-tooltip").concat(tome_fields.map(x => x + "-tooltip")); let tomeInputs = tome_fields.map(x => x + "-choice"); -let powderInputs = [ +let powder_inputs = [ "helmet-powder", "chestplate-powder", "leggings-powder", diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index 9b168b1..0929e4e 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -1,3 +1,25 @@ +function parsePowdering(powder_info) { + // TODO: Make this run in linear instead of quadratic time... ew + let powdering = []; + for (let i = 0; i < 5; ++i) { + let powders = ""; + let n_blocks = Base64.toInt(powder_info.charAt(0)); + // console.log(n_blocks + " blocks"); + powder_info = powder_info.slice(1); + for (let j = 0; j < n_blocks; ++j) { + let block = powder_info.slice(0,5); + console.log(block); + let six_powders = Base64.toInt(block); + for (let k = 0; k < 6 && six_powders != 0; ++k) { + powders += powderNames.get((six_powders & 0x1f) - 1); + six_powders >>>= 5; + } + powder_info = powder_info.slice(5); + } + powdering[i] = powders; + } + return [powdering, powder_info]; +} /* * Populate fields based on url, and calculate build. @@ -107,8 +129,8 @@ function decodeBuild(url_tag) { info[1] = info[1].slice(7); } - for (let i in powderInputs) { - setValue(powderInputs[i], powdering[i]); + for (let i in powder_inputs) { + setValue(powder_inputs[i], powdering[i]); } } } diff --git a/js/builder.js b/js/builder.js index 249bf32..fcdf850 100644 --- a/js/builder.js +++ b/js/builder.js @@ -12,29 +12,6 @@ function getTomeNameFromID(id) { return tomeIDMap.get(id); } -function parsePowdering(powder_info) { - // TODO: Make this run in linear instead of quadratic time... ew - let powdering = []; - for (let i = 0; i < 5; ++i) { - let powders = ""; - let n_blocks = Base64.toInt(powder_info.charAt(0)); - // console.log(n_blocks + " blocks"); - powder_info = powder_info.slice(1); - for (let j = 0; j < n_blocks; ++j) { - let block = powder_info.slice(0,5); - console.log(block); - let six_powders = Base64.toInt(block); - for (let k = 0; k < 6 && six_powders != 0; ++k) { - powders += powderNames.get((six_powders & 0x1f) - 1); - six_powders >>>= 5; - } - powder_info = powder_info.slice(5); - } - powdering[i] = powders; - } - return [powdering, powder_info]; -} - function populateBuildList() { const buildList = document.getElementById("build-choice"); const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds")); @@ -139,6 +116,21 @@ function toggle_tab(tab) { } } + +let tabs = ['overall-stats', 'offensive-stats', 'defensive-stats']; +function show_tab(tab) { + //console.log(itemFilters) + + //hide all tabs, then show the tab of the div clicked and highlight the correct button + for (const i in tabs) { + document.querySelector("#" + tabs[i]).style.display = "none"; + document.getElementById("tab-" + tabs[i].split("-")[0] + "-btn").classList.remove("selected-btn"); + } + document.querySelector("#" + tab).style.display = ""; + document.getElementById("tab-" + tab.split("-")[0] + "-btn").classList.add("selected-btn"); +} + + // TODO: Learn and use await function init() { console.log("builder.js init"); diff --git a/js/builder_graph.js b/js/builder_graph.js index 111cd92..c1d80d4 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -42,14 +42,71 @@ class BuildAssembleNode extends ComputeNode { ]; let weapon = input_map.get('weapon-input'); let level = input_map.get('level-input'); - console.log('build node run'); + + let all_none = weapon.statMap.has('NONE'); + for (const item of equipments) { + all_none = all_none && item.statMap.has('NONE'); + } + if (all_none) { + return null; + } return new Build(level, equipments, [], weapon); } } +class PowderInputNode extends InputNode { + + constructor(name, input_field) { + super(name, input_field); + } + + compute_func(input_map) { + // TODO: haha improve efficiency to O(n) dumb + // also, error handling is missing + let input = this.input_field.value.trim(); + let powdering = []; + let errorederrors = []; + while (input) { + let first = input.slice(0, 2); + let powder = powderIDs.get(first); + if (powder === undefined) { + return null; + } else { + powdering.push(powder); + } + input = input.slice(2); + } + //console.log("POWDERING: " + powdering); + return powdering; + } +} + +class SpellDamageCalcNode extends ComputeNode { + constructor(spell_num) { + super("builder-spell"+spell_num+"-calc"); + this.spell_idx = spell_num; + } + + compute_func(input_map) { + // inputs: + let weapon = new Map(input_map.get('weapon-input').statMap); + let build = input_map.get('build'); + let weapon_powder = input_map.get('weapon-powder'); + weapon.set("powders", weapon_powder); + const i = this.spell_idx; + let spell = spell_table[weapon.get("type")][i]; + let parent_elem = document.getElementById("spell"+i+"-info"); + let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); + displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, i+1, weapon); + } +} + let item_nodes = []; +let powder_nodes = []; +let spell_nodes = []; document.addEventListener('DOMContentLoaded', function() { + // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). for (const [eq, none_item] of zip(equipment_fields, none_items)) { let input_field = document.getElementById(eq+"-choice"); let item_image = document.getElementById(eq+"-img"); @@ -57,28 +114,70 @@ document.addEventListener('DOMContentLoaded', function() { let item_input = new ItemInputNode(eq+'-input', input_field, none_item); item_nodes.push(item_input); new ItemInputDisplayNode(eq+'-display', input_field, item_image).link_to(item_input); - new PrintNode(eq+'-debug').link_to(item_input); + //new PrintNode(eq+'-debug').link_to(item_input); //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); - } + + // weapon image changer node. let weapon_image = document.getElementById("weapon-img"); new WeaponDisplayNode('weapon-type', weapon_image).link_to(item_nodes[8]); - let level_input = new InputNode('level-input', document.getElementById('level-choice')); - new PrintNode('lvl-debug').link_to(level_input); + // Level input node. + let level_input = new InputNode('level-input', document.getElementById('level-choice')); + + // "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display. let build_node = new BuildAssembleNode(); for (const input of item_nodes) { build_node.link_to(input); } build_node.link_to(level_input); + + + for (const input of powder_inputs) { + powder_nodes.push(new PowderInputNode(input, document.getElementById(input))); + } + + for (let i = 0; i < 4; ++i) { + let spell_node = new SpellDamageCalcNode(i); + spell_node.link_to(item_nodes[8], 'weapon-input'); + spell_node.link_to(build_node, 'build'); + spell_node.link_to(powder_nodes[4], 'weapon-powder'); + spell_nodes.push(spell_node); + } + console.log("Set up graph"); + let masonry = Macy({ + container: "#masonry-container", + columns: 1, + mobileFirst: true, + breakAt: { + 1200: 4, + }, + margin: { + x: 20, + y: 20, + } + + }); + + let search_masonry = Macy({ + container: "#search-results", + columns: 1, + mobileFirst: true, + breakAt: { + 1200: 4, + }, + margin: { + x: 20, + y: 20, + } + + }); }); // autocomplete initialize function init_autocomplete() { - console.log("autocomplete init"); - console.log(itemLists) let dropdowns = new Map(); for (const eq of equipment_keys) { if (tome_keys.includes(eq)) { diff --git a/js/computation_graph.js b/js/computation_graph.js index 155d95a..45ba267 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -7,13 +7,14 @@ class ComputeNode { */ constructor(name) { this.inputs = []; // parent nodes + this.input_translation = new Map(); this.children = []; - this.value = 0; + this.value = null; this.name = name; this.update_task = null; this.update_time = Date.now(); this.fail_cb = false; // Set to true to force updates even if parent failed. - this.dirty = false; + this.dirty = true; this.inputs_dirty = new Map(); this.inputs_dirty_count = 0; } @@ -32,7 +33,7 @@ class ComputeNode { } let calc_inputs = new Map(); for (const input of this.inputs) { - calc_inputs.set(input.name, input.value); + calc_inputs.set(this.input_translation.get(input.name), input.value); } this.value = this.compute_func(calc_inputs); this.dirty = false; @@ -41,18 +42,6 @@ class ComputeNode { } } - /** - * Set this node's value directly. Notifies children. - */ - set_value(value) { - let timestamp = Date.now(); - this.update_time = timestamp; - this.value = value; - for (const child of this.children) { - child.set_input(this.name, this.value, timestamp); - } - } - /** * Mark parent as not dirty. Propagates calculation if all inputs are present. */ @@ -99,8 +88,10 @@ class ComputeNode { throw "no compute func specified"; } - link_to(parent_node) { + link_to(parent_node, link_name) { this.inputs.push(parent_node) + link_name = (link_name !== undefined) ? link_name : parent_node.name; + this.input_translation.set(parent_node.name, link_name); this.inputs_dirty.set(parent_node.name, parent_node.dirty); if (parent_node.dirty) { this.inputs_dirty_count += 1; @@ -147,6 +138,7 @@ class InputNode extends ComputeNode { super(name); this.input_field = input_field; this.input_field.addEventListener("input", () => calcSchedule(this)); + calcSchedule(this); } compute_func(input_map) { @@ -168,6 +160,7 @@ class ItemInputNode extends InputNode { constructor(name, item_input_field, none_item) { super(name, item_input_field); this.none_item = new Item(none_item); + this.none_item.statMap.set('NONE', true); } compute_func(input_map) { @@ -231,9 +224,13 @@ class ItemInputDisplayNode extends ComputeNode { return null; } + if (item.statMap.has('NONE')) { + return null; + } const tier = item.statMap.get('tier'); this.input_field.classList.add(tier); this.image.classList.add(tier + "-shadow"); + return null; } } diff --git a/js/damage_calc.js b/js/damage_calc.js index 564ab2e..6aa7c64 100644 --- a/js/damage_calc.js +++ b/js/damage_calc.js @@ -1,29 +1,12 @@ const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.10], ["bash", 0.50] ]); // Calculate spell damage given a spell elemental conversion table, and a spell multiplier. // If spell mult is 0, its melee damage and we don't multiply by attack speed. -// externalStats should be a map -function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, spellMultiplier, weapon, total_skillpoints, damageMultiplier, externalStats) { +function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, spellMultiplier, weapon, total_skillpoints, damageMultiplier) { let buildStats = new Map(stats); let tooltipinfo = new Map(); //6x for damages, normal min normal max crit min crit max let damageformulas = [["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "]]; - if(externalStats) { //if nothing is passed in, then this hopefully won't trigger - for (const entry of externalStats) { - const key = entry[0]; - const value = entry[1]; - if (typeof value === "number") { - buildStats.set(key, buildStats.get(key) + value); - } else if (Array.isArray(value)) { - arr = []; - for (let j = 0; j < value.length; j++) { - arr[j] = buildStats.get(key)[j] + value[j]; - } - buildStats.set(key, arr); - } - } - } - let powders = weapon.get("powders").slice(); // Array of neutral + ewtfa damages. Each entry is a pair (min, max). diff --git a/js/load.js b/js/load.js index 4f6e4b2..521765e 100644 --- a/js/load.js +++ b/js/load.js @@ -6,7 +6,7 @@ let reload = false; let load_complete = false; let load_in_progress = false; let items; -let sets; +let sets = new Map(); let itemMap; let idMap; let redirectMap; @@ -20,7 +20,6 @@ async function load_local() { let sets_store = get_tx.objectStore('set_db'); let get_store = get_tx.objectStore('item_db'); let request = get_store.getAll(); - let request2 = sets_store.getAll(); request.onerror = function(event) { reject("Could not read local item db..."); } @@ -28,15 +27,27 @@ async function load_local() { console.log("Successfully read local item db."); } + // key-value iteration (hpp don't break this again) + // https://stackoverflow.com/questions/47931595/indexeddb-getting-all-data-with-keys + let request2 = sets_store.openCursor(); request2.onerror = function(event) { reject("Could not read local set db..."); } request2.onsuccess = function(event) { - console.log("Successfully read local set db."); - } + let cursor = event.target.result; + if (cursor) { + let key = cursor.primaryKey; + let value = cursor.value; + sets.set(key, value); + cursor.continue(); + } + else { + // no more results + console.log("Successfully read local set db."); + } + }; get_tx.oncomplete = function(event) { items = request.result; - sets = request2.result; init_maps(); load_complete = true; db.close(); diff --git a/js/skillpoints.js b/js/skillpoints.js index 12c8805..fcf24b6 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -31,14 +31,15 @@ function calculate_skillpoints(equipment, weapon) { let setCount = activeSetCounts.get(setName); let old_bonus = {}; if (setCount) { - old_bonus = sets[setName].bonuses[setCount-1]; + old_bonus = sets.get(setName).bonuses[setCount-1]; activeSetCounts.set(setName, setCount + 1); } else { setCount = 0; activeSetCounts.set(setName, 1); } - const new_bonus = sets[setName].bonuses[setCount]; + console.log(sets); + const new_bonus = sets.get(setName).bonuses[setCount]; //let skp_order = ["str","dex","int","def","agi"]; for (const i in skp_order) { const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0); @@ -74,8 +75,8 @@ function calculate_skillpoints(equipment, weapon) { if (setName) { // undefined/null means no set. const setCount = activeSetCounts.get(setName); if (setCount) { - const old_bonus = sets[setName].bonuses[setCount-1]; - const new_bonus = sets[setName].bonuses[setCount]; + const old_bonus = sets.get(setName).bonuses[setCount-1]; + const new_bonus = sets.get(setName).bonuses[setCount]; //let skp_order = ["str","dex","int","def","agi"]; for (const i in skp_order) { const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0); diff --git a/js/sq2display.js b/js/sq2display.js index b6b588b..195aea5 100644 --- a/js/sq2display.js +++ b/js/sq2display.js @@ -41,7 +41,7 @@ function displaysq2BuildStats(parent_id,build,command_group){ // id instruction else { let id = command; - if (stats.get(id) || build.externalStats.get(id)) { + if (stats.get(id)) { let style = null; // TODO: add pos and neg style @@ -54,10 +54,6 @@ function displaysq2BuildStats(parent_id,build,command_group){ // ignore let id_val = stats.get(id); - if (build.externalStats.has(id)) { - id_val += build.externalStats.get(id); - } - if (reversedIDs.includes(id)) { style === "positive" ? style = "negative" : style = "positive"; } @@ -650,7 +646,7 @@ function displaysq2PoisonDamage(overallparent_elem, build) { let overallpoisonDamage = document.createElement("p"); let overallpoisonDamageFirst = document.createElement("span"); let overallpoisonDamageSecond = document.createElement("span"); - let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct") + build.externalStats.get("poisonPct"))/100 /3); + let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct"))/100 /3); overallpoisonDamageFirst.textContent = "Poison Tick: "; overallpoisonDamageSecond.textContent = Math.max(poison_tick,0); overallpoisonDamageSecond.classList.add("Damage"); @@ -1126,8 +1122,8 @@ function displaysq2PowderSpecials(parent_elem, powderSpecials, build, overall=fa let spell = (powderSpecialStats.indexOf(special[0]) == 3 ? spells[2] : spells[powderSpecialStats.indexOf(special[0])]); let part = spell["parts"][0]; let _results = calculateSpellDamage(stats, part.conversion, - stats.get("mdRaw"), stats.get("mdPct") + build.externalStats.get("mdPct"), - 0, build.weapon, build.total_skillpoints, build.damageMultiplier * ((part.multiplier[power-1] / 100)), build.externalStats);//part.multiplier[power] / 100 + stats.get("mdRaw"), stats.get("mdPct"), + 0, build.weapon, build.total_skillpoints, build.damageMultiplier * ((part.multiplier[power-1] / 100)));//part.multiplier[power] / 100 let critChance = skillPointsToPercentage(build.total_skillpoints[1]); let save_damages = []; @@ -1215,7 +1211,7 @@ function displaysq2PowderSpecials(parent_elem, powderSpecials, build, overall=fa } } -function displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx) { +function displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx, weapon) { parent_elem.textContent = ""; @@ -1264,7 +1260,7 @@ function displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, sp parent_elem.append(title_elem); overallparent_elem.append(title_elemavg); - overallparent_elem.append(displaysq2NextCosts(spell, build)); + overallparent_elem.append(displaysq2NextCosts(spell, build, weapon)); let critChance = skillPointsToPercentage(build.total_skillpoints[1]); @@ -1300,8 +1296,8 @@ function displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, sp if (part.type === "damage") { //console.log(build.expandedStats); let _results = calculateSpellDamage(stats, part.conversion, - stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct") + build.externalStats.get("sdPct"), - part.multiplier / 100, build.weapon, build.total_skillpoints, build.damageMultiplier, build.externalStats); + stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct"), + part.multiplier / 100, weapon, build.total_skillpoints, build.damageMultiplier); let totalDamNormal = _results[0]; let totalDamCrit = _results[1]; let results = _results[2]; @@ -1427,9 +1423,9 @@ function displaysq2EquipOrder(parent_elem, buildOrder){ } } -function displaysq2NextCosts(spell, build) { +function displaysq2NextCosts(spell, build, weapon) { let int = build.total_skillpoints[2]; - let spells = spell_table[build.weapon.get("type")]; + let spells = spell_table[weapon.get("type")]; let row = document.createElement("div"); row.classList.add("spellcost-tooltip"); From e5b653619f29f818ed2be314cbb11f7767c2227b Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 11:02:28 -0700 Subject: [PATCH 11/30] Working spell damage calculation pipeline broke like everything else --- builder/index.html | 1 - js/build_utils.js | 65 +++++++++++ js/builder_graph.js | 98 ++++++++++++++-- js/damage_calc.js | 29 +---- js/display.js | 278 +++++++++++--------------------------------- 5 files changed, 219 insertions(+), 252 deletions(-) diff --git a/builder/index.html b/builder/index.html index d9c3e8f..eaa3bc2 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1390,7 +1390,6 @@ - diff --git a/js/build_utils.js b/js/build_utils.js index 9381035..4da3309 100644 --- a/js/build_utils.js +++ b/js/build_utils.js @@ -196,3 +196,68 @@ class Item { this.statMap = expandItem(item_obj); } } + +/* Takes in an ingredient object and returns an equivalent Map(). +*/ +function expandIngredient(ing) { + let expandedIng = new Map(); + let mapIds = ['consumableIDs', 'itemIDs', 'posMods']; + for (const id of mapIds) { + let idMap = new Map(); + for (const key of Object.keys(ing[id])) { + idMap.set(key, ing[id][key]); + } + expandedIng.set(id, idMap); + } + let normIds = ['lvl','name', 'displayName','tier','skills','id']; + for (const id of normIds) { + expandedIng.set(id, ing[id]); + } + if (ing['isPowder']) { + expandedIng.set("isPowder",ing['isPowder']); + expandedIng.set("pid",ing['pid']); + } + //now the actually hard one + let idMap = new Map(); + idMap.set("minRolls", new Map()); + idMap.set("maxRolls", new Map()); + for (const field of ingFields) { + let val = (ing['ids'][field] || 0); + idMap.get("minRolls").set(field, val['minimum']); + idMap.get("maxRolls").set(field, val['maximum']); + } + expandedIng.set("ids",idMap); + return expandedIng; +} + +/* Takes in a recipe object and returns an equivalent Map(). +*/ +function expandRecipe(recipe) { + let expandedRecipe = new Map(); + let normIDs = ["name", "skill", "type","id"]; + for (const id of normIDs) { + expandedRecipe.set(id,recipe[id]); + } + let rangeIDs = ["durability","lvl", "healthOrDamage", "duration", "basicDuration"]; + for (const id of rangeIDs) { + if(recipe[id]){ + expandedRecipe.set(id, [recipe[id]['minimum'], recipe[id]['maximum']]); + } else { + expandedRecipe.set(id, [0,0]); + } + } + expandedRecipe.set("materials", [ new Map([ ["item", recipe['materials'][0]['item']], ["amount", recipe['materials'][0]['amount']] ]) , new Map([ ["item", recipe['materials'][1]['item']], ["amount",recipe['materials'][1]['amount'] ] ]) ]); + return expandedRecipe; +} + +/*An independent helper function that rounds a rolled ID to the nearest integer OR brings the roll away from 0. +* @param id +*/ +function idRound(id){ + rounded = Math.round(id); + if(rounded == 0){ + return 1; //this is a hack, will need changing along w/ rest of ID system if anything changes + }else{ + return rounded; + } +} diff --git a/js/builder_graph.js b/js/builder_graph.js index c1d80d4..d4a8eb5 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -81,6 +81,36 @@ class PowderInputNode extends InputNode { } } +class SpellSelectNode extends ComputeNode { + constructor(spell_num) { + super("builder-spell"+spell_num+"-select"); + this.spell_idx = spell_num; + } + + compute_func(input_map) { + const build = input_map.get('build'); + + const i = this.spell_idx; + let spell = spell_table[build.weapon.statMap.get("type")][i]; + let stats = build.statMap; + + let spell_parts; + if (spell.parts) { + spell_parts = spell.parts; + } + else { + spell_parts = spell.variants.DEFAULT; + for (const majorID of stats.get("activeMajorIDs")) { + if (majorID in spell.variants) { + spell_parts = spell.variants[majorID]; + break; + } + } + } + return [spell, spell_parts]; + } +} + class SpellDamageCalcNode extends ComputeNode { constructor(spell_num) { super("builder-spell"+spell_num+"-calc"); @@ -88,22 +118,59 @@ class SpellDamageCalcNode extends ComputeNode { } compute_func(input_map) { - // inputs: - let weapon = new Map(input_map.get('weapon-input').statMap); - let build = input_map.get('build'); - let weapon_powder = input_map.get('weapon-powder'); + const weapon = new Map(input_map.get('weapon-input').statMap); + const build = input_map.get('build'); + const weapon_powder = input_map.get('weapon-powder'); + const damage_mult = 1; // TODO: hook up + const spell_info = input_map.get('spell-info'); + const spell_parts = spell_info[1]; + weapon.set("powders", weapon_powder); + let spell_results = [] + let stats = build.statMap; + + for (const part of spell_parts) { + if (part.type === "damage") { + let results = calculateSpellDamage(stats, part.conversion, + stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct"), + part.multiplier / 100, weapon, build.total_skillpoints, damage_mult); + spell_results.push(results); + } else if (part.type === "heal") { + // TODO: wynn2 formula + let heal_amount = (part.strength * build.getDefenseStats()[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2); + spell_results.push(heal_amount); + } else if (part.type === "total") { + // TODO: remove "total" type + spell_results.push(null); + } + } + return spell_results; + } +} + +class SpellDisplayNode extends ComputeNode { + constructor(spell_num) { + super("builder-spell"+spell_num+"-display"); + this.spell_idx = spell_num; + } + + compute_func(input_map) { + const build = input_map.get('build'); + const spell_info = input_map.get('spell-info'); + const damages = input_map.get('spell-damage'); + const spell = spell_info[0]; + const spell_parts = spell_info[1]; + const i = this.spell_idx; - let spell = spell_table[weapon.get("type")][i]; let parent_elem = document.getElementById("spell"+i+"-info"); let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); - displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, i+1, weapon); + displaySpellDamage(parent_elem, overallparent_elem, build, spell, i+1, spell_parts, damages); } } let item_nodes = []; let powder_nodes = []; -let spell_nodes = []; +let spelldmg_nodes = []; document.addEventListener('DOMContentLoaded', function() { // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). @@ -138,11 +205,20 @@ document.addEventListener('DOMContentLoaded', function() { } for (let i = 0; i < 4; ++i) { - let spell_node = new SpellDamageCalcNode(i); - spell_node.link_to(item_nodes[8], 'weapon-input'); + let spell_node = new SpellSelectNode(i); spell_node.link_to(build_node, 'build'); - spell_node.link_to(powder_nodes[4], 'weapon-powder'); - spell_nodes.push(spell_node); + + let calc_node = new SpellDamageCalcNode(i); + calc_node.link_to(item_nodes[8], 'weapon-input'); + calc_node.link_to(build_node, 'build'); + calc_node.link_to(powder_nodes[4], 'weapon-powder'); + calc_node.link_to(spell_node, 'spell-info'); + spelldmg_nodes.push(calc_node); + + let display_node = new SpellDisplayNode(i); + display_node.link_to(build_node, 'build'); + display_node.link_to(spell_node, 'spell-info'); + display_node.link_to(calc_node, 'spell-damage'); } console.log("Set up graph"); diff --git a/js/damage_calc.js b/js/damage_calc.js index 6aa7c64..9149a13 100644 --- a/js/damage_calc.js +++ b/js/damage_calc.js @@ -3,9 +3,7 @@ const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["va // If spell mult is 0, its melee damage and we don't multiply by attack speed. function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, spellMultiplier, weapon, total_skillpoints, damageMultiplier) { let buildStats = new Map(stats); - let tooltipinfo = new Map(); //6x for damages, normal min normal max crit min crit max - let damageformulas = [["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "]]; let powders = weapon.get("powders").slice(); @@ -75,28 +73,22 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, } - - //console.log(tooltipinfo); damages[0] = neutralRemainingRaw; - tooltipinfo.set("damageBases", damages); let damageMult = damageMultiplier; let melee = false; // If we are doing melee calculations: - tooltipinfo.set("dmgMult", damageMult); if (spellMultiplier == 0) { spellMultiplier = 1; melee = true; } else { - tooltipinfo.set("dmgMult", `(${tooltipinfo.get("dmgMult")} * ${spellMultiplier} * ${baseDamageMultiplier[attackSpeeds.indexOf(buildStats.get("atkSpd"))]})`) damageMult *= spellMultiplier * baseDamageMultiplier[attackSpeeds.indexOf(buildStats.get("atkSpd"))]; } //console.log(damages); //console.log(damageMult); - tooltipinfo.set("rawModifier", `(${rawModifier} * ${spellMultiplier} * ${damageMultiplier})`); rawModifier *= spellMultiplier * damageMultiplier; let totalDamNorm = [0, 0]; let totalDamCrit = [0, 0]; @@ -109,33 +101,21 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, let baseDamCrit = rawModifier * (1 + strBoost); totalDamNorm = [baseDam, baseDam]; totalDamCrit = [baseDamCrit, baseDamCrit]; - for (let arr of damageformulas) { - arr = arr.map(x => x + " + " +tooltipinfo.get("rawModifier")); - } } let staticBoost = (pctModifier / 100.); - tooltipinfo.set("staticBoost", `${(pctModifier/ 100.).toFixed(2)}`); - tooltipinfo.set("skillBoost",["","","","","",""]); let skillBoost = [0]; for (let i in total_skillpoints) { skillBoost.push(skillPointsToPercentage(total_skillpoints[i]) + buildStats.get("damageBonus")[i] / 100.); - tooltipinfo.get("skillBoost")[parseInt(i,10)+1] = `(${skillPointsToPercentage(total_skillpoints[i]).toFixed(2)} + ${(buildStats.get("damageBonus")[i]/100.).toFixed(2)})` } - tooltipinfo.get("skillBoost")[0] = undefined; for (let i in damages) { let damageBoost = 1 + skillBoost[i] + staticBoost; - tooltipinfo.set("damageBoost", `(1 + ${(tooltipinfo.get("skillBoost")[i] ? tooltipinfo.get("skillBoost")[i] + " + " : "")} ${tooltipinfo.get("staticBoost")})`) damages_results.push([ Math.max(damages[i][0] * strBoost * Math.max(damageBoost,0) * damageMult, 0), // Normal min Math.max(damages[i][1] * strBoost * Math.max(damageBoost,0) * damageMult, 0), // Normal max Math.max(damages[i][0] * (strBoost + 1) * Math.max(damageBoost,0) * damageMult, 0), // Crit min Math.max(damages[i][1] * (strBoost + 1) * Math.max(damageBoost,0) * damageMult, 0), // Crit max ]); - damageformulas[i][0] += `(max((${tooltipinfo.get("damageBases")[i][0]} * ${strBoost} * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))` - damageformulas[i][1] += `(max((${tooltipinfo.get("damageBases")[i][1]} * ${strBoost} * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))` - damageformulas[i][2] += `(max((${tooltipinfo.get("damageBases")[i][0]} * ${strBoost} * 2 * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))` - damageformulas[i][3] += `(max((${tooltipinfo.get("damageBases")[i][1]} * ${strBoost} * 2 * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))` totalDamNorm[0] += damages_results[i][0]; totalDamNorm[1] += damages_results[i][1]; totalDamCrit[0] += damages_results[i][2]; @@ -151,20 +131,13 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, damages_results[0][1] += strBoost*rawModifier; damages_results[0][2] += (strBoost + 1)*rawModifier; damages_results[0][3] += (strBoost + 1)*rawModifier; - for (let i = 0; i < 2; i++) { - damageformulas[0][i] += ` + (${strBoost} * ${tooltipinfo.get("rawModifier")})` - } - for (let i = 2; i < 4; i++) { - damageformulas[0][i] += ` + (2 * ${strBoost} * ${tooltipinfo.get("rawModifier")})` - } if (totalDamNorm[0] < 0) totalDamNorm[0] = 0; if (totalDamNorm[1] < 0) totalDamNorm[1] = 0; if (totalDamCrit[0] < 0) totalDamCrit[0] = 0; if (totalDamCrit[1] < 0) totalDamCrit[1] = 0; - tooltipinfo.set("damageformulas", damageformulas); - return [totalDamNorm, totalDamCrit, damages_results, tooltipinfo]; + return [totalDamNorm, totalDamCrit, damages_results]; } diff --git a/js/display.js b/js/display.js index 707efe3..c2ca48b 100644 --- a/js/display.js +++ b/js/display.js @@ -27,72 +27,6 @@ function applyArmorPowdersOnce(expandedItem, powders) { } } - -/* Takes in an ingredient object and returns an equivalent Map(). -*/ -function expandIngredient(ing) { - let expandedIng = new Map(); - let mapIds = ['consumableIDs', 'itemIDs', 'posMods']; - for (const id of mapIds) { - let idMap = new Map(); - for (const key of Object.keys(ing[id])) { - idMap.set(key, ing[id][key]); - } - expandedIng.set(id, idMap); - } - let normIds = ['lvl','name', 'displayName','tier','skills','id']; - for (const id of normIds) { - expandedIng.set(id, ing[id]); - } - if (ing['isPowder']) { - expandedIng.set("isPowder",ing['isPowder']); - expandedIng.set("pid",ing['pid']); - } - //now the actually hard one - let idMap = new Map(); - idMap.set("minRolls", new Map()); - idMap.set("maxRolls", new Map()); - for (const field of ingFields) { - let val = (ing['ids'][field] || 0); - idMap.get("minRolls").set(field, val['minimum']); - idMap.get("maxRolls").set(field, val['maximum']); - } - expandedIng.set("ids",idMap); - return expandedIng; -} - -/* Takes in a recipe object and returns an equivalent Map(). -*/ -function expandRecipe(recipe) { - let expandedRecipe = new Map(); - let normIDs = ["name", "skill", "type","id"]; - for (const id of normIDs) { - expandedRecipe.set(id,recipe[id]); - } - let rangeIDs = ["durability","lvl", "healthOrDamage", "duration", "basicDuration"]; - for (const id of rangeIDs) { - if(recipe[id]){ - expandedRecipe.set(id, [recipe[id]['minimum'], recipe[id]['maximum']]); - } else { - expandedRecipe.set(id, [0,0]); - } - } - expandedRecipe.set("materials", [ new Map([ ["item", recipe['materials'][0]['item']], ["amount", recipe['materials'][0]['amount']] ]) , new Map([ ["item", recipe['materials'][1]['item']], ["amount",recipe['materials'][1]['amount'] ] ]) ]); - return expandedRecipe; -} - -/*An independent helper function that rounds a rolled ID to the nearest integer OR brings the roll away from 0. -* @param id -*/ -function idRound(id){ - rounded = Math.round(id); - if(rounded == 0){ - return 1; //this is a hack, will need changing along w/ rest of ID system if anything changes - }else{ - return rounded; - } -} - function apply_elemental_format(p_elem, id, suffix) { suffix = (typeof suffix !== 'undefined') ? suffix : ""; // THIS IS SO JANK BUT IM TOO LAZY TO FIX IT TODO @@ -998,78 +932,54 @@ function displayExpandedIngredient(ingred, parent_id) { } } -function displayNextCosts(parent_id, build) { - let p_elem = document.getElementById(parent_id); +function displayNextCosts(spell, build, weapon) { let int = build.total_skillpoints[2]; - let spells = spell_table[build.weapon.get("type")]; + let spells = spell_table[weapon.get("type")]; - p_elem.textContent = ""; - - let title = document.createElement("p"); - title.classList.add("title"); - title.classList.add("Normal"); - title.textContent = "Next Spell Costs"; - - let int_title = document.createElement("p"); - int_title.classList.add("itemp"); - int_title.textContent = int + " Intelligence points."; - - p_elem.append(title); - p_elem.append(int_title); - - for (const spell of spells) { - let spellp = document.createElement("p"); - let spelltitle = document.createElement("p"); - spelltitle.classList.add("itemp"); - spelltitle.textContent = spell.title; - spellp.appendChild(spelltitle); - let row = document.createElement("p"); - row.classList.add("itemp"); - let init_cost = document.createElement("b"); - init_cost.textContent = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost); - init_cost.classList.add("Mana"); - let arrow = document.createElement("b"); - arrow.textContent = "\u279C"; - let next_cost = document.createElement("b"); - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1); - next_cost.classList.add("Mana"); - let int_needed = document.createElement("b"); - if (init_cost.textContent === "1") { - int_needed.textContent = ": n/a (+0)"; - }else { //do math - let target = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1; - let needed = int; - let noUpdate = false; - //forgive me... I couldn't inverse ceil, floor, and max. - while (build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) > target) { - if(needed > 150) { - noUpdate = true; - break; - } - needed++; - build.total_skillpoints[2] = needed; + let row = document.createElement("div"); + row.classList.add("spellcost-tooltip"); + let init_cost = document.createElement("b"); + init_cost.textContent = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost); + init_cost.classList.add("Mana"); + let arrow = document.createElement("b"); + arrow.textContent = "\u279C"; + let next_cost = document.createElement("b"); + next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1); + next_cost.classList.add("Mana"); + let int_needed = document.createElement("b"); + if (init_cost.textContent === "1") { + int_needed.textContent = ": n/a (+0)"; + }else { //do math + let target = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1; + let needed = int; + let noUpdate = false; + //forgive me... I couldn't inverse ceil, floor, and max. + while (build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) > target) { + if(needed > 150) { + noUpdate = true; + break; } - let missing = needed - int; - //in rare circumstances, the next spell cost can jump. - if (noUpdate) { - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)-1); - }else { - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)); - } - - - build.total_skillpoints[2] = int;//forgive me pt 2 - int_needed.textContent = ": " + (needed > 150 ? ">150" : needed) + " int (+" + (needed > 150 ? "n/a" : missing) + ")"; + needed++; + build.total_skillpoints[2] = needed; + } + let missing = needed - int; + //in rare circumstances, the next spell cost can jump. + if (noUpdate) { + next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)-1); + }else { + next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)); } - row.appendChild(init_cost); - row.appendChild(arrow); - row.appendChild(next_cost); - row.appendChild(int_needed); - spellp.appendChild(row); - - p_elem.append(spellp); + + build.total_skillpoints[2] = int;//forgive me pt 2 + int_needed.textContent = ": " + (needed > 150 ? ">150" : needed) + " int (+" + (needed > 150 ? "n/a" : missing) + ")"; } + + // row.appendChild(init_cost); + row.appendChild(arrow); + row.appendChild(next_cost); + row.appendChild(int_needed); + return row; } function displayRolledID(item, id, elemental_format) { @@ -1733,31 +1643,26 @@ function displayPowderSpecials(parent_elem, powderSpecials, build) { } } -function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx) { +function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx, spell_parts, damages) { + // TODO: remove spellIdx (just used to flag melee and cost) + // TODO: move cost calc out parent_elem.textContent = ""; - - let tooltip; let tooltiptext; const stats = build.statMap; let title_elem = document.createElement("p"); - title_elem.classList.add("smalltitle"); - title_elem.classList.add("Normal"); overallparent_elem.textContent = ""; - let title_elemavg = document.createElement("p"); - title_elemavg.classList.add('smalltitle'); - title_elemavg.classList.add('Normal'); + let title_elemavg = document.createElement("b"); if (spellIdx != 0) { - let first = document.createElement("b"); + let first = document.createElement("span"); first.textContent = spell.title + " ("; title_elem.appendChild(first.cloneNode(true)); //cloneNode is needed here. title_elemavg.appendChild(first); - let second = document.createElement("b"); + let second = document.createElement("span"); second.textContent = build.getSpellCost(spellIdx, spell.cost); second.classList.add("Mana"); - second.classList.add("tooltip"); let int_redux = skillPointsToPercentage(build.total_skillpoints[2]).toFixed(2); let spPct_redux = (build.statMap.get("spPct" + spellIdx)/100).toFixed(2); @@ -1765,17 +1670,14 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell spPct_redux >= 0 ? spPct_redux = "+ " + spPct_redux : spPct_redux = "- " + Math.abs(spPct_redux); spRaw_redux >= 0 ? spRaw_redux = "+ " + spRaw_redux : spRaw_redux = "- " + Math.abs(spRaw_redux); - tooltiptext = `= max(1, floor((ceil(${spell.cost} * (1 - ${int_redux})) ${spRaw_redux}) * (1 ${spPct_redux})))`; - tooltip = createTooltip(tooltip, "p", tooltiptext, second, ["spellcostcalc"]); - second.appendChild(tooltip); title_elem.appendChild(second.cloneNode(true)); title_elemavg.appendChild(second); - let third = document.createElement("b"); + let third = document.createElement("span"); third.textContent = ") [Base: " + build.getBaseSpellCost(spellIdx, spell.cost) + " ]"; title_elem.appendChild(third); - let third_summary = document.createElement("b"); + let third_summary = document.createElement("span"); third_summary.textContent = ")"; title_elemavg.appendChild(third_summary); } @@ -1787,48 +1689,33 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell parent_elem.append(title_elem); overallparent_elem.append(title_elemavg); + overallparent_elem.append(displayNextCosts(spell, build, build.weapon.statMap)); + + let critChance = skillPointsToPercentage(build.total_skillpoints[1]); let save_damages = []; let part_divavg = document.createElement("p"); - part_divavg.classList.add("lessbottom"); overallparent_elem.append(part_divavg); - let spell_parts; - if (spell.parts) { - spell_parts = spell.parts; - } - else { - spell_parts = spell.variants.DEFAULT; - for (const majorID of stats.get("activeMajorIDs")) { - if (majorID in spell.variants) { - spell_parts = spell.variants[majorID]; - break; - } - } - } - //console.log(spell_parts); + for (let i = 0; i < spell_parts.length; ++i) { + const part = spell_parts[i]; + const damage = damages[i]; - for (const part of spell_parts) { - parent_elem.append(document.createElement("br")); let part_div = document.createElement("p"); parent_elem.append(part_div); let subtitle_elem = document.createElement("p"); subtitle_elem.textContent = part.subtitle; - subtitle_elem.classList.add("nomargin"); part_div.append(subtitle_elem); if (part.type === "damage") { //console.log(build.expandedStats); - let _results = calculateSpellDamage(stats, part.conversion, - stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct") + build.externalStats.get("sdPct"), - part.multiplier / 100, build.weapon, build.total_skillpoints, build.damageMultiplier, build.externalStats); + let _results = damage; let totalDamNormal = _results[0]; let totalDamCrit = _results[1]; let results = _results[2]; - let tooltipinfo = _results[3]; for (let i = 0; i < 6; ++i) { for (let j in results[i]) { @@ -1841,30 +1728,25 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell let averageLabel = document.createElement("p"); averageLabel.textContent = "Average: "+averageDamage.toFixed(2); - tooltiptext = ` = ((1 - ${critChance}) * ${nonCritAverage.toFixed(2)}) + (${critChance} * ${critAverage.toFixed(2)})` - averageLabel.classList.add("damageSubtitle"); - tooltip = createTooltip(tooltip, "p", tooltiptext, averageLabel, ["spell-tooltip"]); + // averageLabel.classList.add("damageSubtitle"); part_div.append(averageLabel); if (part.summary == true) { let overallaverageLabel = document.createElement("p"); - let first = document.createElement("b"); - let second = document.createElement("b"); + let first = document.createElement("span"); + let second = document.createElement("span"); first.textContent = part.subtitle + " Average: "; second.textContent = averageDamage.toFixed(2); overallaverageLabel.appendChild(first); overallaverageLabel.appendChild(second); - tooltip = createTooltip(tooltip, "p", tooltiptext, overallaverageLabel, ["spell-tooltip", "summary-tooltip"]); second.classList.add("Damage"); - overallaverageLabel.classList.add("itemp"); part_divavg.append(overallaverageLabel); } function _damage_display(label_text, average, result_idx) { let label = document.createElement("p"); label.textContent = label_text+average.toFixed(2); - label.classList.add("damageSubtitle"); part_div.append(label); let arrmin = []; @@ -1872,84 +1754,56 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell for (let i = 0; i < 6; i++){ if (results[i][1] != 0){ let p = document.createElement("p"); - p.classList.add("damagep"); p.classList.add(damageClasses[i]); p.textContent = results[i][result_idx] + " \u2013 " + results[i][result_idx + 1]; - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(0,2).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, p, ["spell-tooltip"]); arrmin.push(results[i][result_idx]); arrmax.push(results[i][result_idx + 1]); part_div.append(p); } } - tooltiptext = ` = ((${arrmin.join(" + ")}) + (${arrmax.join(" + ")})) / 2`; - tooltip = createTooltip(tooltip, "p", tooltiptext, label, ["spell-tooltip"]); } _damage_display("Non-Crit Average: ", nonCritAverage, 0); _damage_display("Crit Average: ", critAverage, 2); save_damages.push(averageDamage); } else if (part.type === "heal") { - let heal_amount = (part.strength * build.getDefenseStats()[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2); - tooltiptext = ` = ${part.strength} * ${build.getDefenseStats()[0]} * max(0.5, min(1.75, 1 + 0.5 * ${stats.get("wDamPct")/100}))`; + let heal_amount = damage; let healLabel = document.createElement("p"); healLabel.textContent = heal_amount; - healLabel.classList.add("damagep"); - tooltip = createTooltip(tooltip, "p", tooltiptext, healLabel, ["spell-tooltip"]); + // healLabel.classList.add("damagep"); part_div.append(healLabel); if (part.summary == true) { let overallhealLabel = document.createElement("p"); - let first = document.createElement("b"); - let second = document.createElement("b"); + let first = document.createElement("span"); + let second = document.createElement("span"); first.textContent = part.subtitle + ": "; second.textContent = heal_amount; overallhealLabel.appendChild(first); second.classList.add("Set"); overallhealLabel.appendChild(second); - overallhealLabel.classList.add("itemp"); - tooltip = createTooltip(tooltip, "p", tooltiptext, second, ["spell-tooltip"]); part_divavg.append(overallhealLabel); - - let effectiveHealLabel = document.createElement("p"); - first = document.createElement("b"); - second = document.createElement("b"); - let defStats = build.getDefenseStats(); - tooltiptext = ` = ${heal_amount} * ${defStats[1][0].toFixed(2)} / ${defStats[0]}`; - first.textContent = "Effective Heal: "; - second.textContent = (defStats[1][0]*heal_amount/defStats[0]).toFixed(2); - effectiveHealLabel.appendChild(first); - second.classList.add("Set"); - effectiveHealLabel.appendChild(second); - effectiveHealLabel.classList.add("itemp"); - tooltip = createTooltip(tooltip, "p", tooltiptext, second, ["spell-tooltip"]); - part_divavg.append(effectiveHealLabel); } } else if (part.type === "total") { let total_damage = 0; - tooltiptext = ""; for (let i in part.factors) { total_damage += save_damages[i] * part.factors[i]; } let dmgarr = part.factors.slice(); dmgarr = dmgarr.map(x => "(" + x + " * " + save_damages[dmgarr.indexOf(x)].toFixed(2) + ")"); - tooltiptext = " = " + dmgarr.join(" + "); let averageLabel = document.createElement("p"); averageLabel.textContent = "Average: "+total_damage.toFixed(2); averageLabel.classList.add("damageSubtitle"); - tooltip = createTooltip(tooltip, "p", tooltiptext, averageLabel, ["spell-tooltip"]); part_div.append(averageLabel); let overallaverageLabel = document.createElement("p"); - overallaverageLabel.classList.add("damageSubtitle"); - let overallaverageLabelFirst = document.createElement("b"); - let overallaverageLabelSecond = document.createElement("b"); + let overallaverageLabelFirst = document.createElement("span"); + let overallaverageLabelSecond = document.createElement("span"); overallaverageLabelFirst.textContent = "Average: "; overallaverageLabelSecond.textContent = total_damage.toFixed(2); overallaverageLabelSecond.classList.add("Damage"); - tooltip = createTooltip(tooltip, "p", tooltiptext, overallaverageLabel, ["spell-tooltip", "summary-tooltip"]); overallaverageLabel.appendChild(overallaverageLabelFirst); From fd97eb45e05d09b6d34d2a5e509f7a4c674d6e6e Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 11:30:10 -0700 Subject: [PATCH 12/30] Copying code from sq2display.js to display.js --- js/display.js | 591 +++++++++++++++++++++++++------------------------- 1 file changed, 297 insertions(+), 294 deletions(-) diff --git a/js/display.js b/js/display.js index c2ca48b..02104a5 100644 --- a/js/display.js +++ b/js/display.js @@ -1,23 +1,10 @@ /** - * Apply armor powdering. - * Applies twice for crafted items because wynn. - * Also for jeweling for crafted items. - */ -function applyArmorPowders(expandedItem, powders) { - applyArmorPowdersOnce(expandedItem, powders); - // NOTE: armor powder only applies once! - //if (expandedItem.get("crafted")) { - // applyArmorPowdersOnce(expandedItem, powders); - //} -} - -/** - * Apply armor powders once only. + * Apply armor powders. * Encoding shortcut assumes that all powders give +def to one element * and -def to the element "behind" it in cycle ETWFA, which is true * as of now and unlikely to change in the near future. */ -function applyArmorPowdersOnce(expandedItem, powders) { +function applyArmorPowders(expandedItem, powders) { for(const id of powders){ let powder = powderStats[id]; let name = powderNames.get(id).charAt(0); @@ -33,12 +20,12 @@ function apply_elemental_format(p_elem, id, suffix) { let parts = idPrefixes[id].split(/ (.*)/); let element_prefix = parts[0]; let desc = parts[1]; - let i_elem = document.createElement('b'); + let i_elem = document.createElement('span'); i_elem.classList.add(element_prefix); i_elem.textContent = element_prefix; p_elem.appendChild(i_elem); - let i_elem2 = document.createElement('b'); + let i_elem2 = document.createElement('span'); i_elem2.textContent = " " + desc + suffix; p_elem.appendChild(i_elem2); } @@ -90,48 +77,20 @@ function displaySetBonuses(parent_id,build) { } -function displayBuildStats(parent_id,build){ +function displayBuildStats(parent_id,build,command_group){ // Commands to "script" the creation of nice formatting. // #commands create a new element. // !elemental is some janky hack for elemental damage. // normals just display a thing. - let display_commands = build_overall_display_commands; - - // Clear the parent div. - setHTML(parent_id, ""); + let display_commands = command_group; let parent_div = document.getElementById(parent_id); - let title = document.createElement("p"); - title.classList.add("itemcenter"); - title.classList.add("itemp"); - title.classList.add("title"); - title.classList.add("Normal"); - title.textContent = "Overall Build Stats"; - parent_div.append(title); - parent_div.append(document.createElement("br")); - - if (build.activeSetCounts.size > 0) { - let set_summary_elem = document.createElement('p'); - set_summary_elem.classList.add('itemp'); - set_summary_elem.classList.add('left'); - set_summary_elem.textContent = "Set Summary:"; - parent_div.append(set_summary_elem); - for (const [setName, count] of build.activeSetCounts) { - const active_set = sets[setName]; - if (active_set["hidden"]) { continue; } - - let set_elem = document.createElement('p'); - set_elem.classList.add('itemp'); - set_elem.classList.add('left'); - set_elem.textContent = " "+setName+" Set: "+count+"/"+sets[setName].items.length; - set_summary_elem.append(set_elem); - } + // Clear the parent div. + if (parent_div != null) { + setHTML(parent_id, ""); } - displayDefenseStats(parent_div, build, true); - let stats = build.statMap; - //console.log(build.statMap); let active_elem; let elemental_format = false; @@ -140,37 +99,35 @@ function displayBuildStats(parent_id,build){ let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef"]; for (const command of display_commands) { + // style instructions + if (command.charAt(0) === "#") { - if (command === "#cdiv") { - active_elem = document.createElement('div'); - active_elem.classList.add('itemcenter'); + if (command === "#defense-stats") { + displayDefenseStats(parent_div, build, true); } - else if (command === "#ldiv") { - active_elem = document.createElement('div'); - active_elem.classList.add('itemleft'); - } - else if (command === "#table") { - active_elem = document.createElement('table'); - active_elem.classList.add('itemtable'); - } - parent_div.appendChild(active_elem); } - else if (command.charAt(0) === "!") { + if (command.charAt(0) === "!") { // TODO: This is sooo incredibly janky..... if (command === "!elemental") { elemental_format = !elemental_format; } } + + // id instruction else { let id = command; if (stats.get(id)) { let style = null; + + // TODO: add pos and neg style if (!staticIDs.includes(id)) { style = "positive"; if (stats.get(id) < 0) { style = "negative"; } } + + // ignore let id_val = stats.get(id); if (reversedIDs.includes(id)) { style === "positive" ? style = "negative" : style = "positive"; @@ -178,12 +135,14 @@ function displayBuildStats(parent_id,build){ if (id === "poison" && id_val > 0) { id_val = Math.ceil(id_val*build.statMap.get("poisonPct")/100); } - displayFixedID(active_elem, id, id_val, elemental_format, style); + displayFixedID(parent_div, id, id_val, elemental_format, style); if (id === "poison" && id_val > 0) { - let row = document.createElement('tr'); - let value_elem = document.createElement('td'); - value_elem.classList.add('right'); - value_elem.setAttribute("colspan", "2"); + let row = document.createElement('div'); + row.classList.add("row") + let value_elem = document.createElement('div'); + value_elem.classList.add('col'); + value_elem.classList.add('text-end'); + let prefix_elem = document.createElement('b'); prefix_elem.textContent = "\u279C With Strength: "; let number_elem = document.createElement('b'); @@ -192,23 +151,30 @@ function displayBuildStats(parent_id,build){ value_elem.append(prefix_elem); value_elem.append(number_elem); row.appendChild(value_elem); - - active_elem.appendChild(row); - } else if (id === "ls" && id_val != 0) { - let row = document.createElement("tr"); - let title = document.createElement("td"); - title.classList.add("left"); - title.textContent = "Effective Life Steal:" - let value = document.createElement("td"); - let defStats = build.getDefenseStats(); - value.textContent = Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s"; - value.classList.add("right"); - value.classList.add(style); - row.appendChild(title); - row.appendChild(value); - active_elem.appendChild(row); + parent_div.appendChild(row); } - } else if (skp_order.includes(id)) { + else if (id === "ls" && id_val != 0) { + let row = document.createElement('div'); + row.classList.add("row") + let value_elem = document.createElement('div'); + value_elem.classList.add('col'); + value_elem.classList.add('text-end'); + + let prefix_elem = document.createElement('b'); + prefix_elem.textContent = "\u279C Effective LS: "; + + let defStats = build.getDefenseStats(); + let number_elem = document.createElement('b'); + number_elem.classList.add(style); + number_elem.textContent = Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s"; + value_elem.append(prefix_elem); + value_elem.append(number_elem); + row.appendChild(value_elem); + parent_div.appendChild(row); + } + } + // sp thingy (WHY IS THIS HANDLED SEPARATELY TODO + else if (skp_order.includes(id)) { let total_assigned = build.total_skillpoints[skp_order.indexOf(id)]; let base_assigned = build.base_skillpoints[skp_order.indexOf(id)]; let diff = total_assigned - base_assigned; @@ -219,7 +185,7 @@ function displayBuildStats(parent_id,build){ style = "negative"; } if (diff != 0) { - displayFixedID(active_elem, id, diff, false, style); + displayFixedID(parent_div, id, diff, false, style); } } } @@ -276,38 +242,28 @@ function displayExpandedItem(item, parent_id){ } else if (item.get("category") === "armor") { } - let display_commands = item_display_commands; + let display_commands = sq2_item_display_commands; // Clear the parent div. setHTML(parent_id, ""); let parent_div = document.getElementById(parent_id); + parent_div.classList.add("border", "border-2", "border-dark"); - let active_elem; let fix_id = item.has("fixID") && item.get("fixID"); let elemental_format = false; for (let i = 0; i < display_commands.length; i++) { const command = display_commands[i]; - if (command.charAt(0) === "#") { - if (command === "#cdiv") { - active_elem = document.createElement('div'); - active_elem.classList.add('itemcenter'); - } - else if (command === "#ldiv") { - active_elem = document.createElement('div'); - active_elem.classList.add('itemleft'); - } - else if (command === "#table") { - active_elem = document.createElement('table'); - active_elem.classList.add('itemtable'); - } - active_elem.style.maxWidth = "100%"; - parent_div.appendChild(active_elem); - } - else if (command.charAt(0) === "!") { + if (command.charAt(0) === "!") { // TODO: This is sooo incredibly janky..... if (command === "!elemental") { elemental_format = !elemental_format; - } + } + else if (command === "!spacer") { + let spacer = document.createElement('div'); + spacer.classList.add("row", "my-2"); + parent_div.appendChild(spacer); + continue; + } } else { let id = command; @@ -319,12 +275,13 @@ function displayExpandedItem(item, parent_id){ } } if (id === "slots") { - let p_elem = document.createElement("p"); + let p_elem = document.createElement("div"); + p_elem.classList.add("col"); + // PROPER POWDER DISPLAYING let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]); let powderPrefix = document.createElement("b"); - powderPrefix.classList.add("powderLeft"); powderPrefix.classList.add("left"); powderPrefix.textContent = "Powder Slots: " + item.get(id) + " ["; p_elem.appendChild(powderPrefix); @@ -337,21 +294,21 @@ function displayExpandedItem(item, parent_id){ } let powderSuffix = document.createElement("b"); - powderSuffix.classList.add("powderRight"); powderSuffix.classList.add("left"); powderSuffix.textContent = "]"; p_elem.appendChild(powderSuffix); - active_elem.appendChild(p_elem); + parent_div.appendChild(p_elem); } else if (id === "set") { if (item.get("hideSet")) { continue; } - let p_elem = document.createElement("p"); - p_elem.classList.add("itemp"); + let p_elem = document.createElement("div"); + p_elem.classList.add("col"); p_elem.textContent = "Set: " + item.get(id).toString(); - active_elem.appendChild(p_elem); + parent_div.appendChild(p_elem); } else if (id === "majorIds") { + //console.log(item.get(id)); for (let majorID of item.get(id)) { - let p_elem = document.createElement("p"); - p_elem.classList.add("itemp"); + let p_elem = document.createElement("div"); + p_elem.classList.add("col"); let title_elem = document.createElement("b"); let b_elem = document.createElement("b"); @@ -372,60 +329,94 @@ function displayExpandedItem(item, parent_id){ b_elem.textContent = name; p_elem.appendChild(b_elem); } - active_elem.appendChild(p_elem); + parent_div.appendChild(p_elem); } } else if (id === "lvl" && item.get("tier") === "Crafted") { - let p_elem = document.createElement("p"); - p_elem.classList.add("itemp"); + let p_elem = document.createElement("div"); + p_elem.classList.add("col"); p_elem.textContent = "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id); - active_elem.appendChild(p_elem); + parent_div.appendChild(p_elem); } else if (id === "displayName") { - let p_elem = document.createElement("a"); - p_elem.classList.add('itemp'); - p_elem.classList.add("smalltitle"); - p_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "none"); + let row = document.createElement("div"); + + let a_elem = document.createElement("a"); + row.classList.add("row", "justify-content-center"); + a_elem.classList.add("col-auto", "text-center", "item-title", "p-0"); + a_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal"); + // a_elem.style.textGrow = 1; + row.appendChild(a_elem); + + /* + FUNCTIONALITY FOR THIS FEATURE HAS SINCE BEEN REMOVED (WITH SQ2). + IF WE WANT TO USE IT IN THE FUTURE, I'VE LEFT THE CODE TO ADD IT IN HERE + */ + + //allow the plus minus element to toggle upon click: âž•âž– + let plusminus = document.createElement("div"); + plusminus.id = parent_div.id.split("-")[0] + "-pm"; + plusminus.classList.add("col", "plus_minus", "text_end"); + plusminus.style.flexGrow = 0; + plusminus.textContent = "\u2795"; + row.appendChild(plusminus); + + if (item.get("custom")) { + a_elem.href = "../custom/#" + item.get("hash"); + a_elem.textContent = item.get("displayName"); + } else if (item.get("crafted")) { + a_elem.href = "../crafter/#" + item.get("hash"); + a_elem.textContent = item.get(id); + } else { + a_elem.href = "../item/#" + item.get("displayName"); + a_elem.textContent = item.get("displayName"); + } + parent_div.appendChild(row); + + let nolink_row = document.createElement("div"); + let p_elem = document.createElement("p"); + nolink_row.classList.add("row", "justify-content-center"); + nolink_row.style.display = "none"; + p_elem.classList.add("col-auto", "text-center", "item-title", "p-0"); + p_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal"); if (item.get("custom")) { - p_elem.href = url_base.replace(/\w+.html/, "") + "customizer.html#" + item.get("hash"); p_elem.textContent = item.get("displayName"); } else if (item.get("crafted")) { - p_elem.href = url_base.replace(/\w+.html/, "") + "crafter.html#" + item.get("hash"); p_elem.textContent = item.get(id); } else { - p_elem.href = url_base.replace(/\w+.html/, "") + "item.html#" + item.get("displayName"); p_elem.textContent = item.get("displayName"); } + + nolink_row.appendChild(p_elem); + parent_div.appendChild(nolink_row); - p_elem.target = "_blank"; - active_elem.appendChild(p_elem); let img = document.createElement("img"); if (item && item.has("type")) { img.src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png"; img.alt = item.get("type"); - img.style = " z=index: 1;max-width: 64px; max-height: 64px; position: relative; top: 50%; transform: translateY(-50%);"; - let bckgrd = document.createElement("p"); - bckgrd.style = "width: 96px; height: 96px; border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "#121516 80%); margin-left: auto; margin-right: auto;" - bckgrd.classList.add("center"); - bckgrd.classList.add("itemp"); - active_elem.appendChild(bckgrd); + img.style = " z=index: 1; position: relative;"; + let container = document.createElement("div"); + + let bckgrd = document.createElement("div"); + bckgrd.classList.add("col", "px-0", "d-flex", "align-items-center", "justify-content-center");// , "no-collapse"); + bckgrd.style = "border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "hsl(0, 0%, 16%) 80%); margin-left: auto; margin-right: auto;" + bckgrd.classList.add("scaled-bckgrd"); + parent_div.appendChild(container); + container.appendChild(bckgrd); bckgrd.appendChild(img); } } else { let p_elem; - if ( !(item.get("tier") === "Crafted" && item.get("category") === "armor" && id === "hp") && (!skp_order.includes(id)) || (skp_order.includes(id) && item.get("tier") !== "Crafted" && active_elem.nodeName === "DIV") ) { //skp warp - p_elem = displayFixedID(active_elem, id, item.get(id), elemental_format); + if ( !(item.get("tier") === "Crafted" && item.get("category") === "armor" && id === "hp") && (!skp_order.includes(id)) || (skp_order.includes(id) && item.get("tier") !== "Crafted" && parent_div.nodeName === "table") ) { //skp warp + p_elem = displayFixedID(parent_div, id, item.get(id), elemental_format); } else if (item.get("tier") === "Crafted" && item.get("category") === "armor" && id === "hp") { - p_elem = displayFixedID(active_elem, id, item.get(id+"Low")+"-"+item.get(id), elemental_format); + p_elem = displayFixedID(parent_div, id, item.get(id+"Low")+"-"+item.get(id), elemental_format); } if (id === "lore") { p_elem.style = "font-style: italic"; - p_elem.classList.add("lore"); } else if (skp_order.includes(id)) { //id = str, dex, int, def, or agi - if ( item.get("tier") !== "Crafted" && active_elem.nodeName === "DIV") { - p_elem.textContent = ""; - p_elem.classList.add("itemp"); - row = document.createElement("p"); - row.classList.add("left"); + if ( item.get("tier") !== "Crafted") { + row = document.createElement("div"); + row.classList.add("col"); let title = document.createElement("b"); title.textContent = idPrefixes[id] + " "; @@ -438,10 +429,10 @@ function displayExpandedItem(item, parent_id){ boost.textContent = item.get(id); row.appendChild(title); row.appendChild(boost); - p_elem.appendChild(row); - } else if ( item.get("tier") === "Crafted" && active_elem.nodeName === "TABLE") { + parent_div.appendChild(row); + } else if ( item.get("tier") === "Crafted") { let row = displayRolledID(item, id, elemental_format); - active_elem.appendChild(row); + parent_div.appendChild(row); } } else if (id === "restrict") { p_elem.classList.add("restrict"); @@ -459,11 +450,17 @@ function displayExpandedItem(item, parent_id){ style === "positive" ? style = "negative" : style = "positive"; } if (fix_id) { - displayFixedID(active_elem, id, item.get("minRolls").get(id), elemental_format, style); + p_elem = document.createElement("div"); + p_elem.classList.add("col", "text-nowrap"); + if (id == "dex") { + console.log("dex activated at fix_id") + } + displayFixedID(p_elem, id, item.get("minRolls").get(id), elemental_format, style); + parent_div.appendChild(p_elem); } else { let row = displayRolledID(item, id, elemental_format); - active_elem.appendChild(row); + parent_div.appendChild(row); } }else{ // :/ @@ -473,8 +470,8 @@ function displayExpandedItem(item, parent_id){ //Show powder specials ;-; let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots", "ring", "bracelet", "necklace"]; if(nonConsumables.includes(item.get("type"))) { - let powder_special = document.createElement("p"); - powder_special.classList.add("left"); + let powder_special = document.createElement("div"); + powder_special.classList.add("col"); let powders = item.get("powders"); let element = ""; let power = 0; @@ -495,10 +492,9 @@ function displayExpandedItem(item, parent_id){ if (element !== "") {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]" let powderSpecial = powderSpecialStats[ skp_elements.indexOf(element)]; let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); - let specialTitle = document.createElement("p"); - let specialEffects = document.createElement("p"); - addClasses(specialTitle, ["left", "itemp", damageClasses[skp_elements.indexOf(element) + 1]]); - addClasses(specialEffects, ["left", "itemp", "nocolor"]); + let specialTitle = document.createElement("span"); + let specialEffects = document.createElement("span"); + addClasses(specialTitle, [damageClasses[skp_elements.indexOf(element) + 1]]); let effects; if (item.get("category") === "weapon") {//weapon effects = powderSpecial["weaponSpecialEffects"]; @@ -510,7 +506,7 @@ function displayExpandedItem(item, parent_id){ for (const [key,value] of effects.entries()) { if (key !== "Description") { let effect = document.createElement("p"); - effect.classList.add("itemp"); + effect.classList.add("m-0"); effect.textContent = key + ": " + value[power] + specialSuffixes.get(key); if(key === "Damage"){ effect.textContent += elementIcons[skp_elements.indexOf(element)]; @@ -530,8 +526,8 @@ function displayExpandedItem(item, parent_id){ } if(item.get("tier") && item.get("tier") === "Crafted") { - let dura_elem = document.createElement("p"); - dura_elem.classList.add("itemp"); + let dura_elem = document.createElement("div"); + dura_elem.classList.add("col"); let dura = []; let suffix = ""; if(nonConsumables.includes(item.get("type"))) { @@ -543,8 +539,7 @@ function displayExpandedItem(item, parent_id){ suffix = " sec." let charges = document.createElement("b"); charges.textContent = "Charges: " + item.get("charges"); - charges.classList.add("spaceleft"); - active_elem.appendChild(charges); + parent_div.appendChild(charges); } if (typeof(dura) === "string") { @@ -552,16 +547,21 @@ function displayExpandedItem(item, parent_id){ } else { dura_elem.textContent += dura[0]+"-"+dura[1] + suffix; } - active_elem.append(dura_elem); + parent_div.append(dura_elem); } //Show item tier if (item.get("tier") && item.get("tier") !== " ") { - let item_desc_elem = document.createElement("p"); - item_desc_elem.classList.add('itemp'); + let item_desc_elem = document.createElement("div"); + item_desc_elem.classList.add("col"); item_desc_elem.classList.add(item.get("tier")); - item_desc_elem.textContent = item.get("tier")+" "+item.get("type"); - active_elem.append(item_desc_elem); + if (tome_types.includes(item.get("type"))) { + tome_type_map = new Map([["weaponTome", "Weapon Tome"],["armorTome", "Armor Tome"],["guildTome", "Guild Tome"]]); + item_desc_elem.textContent = item.get("tier")+" "+tome_type_map.get(item.get("type")); + } else { + item_desc_elem.textContent = item.get("tier")+" "+item.get("type"); + } + parent_div.append(item_desc_elem); } //Show item hash if applicable @@ -572,7 +572,7 @@ function displayExpandedItem(item, parent_id){ item_desc_elem.style.wordWrap = "break-word"; item_desc_elem.style.wordBreak = "break-word"; item_desc_elem.textContent = item.get("hash"); - active_elem.append(item_desc_elem); + parent_div.append(item_desc_elem); } if (item.get("category") === "weapon") { @@ -1021,9 +1021,12 @@ function displayRolledID(item, id, elemental_format) { function displayFixedID(active, id, value, elemental_format, style) { if (style) { - let row = document.createElement('tr'); - let desc_elem = document.createElement('td'); - desc_elem.classList.add('left'); + let row = document.createElement('div'); + row.classList.add("row"); + let desc_elem = document.createElement('div'); + desc_elem.classList.add('col'); + desc_elem.classList.add('text-start'); + if (elemental_format) { apply_elemental_format(desc_elem, id); } @@ -1032,8 +1035,9 @@ function displayFixedID(active, id, value, elemental_format, style) { } row.appendChild(desc_elem); - let value_elem = document.createElement('td'); - value_elem.classList.add('right'); + let value_elem = document.createElement('div'); + value_elem.classList.add('col'); + value_elem.classList.add('text-end'); value_elem.classList.add(style); value_elem.textContent = value + idSuffixes[id]; row.appendChild(value_elem); @@ -1045,8 +1049,8 @@ function displayFixedID(active, id, value, elemental_format, style) { if (value === "0-0" || value === "0-0\u279c0-0") { return; } - let p_elem = document.createElement('p'); - p_elem.classList.add('itemp'); + let p_elem = document.createElement('div'); + p_elem.classList.add('col'); if (elemental_format) { apply_elemental_format(p_elem, id, value); } @@ -1057,6 +1061,28 @@ function displayFixedID(active, id, value, elemental_format, style) { return p_elem; } } + +function displayPoisonDamage(overallparent_elem, build) { + overallparent_elem.textContent = ""; + + //Title + let title_elemavg = document.createElement("b"); + title_elemavg.textContent = "Poison Stats"; + overallparent_elem.append(title_elemavg); + + let overallpoisonDamage = document.createElement("p"); + let overallpoisonDamageFirst = document.createElement("span"); + let overallpoisonDamageSecond = document.createElement("span"); + let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct"))/100 /3); + overallpoisonDamageFirst.textContent = "Poison Tick: "; + overallpoisonDamageSecond.textContent = Math.max(poison_tick,0); + overallpoisonDamageSecond.classList.add("Damage"); + + overallpoisonDamage.appendChild(overallpoisonDamageFirst); + overallpoisonDamage.appendChild(overallpoisonDamageSecond); + overallparent_elem.append(overallpoisonDamage); +} + function displayEquipOrder(parent_elem,buildOrder){ parent_elem.textContent = ""; const order = buildOrder.slice(); @@ -1076,29 +1102,6 @@ function displayEquipOrder(parent_elem,buildOrder){ } } -function displayPoisonDamage(overallparent_elem, build) { - overallparent_elem.textContent = ""; - - //Title - let title_elemavg = document.createElement("p"); - title_elemavg.classList.add("smalltitle"); - title_elemavg.classList.add("Normal"); - title_elemavg.textContent = "Poison Stats"; - overallparent_elem.append(title_elemavg); - - let overallpoisonDamage = document.createElement("p"); - overallpoisonDamage.classList.add("lessbottom"); - let overallpoisonDamageFirst = document.createElement("b"); - let overallpoisonDamageSecond = document.createElement("b"); - let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct") + build.externalStats.get("poisonPct"))/100 /3); - overallpoisonDamageFirst.textContent = "Poison Tick: "; - overallpoisonDamageSecond.textContent = Math.max(poison_tick,0); - overallpoisonDamageSecond.classList.add("Damage"); - - overallpoisonDamage.appendChild(overallpoisonDamageFirst); - overallpoisonDamage.appendChild(overallpoisonDamageSecond); - overallparent_elem.append(overallpoisonDamage); -} function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ console.log("Melee Stats"); @@ -1314,40 +1317,8 @@ function displayDefenseStats(parent_elem, build, insertSummary){ } const stats = defenseStats.slice(); - if (!insertSummary) { - let title_elem = document.createElement("p"); - title_elem.textContent = "Defense Stats"; - title_elem.classList.add("title"); - title_elem.classList.add("Normal"); - title_elem.classList.add("itemp"); - parent_elem.append(title_elem); - - let base_stat_elem = document.createElement("p"); - base_stat_elem.id = "base-stat"; - parent_elem.append(base_stat_elem); - - let mock_item = new Map(); - - mock_item.set("fixID", true); - let mock_minRolls = new Map(); - mock_item.set("minRolls", mock_minRolls); - const stats = ["hp", "hpBonus", "hprRaw", "hprPct", "fDef", "wDef", "aDef", "tDef", "eDef", - "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct"]; - for (const stat of stats) { - if (rolledIDs.includes(stat)) { - mock_minRolls.set(stat, build.statMap.get(stat)); - } - else { - mock_item.set(stat, build.statMap.get(stat)); - } - } - mock_item.set("powders", []); - displayExpandedItem(mock_item, base_stat_elem.id); - } - - parent_elem.append(document.createElement("br")); - let statsTable = document.createElement("table"); - statsTable.classList.add("itemtable"); + // parent_elem.append(document.createElement("br")); + let statsTable = document.createElement("div"); //[total hp, ehp, total hpr, ehpr, [def%, agi%], [edef,tdef,wdef,fdef,adef]] for(const i in stats){ @@ -1361,18 +1332,26 @@ function displayDefenseStats(parent_elem, build, insertSummary){ } //total HP - let hpRow = document.createElement("tr"); - let hp = document.createElement("td"); + let hpRow = document.createElement("div"); + hpRow.classList.add('row'); + let hp = document.createElement("div"); + hp.classList.add('col'); hp.classList.add("Health"); - hp.classList.add("left"); + hp.classList.add("text-start"); hp.textContent = "Total HP:"; - let boost = document.createElement("td"); + let boost = document.createElement("div"); + boost.classList.add('col'); boost.textContent = stats[0]; - boost.classList.add("right"); + boost.classList.add("text-end"); hpRow.appendChild(hp); hpRow.append(boost); - statsTable.appendChild(hpRow); + + if (insertSummary) { + parent_elem.appendChild(hpRow); + } else { + statsTable.appendChild(hpRow); + } let tooltip; let tooltiptext; @@ -1380,100 +1359,113 @@ function displayDefenseStats(parent_elem, build, insertSummary){ if (!defMult) {defMult = 1} //EHP - let ehpRow = document.createElement("tr"); - let ehp = document.createElement("td"); - ehp.classList.add("left"); + let ehpRow = document.createElement("div"); + ehpRow.classList.add("row"); + let ehp = document.createElement("div"); + ehp.classList.add("col"); + ehp.classList.add("text-start"); ehp.textContent = "Effective HP:"; - boost = document.createElement("td"); + boost = document.createElement("div"); boost.textContent = stats[1][0]; - boost.classList.add("right"); + boost.classList.add("col"); + boost.classList.add("text-end"); tooltiptext = `= ${stats[0]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (1 - ${skillPointsToPercentage(build.total_skillpoints[4]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); + // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); ehpRow.appendChild(ehp); ehpRow.append(boost); - statsTable.append(ehpRow); - ehpRow = document.createElement("tr"); - ehp = document.createElement("td"); - ehp.classList.add("left"); + if (insertSummary) { + parent_elem.appendChild(ehpRow) + } else { + statsTable.append(ehpRow); + } + + ehpRow = document.createElement("div"); + ehpRow.classList.add("row"); + ehp = document.createElement("div"); + ehp.classList.add("col"); + ehp.classList.add("text-start"); ehp.textContent = "Effective HP (no agi):"; - boost = document.createElement("td"); + boost = document.createElement("div"); boost.textContent = stats[1][1]; - boost.classList.add("right"); + boost.classList.add("col"); + boost.classList.add("text-end"); tooltiptext = `= ${stats[0]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); + // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); ehpRow.appendChild(ehp); ehpRow.append(boost); statsTable.append(ehpRow); //total HPR - let hprRow = document.createElement("tr"); - let hpr = document.createElement("td"); + let hprRow = document.createElement("div"); + hprRow.classList.add("row") + let hpr = document.createElement("div"); hpr.classList.add("Health"); - hpr.classList.add("left"); + hpr.classList.add("col"); + hpr.classList.add("text-start"); hpr.textContent = "HP Regen (Total):"; - boost = document.createElement("td"); + boost = document.createElement("div"); boost.textContent = stats[2]; - boost.classList.add("right"); + boost.classList.add("col"); + boost.classList.add("text-end"); hprRow.appendChild(hpr); hprRow.appendChild(boost); - statsTable.appendChild(hprRow); + + if (insertSummary) { + parent_elem.appendChild(hprRow); + } else { + statsTable.appendChild(hprRow); + } + //EHPR - let ehprRow = document.createElement("tr"); - let ehpr = document.createElement("td"); - ehpr.classList.add("left"); + let ehprRow = document.createElement("div"); + ehprRow.classList.add("row") + let ehpr = document.createElement("div"); + ehpr.classList.add("col"); + ehpr.classList.add("text-start"); ehpr.textContent = "Effective HP Regen:"; - boost = document.createElement("td"); + boost = document.createElement("div"); boost.textContent = stats[3][0]; - boost.classList.add("right"); + boost.classList.add("col"); + boost.classList.add("text-end"); tooltiptext = `= ${stats[2]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (1 - ${skillPointsToPercentage(build.total_skillpoints[4]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); + // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); ehprRow.appendChild(ehpr); ehprRow.append(boost); statsTable.append(ehprRow); - /* - ehprRow = document.createElement("tr"); - ehpr = document.createElement("td"); - ehpr.classList.add("left"); - ehpr.textContent = "Effective HP Regen (no agi):"; - - boost = document.createElement("td"); - boost.textContent = stats[3][1]; - boost.classList.add("right"); - - ehprRow.appendChild(ehpr); - ehprRow.append(boost); - statsTable.append(ehprRow); */ //eledefs let eledefs = stats[5]; for (let i = 0; i < eledefs.length; i++){ - let eledefElemRow = document.createElement("tr"); + let eledefElemRow = document.createElement("div"); + eledefElemRow.classList.add("row") - let eledef = document.createElement("td"); - eledef.classList.add("left") - let eledefTitle = document.createElement("b"); + let eledef = document.createElement("div"); + eledef.classList.add("col"); + eledef.classList.add("text-start"); + let eledefTitle = document.createElement("span"); eledefTitle.textContent = damageClasses[i+1]; eledefTitle.classList.add(damageClasses[i+1]); - let defense = document.createElement("b"); + let defense = document.createElement("span"); defense.textContent = " Def (Total): "; eledef.appendChild(eledefTitle); eledef.appendChild(defense); eledefElemRow.appendChild(eledef); - let boost = document.createElement("td"); + let boost = document.createElement("div"); boost.textContent = eledefs[i]; boost.classList.add(eledefs[i] >= 0 ? "positive" : "negative"); - boost.classList.add("right"); + boost.classList.add("col"); + boost.classList.add("text-end"); let defRaw = build.statMap.get("defRaw")[i]; let defPct = build.statMap.get("defBonus")[i]/100; @@ -1484,39 +1476,51 @@ function displayDefenseStats(parent_elem, build, insertSummary){ defPct >= 0 ? defPct = "+ " + defPct: defPct = "- " + defPct; tooltiptext = `= ${defRaw} * (1 ${defPct})` } - tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); + // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); eledefElemRow.appendChild(boost); - - statsTable.appendChild(eledefElemRow); + + if (insertSummary) { + parent_elem.appendChild(eledefElemRow); + } else { + statsTable.appendChild(eledefElemRow); + } } if (!insertSummary) { //skp - let defRow = document.createElement("tr"); - let defElem = document.createElement("td"); - defElem.classList.add("left"); + let defRow = document.createElement("div"); + defRow.classList.add("row"); + let defElem = document.createElement("div"); + defElem.classList.add("col"); + defElem.classList.add("text-start"); defElem.textContent = "Damage Absorbed %:"; - boost = document.createElement("td"); - boost.classList.add("right"); + boost = document.createElement("div"); + boost.classList.add("col"); + boost.classList.add("text-end"); boost.textContent = stats[4][0] + "%"; defRow.appendChild(defElem); defRow.appendChild(boost); statsTable.append(defRow); - let agiRow = document.createElement("tr"); - let agiElem = document.createElement("td"); - agiElem.classList.add("left"); + let agiRow = document.createElement("div"); + agiRow.classList.add("row"); + let agiElem = document.createElement("div"); + agiElem.classList.add("col"); + agiElem.classList.add("text-start"); agiElem.textContent = "Dodge Chance %:"; - boost = document.createElement("td"); - boost.classList.add("right"); + boost = document.createElement("div"); + boost.classList.add("col"); + boost.classList.add("text-end"); boost.textContent = stats[4][1] + "%"; agiRow.appendChild(agiElem); agiRow.appendChild(boost); statsTable.append(agiRow); } - parent_elem.append(statsTable); + if (!insertSummary) { + parent_elem.append(statsTable); + } } function displayPowderSpecials(parent_elem, powderSpecials, build) { @@ -1691,7 +1695,6 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell overallparent_elem.append(displayNextCosts(spell, build, build.weapon.statMap)); - let critChance = skillPointsToPercentage(build.total_skillpoints[1]); let save_damages = []; From 81dfe767a3f11758002a607663a0b99c1cedfa32 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 13:44:02 -0700 Subject: [PATCH 13/30] Item display, build display (partially) --- builder/index.html | 6 +- js/build_constants.js | 4 +- js/build_encode_decode.js | 46 ++-- js/builder.js | 252 ++++++++++++++++++- js/builder_graph.js | 505 +++++++++++++++++++------------------- js/computation_graph.js | 114 +-------- js/display.js | 347 +++++++++++++------------- js/utils.js | 3 +- 8 files changed, 704 insertions(+), 573 deletions(-) diff --git a/builder/index.html b/builder/index.html index d0ab71a..73e20b1 100644 --- a/builder/index.html +++ b/builder/index.html @@ -313,7 +313,7 @@
- +
@@ -1291,7 +1291,7 @@
-
+
diff --git a/js/build_constants.js b/js/build_constants.js index 2983328..820beb7 100644 --- a/js/build_constants.js +++ b/js/build_constants.js @@ -85,7 +85,7 @@ let tome_names = [ "Guild Tome", ] let equipmentInputs = equipment_fields.map(x => x + "-choice"); -let buildFields = equipment_fields.map(x => x+"-tooltip").concat(tome_fields.map(x => x + "-tooltip")); +let build_fields = equipment_fields.map(x => x+"-tooltip"); let tomeInputs = tome_fields.map(x => x + "-choice"); let powder_inputs = [ @@ -100,7 +100,7 @@ let weapon_keys = ['dagger', 'wand', 'bow', 'relik', 'spear']; let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots']; let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace']; let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon']; -let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'].concat(tome_keys); +let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon']; let spell_disp = ['spell0-info', 'spell1-info', 'spell2-info', 'spell3-info']; let other_disp = ['build-order', 'set-info', 'int-info']; diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index 0929e4e..12b45b1 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -137,7 +137,7 @@ function decodeBuild(url_tag) { /* Stores the entire build in a string using B64 encoding and adds it to the URL. */ -function encodeBuild(build) { +function encodeBuild(build, powders) { if (build) { let build_string; @@ -147,19 +147,15 @@ function encodeBuild(build) { build_string = ""; tome_string = ""; - let crafted_idx = 0; - let custom_idx = 0; for (const item of build.items) { - if (item.get("custom")) { - let custom = "CI-"+encodeCustom(build.customItems[custom_idx],true); + if (item.statMap.get("custom")) { + let custom = "CI-"+encodeCustom(item, true); build_string += Base64.fromIntN(custom.length, 3) + custom; - custom_idx += 1; build_version = Math.max(build_version, 5); - } else if (item.get("crafted")) { - build_string += "CR-"+encodeCraft(build.craftedItems[crafted_idx]); - crafted_idx += 1; - } else if (item.get("category") === "tome") { + } else if (item.statMap.get("crafted")) { + build_string += "CR-"+encodeCraft(item); + } else if (item.statMap.get("category") === "tome") { let tome_id = item.get("id"); if (tome_id <= 60) { // valid normal tome. ID 61-63 is for NONE tomes. @@ -167,7 +163,7 @@ function encodeBuild(build) { } tome_string += Base64.fromIntN(tome_id, 1); } else { - build_string += Base64.fromIntN(item.get("id"), 3); + build_string += Base64.fromIntN(item.statMap.get("id"), 3); } } @@ -175,7 +171,7 @@ function encodeBuild(build) { build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 } build_string += Base64.fromIntN(build.level, 2); - for (const _powderset of build.powders) { + for (const _powderset of powders) { let n_bits = Math.ceil(_powderset.length / 6); build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. // Slice copy. @@ -196,26 +192,24 @@ function encodeBuild(build) { } } -function copyBuild(build) { - if (build) { - copyTextToClipboard(url_base+location.hash); - document.getElementById("copy-button").textContent = "Copied!"; - } +function copyBuild() { + copyTextToClipboard(url_base+location.hash); + document.getElementById("copy-button").textContent = "Copied!"; } function shareBuild(build) { if (build) { let text = url_base+location.hash+"\n"+ "WynnBuilder build:\n"+ - "> "+build.helmet.get("displayName")+"\n"+ - "> "+build.chestplate.get("displayName")+"\n"+ - "> "+build.leggings.get("displayName")+"\n"+ - "> "+build.boots.get("displayName")+"\n"+ - "> "+build.ring1.get("displayName")+"\n"+ - "> "+build.ring2.get("displayName")+"\n"+ - "> "+build.bracelet.get("displayName")+"\n"+ - "> "+build.necklace.get("displayName")+"\n"+ - "> "+build.weapon.get("displayName")+" ["+build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]"; + "> "+build.helmet.statMap.get("displayName")+"\n"+ + "> "+build.chestplate.statMap.get("displayName")+"\n"+ + "> "+build.leggings.statMap.get("displayName")+"\n"+ + "> "+build.boots.statMap.get("displayName")+"\n"+ + "> "+build.ring1.statMap.get("displayName")+"\n"+ + "> "+build.ring2.statMap.get("displayName")+"\n"+ + "> "+build.bracelet.statMap.get("displayName")+"\n"+ + "> "+build.necklace.statMap.get("displayName")+"\n"+ + "> "+build.weapon.statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]"; copyTextToClipboard(text); document.getElementById("share-button").textContent = "Copied!"; } diff --git a/js/builder.js b/js/builder.js index 1ce6a3f..0251fef 100644 --- a/js/builder.js +++ b/js/builder.js @@ -1,3 +1,5 @@ +let build_powders; + function getItemNameFromID(id) { if (redirectMap.has(id)) { return getItemNameFromID(redirectMap.get(id)); @@ -142,15 +144,263 @@ function show_tab(tab) { document.getElementById("tab-" + tab.split("-")[0] + "-btn").classList.add("selected-btn"); } +// autocomplete initialize +function init_autocomplete() { + let dropdowns = new Map(); + for (const eq of equipment_keys) { + if (tome_keys.includes(eq)) { + continue; + } + // build dropdown + let item_arr = []; + if (eq == 'weapon') { + for (const weaponType of weapon_keys) { + for (const weapon of itemLists.get(weaponType)) { + let item_obj = itemMap.get(weapon); + if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { + continue; + } + if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { + continue; + } + item_arr.push(weapon); + } + } + } else { + for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) { + let item_obj = itemMap.get(item); + if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { + continue; + } + if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { + continue; + } + item_arr.push(item) + } + } + + // create dropdown + dropdowns.set(eq, new autoComplete({ + data: { + src: item_arr + }, + selector: "#"+ eq +"-choice", + wrapper: false, + resultsList: { + maxResults: 1000, + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + // dynamic result loc + let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); + list.style.top = position.bottom + window.scrollY +"px"; + list.style.left = position.x+"px"; + list.style.width = position.width+"px"; + list.style.maxHeight = position.height * 2 +"px"; + + if (!data.results.length) { + message = document.createElement('li'); + message.classList.add('scaled-font'); + message.textContent = "No results found!"; + list.prepend(message); + } + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + element: (item, data) => { + item.classList.add(itemMap.get(data.value).tier); + }, + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; + } + event.target.dispatchEvent(new Event('input')); + }, + }, + } + })); + } + + for (const eq of tome_keys) { + // build dropdown + let tome_arr = []; + for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) { + let tome_obj = tomeMap.get(tome); + if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") { + continue; + } + //this should suffice for tomes - jank + if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) { + continue; + } + let tome_name = tome; + tome_arr.push(tome_name); + } + + // create dropdown + dropdowns.set(eq, new autoComplete({ + data: { + src: tome_arr + }, + selector: "#"+ eq +"-choice", + wrapper: false, + resultsList: { + maxResults: 1000, + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + // dynamic result loc + let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); + list.style.top = position.bottom + window.scrollY +"px"; + list.style.left = position.x+"px"; + list.style.width = position.width+"px"; + list.style.maxHeight = position.height * 2 +"px"; + + if (!data.results.length) { + message = document.createElement('li'); + message.classList.add('scaled-font'); + message.textContent = "No results found!"; + list.prepend(message); + } + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + element: (tome, data) => { + tome.classList.add(tomeMap.get(data.value).tier); + }, + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; + } + }, + }, + } + })); + } + + let filter_loc = ["filter1", "filter2", "filter3", "filter4"]; + for (const i of filter_loc) { + dropdowns.set(i+"-choice", new autoComplete({ + data: { + src: sq2ItemFilters, + }, + selector: "#"+i+"-choice", + wrapper: false, + resultsList: { + tabSelect: true, + noResults: true, + class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", + element: (list, data) => { + // dynamic result loc + console.log(i); + list.style.zIndex = "100"; + let position = document.getElementById(i+"-dropdown").getBoundingClientRect(); + window_pos = document.getElementById("search-container").getBoundingClientRect(); + list.style.top = position.bottom - window_pos.top + 5 +"px"; + list.style.left = position.x - window_pos.x +"px"; + list.style.width = position.width+"px"; + + if (!data.results.length) { + message = document.createElement('li'); + message.classList.add('scaled-font'); + message.textContent = "No filters found!"; + list.prepend(message); + } + }, + }, + resultItem: { + class: "scaled-font search-item", + selected: "dark-5", + }, + events: { + input: { + selection: (event) => { + if (event.detail.selection.value) { + event.target.value = event.detail.selection.value; + } + }, + }, + } + })); + } +} + +function collapse_element(elmnt) { + elem_list = document.querySelector(elmnt).children; + if (elem_list) { + for (elem of elem_list) { + if (elem.classList.contains("no-collapse")) { continue; } + if (elem.style.display == "none") { + elem.style.display = ""; + } else { + elem.style.display = "none"; + } + } + } + // macy quirk + window.dispatchEvent(new Event('resize')); + // weird bug where display: none overrides?? + document.querySelector(elmnt).style.removeProperty('display'); +} // TODO: Learn and use await function init() { console.log("builder.js init"); init_autocomplete(); + + // Other "main" stuff + // Spell dropdowns + for (const i of spell_disp) { + document.querySelector("#"+i+"Avg").addEventListener("click", () => toggle_spell_tab(i)); + } + for (const eq of equipment_keys) { + document.querySelector("#"+eq+"-tooltip").addEventListener("click", () => collapse_element('#'+eq+'-tooltip')); + } + + // Masonry setup + let masonry = Macy({ + container: "#masonry-container", + columns: 1, + mobileFirst: true, + breakAt: { + 1200: 4, + }, + margin: { + x: 20, + y: 20, + } + + }); + + let search_masonry = Macy({ + container: "#search-results", + columns: 1, + mobileFirst: true, + breakAt: { + 1200: 4, + }, + margin: { + x: 20, + y: 20, + } + + }); decodeBuild(url_tag); + builder_graph_init(); } -//load_init(init3); (async function() { let load_promises = [ load_init(), load_ing_init(), load_tome_init() ]; await Promise.all(load_promises); diff --git a/js/builder_graph.js b/js/builder_graph.js index 4fcd47b..083eb32 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1,22 +1,180 @@ - - -class BuildEncodeNode extends ComputeNode { - constructor() { - super("builder-encode"); +/** + * Node for getting an item's stats from an item input field. + * + * Signature: ItemInputNode() => Item | null + */ +class ItemInputNode extends InputNode { + /** + * Make an item stat pulling compute node. + * + * @param name: Name of this node. + * @param item_input_field: Input field (html element) to listen for item names from. + * @param none_item: Item object to use as the "none" for this field. + */ + constructor(name, item_input_field, none_item) { + super(name, item_input_field); + this.none_item = new Item(none_item); + this.none_item.statMap.set('NONE', true); } compute_func(input_map) { - if (input_map.size !== 1) { throw "BuildEncodeNode accepts exactly one input (build)"; } - const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - return encodeBuild(build); + // built on the assumption of no one will type in CI/CR letter by letter + + let item_text = this.input_field.value; + if (!item_text) { + return this.none_item; + } + + let item; + + if (item_text.slice(0, 3) == "CI-") { + item = getCustomFromHash(item_text); + } + else if (item_text.slice(0, 3) == "CR-") { + item = getCraftFromHash(item_text); + } + else if (itemMap.has(item_text)) { + item = new Item(itemMap.get(item_text)); + } + else if (tomeMap.has(item_text)) { + item = new Item(tomeMap.get(item_text)); + } + + if (item) { + let type_match; + if (this.none_item.statMap.get('category') === 'weapon') { + type_match = item.statMap.get('category') === 'weapon'; + } else { + type_match = item.statMap.get('type') === this.none_item.statMap.get('type'); + } + if (type_match) { return item; } + } + return null; } } -class URLUpdateNode extends ComputeNode { - constructor() { - super("builder-url-update"); +/** + * Node for updating item input fields from parsed items. + * + * Signature: ItemInputDisplayNode(item: Item) => null + */ +class ItemInputDisplayNode extends ComputeNode { + + constructor(name, eq, item_image) { + super(name); + this.input_field = document.getElementById(eq+"-choice"); + this.health_field = document.getElementById(eq+"-health"); + this.level_field = document.getElementById(eq+"-lv"); + this.image = item_image; + this.fail_cb = true; } + compute_func(input_map) { + if (input_map.size !== 1) { throw "ItemInputDisplayNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + this.input_field.classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set', 'Crafted', 'Custom'); + this.input_field.classList.add("text-light"); + this.image.classList.remove('Normal-shadow', 'Unique-shadow', 'Rare-shadow', 'Legendary-shadow', 'Fabled-shadow', 'Mythic-shadow', 'Set-shadow', 'Crafted-shadow', 'Custom-shadow'); + + if (!item) { + this.input_field.classList.add("is-invalid"); + return null; + } + + if (item.statMap.has('NONE')) { + return null; + } + const tier = item.statMap.get('tier'); + this.input_field.classList.add(tier); + if (this.health_field) { + // Doesn't exist for weapons. + this.health_field.textContent = item.statMap.get('hp'); + } + this.level_field.textContent = item.statMap.get('lvl'); + this.image.classList.add(tier + "-shadow"); + return null; + } +} + +/** + * Node for rendering an item. + * + * Signature: ItemDisplayNode(item: Item) => null + */ +class ItemDisplayNode extends ComputeNode { + constructor(name, target_elem) { + super(name); + this.target_elem = target_elem; + } + + compute_func(input_map) { + if (input_map.size !== 1) { throw "ItemInputDisplayNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + displayExpandedItem(item.statMap, this.target_elem); + collapse_element("#"+this.target_elem); + } +} + +/** + * Change the weapon to match correct type. + * + * Signature: WeaponInputDisplayNode(item: Item) => null + */ +class WeaponInputDisplayNode extends ComputeNode { + + constructor(name, image_field) { + super(name); + this.image = image_field; + } + + compute_func(input_map) { + if (input_map.size !== 1) { throw "WeaponDisplayNode accepts exactly one input (item)"; } + const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + + const type = item.statMap.get('type'); + this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png'); + } +} + +/** + * Encode the build into a url-able string. + * + * Signature: BuildEncodeNode(build: Build, + helmet-powder: List[powder], + chestplate-powder: List[powder], + leggings-powder: List[powder], + boots-powder: List[powder], + weapon-powder: List[powder]) => str + */ +class BuildEncodeNode extends ComputeNode { + constructor() { super("builder-encode"); } + + compute_func(input_map) { + const build = input_map.get('build'); + let powders = [ + input_map.get('helmet-powder'), + input_map.get('chestplate-powder'), + input_map.get('leggings-powder'), + input_map.get('boots-powder'), + input_map.get('weapon-powder') + ]; + // TODO: grr global state for copy button.. + player_build = build; + build_powders = powders; + return encodeBuild(build, powders); + } +} + +/** + * Update the window's URL. + * + * Signature: URLUpdateNode(build_str: str) => null + */ +class URLUpdateNode extends ComputeNode { + constructor() { super("builder-url-update"); } + compute_func(input_map) { if (input_map.size !== 1) { throw "URLUpdateNode accepts exactly one input (build_str)"; } const [build_str] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element @@ -24,10 +182,25 @@ class URLUpdateNode extends ComputeNode { } } +/** + * Create a "build" object from a set of equipments. + * Returns a new Build object, or null if all items are NONE items. + * + * TODO: add tomes + * + * Signature: BuildAssembleNode(helmet-input: Item, + * chestplate-input: Item, + * leggings-input: Item, + * boots-input: Item, + * ring1-input: Item, + * ring2-input: Item, + * bracelet-input: Item, + * necklace-input: Item, + * weapon-input: Item, + * level-input: int) => Build | null + */ class BuildAssembleNode extends ComputeNode { - constructor() { - super("builder-make-build"); - } + constructor() { super("builder-make-build"); } compute_func(input_map) { let equipments = [ @@ -54,11 +227,15 @@ class BuildAssembleNode extends ComputeNode { } } +/** + * Read an input field and parse into a list of powderings. + * Every two characters makes one powder. If parsing fails, NULL is returned. + * + * Signature: PowderInputNode() => List[powder] | null + */ class PowderInputNode extends InputNode { - constructor(name, input_field) { - super(name, input_field); - } + constructor(name, input_field) { super(name, input_field); } compute_func(input_map) { // TODO: haha improve efficiency to O(n) dumb @@ -81,6 +258,13 @@ class PowderInputNode extends InputNode { } } +/** + * Select a spell+spell "variation" based on a build / spell idx. + * Right now this isn't much logic and is only used to abstract away major id interactions + * but will become significantly more complex in wynn2. + * + * Signature: SpellSelectNode(build: Build) => [Spell, SpellParts] + */ class SpellSelectNode extends ComputeNode { constructor(spell_num) { super("builder-spell"+spell_num+"-select"); @@ -111,10 +295,18 @@ class SpellSelectNode extends ComputeNode { } } +/** + * Compute spell damage of spell parts. + * Currently kinda janky / TODO while we rework the internal rep. of spells. + * + * Signature: SpellDamageCalcNode(weapon-input: Item, + * build: Build, + * weapon-powder: List[powder], + * spell-info: [Spell, SpellParts]) => List[SpellDamage] + */ class SpellDamageCalcNode extends ComputeNode { constructor(spell_num) { super("builder-spell"+spell_num+"-calc"); - this.spell_idx = spell_num; } compute_func(input_map) { @@ -148,6 +340,15 @@ class SpellDamageCalcNode extends ComputeNode { } } + +/** + * Display spell damage from spell parts. + * Currently kinda janky / TODO while we rework the internal rep. of spells. + * + * Signature: SpellDisplayNode(build: Build, + * spell-info: [Spell, SpellParts], + * spell-damage: List[SpellDamage]) => null + */ class SpellDisplayNode extends ComputeNode { constructor(spell_num) { super("builder-spell"+spell_num+"-display"); @@ -168,26 +369,44 @@ class SpellDisplayNode extends ComputeNode { } } +/** + * Display build stats. + * + * Signature: BuildDisplayNode(build: Build) => null + */ +class BuildDisplayNode extends ComputeNode { + constructor(spell_num) { super("builder-stats-display"); } + + compute_func(input_map) { + if (input_map.size !== 1) { throw "BuildDisplayNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + displayBuildStats('overall-stats', build, build_all_display_commands); + displayBuildStats("offensive-stats", build, build_offensive_display_commands); + displaySetBonuses("set-info", build); + } +} + let item_nodes = []; let powder_nodes = []; let spelldmg_nodes = []; -document.addEventListener('DOMContentLoaded', function() { +function builder_graph_init() { // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). - for (const [eq, none_item] of zip(equipment_fields, none_items)) { + for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) { let input_field = document.getElementById(eq+"-choice"); let item_image = document.getElementById(eq+"-img"); let item_input = new ItemInputNode(eq+'-input', input_field, none_item); item_nodes.push(item_input); - new ItemInputDisplayNode(eq+'-display', input_field, item_image).link_to(item_input); + new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); + new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input); //new PrintNode(eq+'-debug').link_to(item_input); //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); } // weapon image changer node. let weapon_image = document.getElementById("weapon-img"); - new WeaponDisplayNode('weapon-type', weapon_image).link_to(item_nodes[8]); + new WeaponInputDisplayNode('weapon-type', weapon_image).link_to(item_nodes[8]); // Level input node. let level_input = new InputNode('level-input', document.getElementById('level-choice')); @@ -198,10 +417,19 @@ document.addEventListener('DOMContentLoaded', function() { build_node.link_to(input); } build_node.link_to(level_input); + new BuildDisplayNode().link_to(build_node, 'build'); + + let build_encode_node = new BuildEncodeNode(); + build_encode_node.link_to(build_node, 'build'); + + let url_update_node = new URLUpdateNode(); + url_update_node.link_to(build_encode_node, 'build-str'); for (const input of powder_inputs) { - powder_nodes.push(new PowderInputNode(input, document.getElementById(input))); + let powder_node = new PowderInputNode(input, document.getElementById(input)); + powder_nodes.push(powder_node); + build_encode_node.link_to(powder_node, input); } for (let i = 0; i < 4; ++i) { @@ -222,234 +450,5 @@ document.addEventListener('DOMContentLoaded', function() { } console.log("Set up graph"); - - // Other "main" stuff - // TODO: consolidate and comment - - // Spell dropdowns - for (const i of spell_disp) { - document.querySelector("#"+i+"Avg").addEventListener("click", () => toggle_spell_tab(i)); - } - - // Masonry setup - let masonry = Macy({ - container: "#masonry-container", - columns: 1, - mobileFirst: true, - breakAt: { - 1200: 4, - }, - margin: { - x: 20, - y: 20, - } - - }); - - let search_masonry = Macy({ - container: "#search-results", - columns: 1, - mobileFirst: true, - breakAt: { - 1200: 4, - }, - margin: { - x: 20, - y: 20, - } - - }); -}); - -// autocomplete initialize -function init_autocomplete() { - let dropdowns = new Map(); - for (const eq of equipment_keys) { - if (tome_keys.includes(eq)) { - continue; - } - // build dropdown - let item_arr = []; - if (eq == 'weapon') { - for (const weaponType of weapon_keys) { - for (const weapon of itemLists.get(weaponType)) { - let item_obj = itemMap.get(weapon); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { - continue; - } - item_arr.push(weapon); - } - } - } else { - for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) { - let item_obj = itemMap.get(item); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { - continue; - } - item_arr.push(item) - } - } - - // create dropdown - dropdowns.set(eq, new autoComplete({ - data: { - src: item_arr - }, - selector: "#"+ eq +"-choice", - wrapper: false, - resultsList: { - maxResults: 1000, - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - // dynamic result loc - let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); - list.style.top = position.bottom + window.scrollY +"px"; - list.style.left = position.x+"px"; - list.style.width = position.width+"px"; - list.style.maxHeight = position.height * 2 +"px"; - - if (!data.results.length) { - message = document.createElement('li'); - message.classList.add('scaled-font'); - message.textContent = "No results found!"; - list.prepend(message); - } - }, - }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - element: (item, data) => { - item.classList.add(itemMap.get(data.value).tier); - }, - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - } - event.target.dispatchEvent(new Event('input')); - }, - }, - } - })); - } - - for (const eq of tome_keys) { - // build dropdown - let tome_arr = []; - for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) { - let tome_obj = tomeMap.get(tome); - if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") { - continue; - } - //this should suffice for tomes - jank - if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) { - continue; - } - let tome_name = tome; - tome_arr.push(tome_name); - } - - // create dropdown - dropdowns.set(eq, new autoComplete({ - data: { - src: tome_arr - }, - selector: "#"+ eq +"-choice", - wrapper: false, - resultsList: { - maxResults: 1000, - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - // dynamic result loc - let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); - list.style.top = position.bottom + window.scrollY +"px"; - list.style.left = position.x+"px"; - list.style.width = position.width+"px"; - list.style.maxHeight = position.height * 2 +"px"; - - if (!data.results.length) { - message = document.createElement('li'); - message.classList.add('scaled-font'); - message.textContent = "No results found!"; - list.prepend(message); - } - }, - }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - element: (tome, data) => { - tome.classList.add(tomeMap.get(data.value).tier); - }, - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - } - }, - }, - } - })); - } - - let filter_loc = ["filter1", "filter2", "filter3", "filter4"]; - for (const i of filter_loc) { - dropdowns.set(i+"-choice", new autoComplete({ - data: { - src: sq2ItemFilters, - }, - selector: "#"+i+"-choice", - wrapper: false, - resultsList: { - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - // dynamic result loc - console.log(i); - list.style.zIndex = "100"; - let position = document.getElementById(i+"-dropdown").getBoundingClientRect(); - window_pos = document.getElementById("search-container").getBoundingClientRect(); - list.style.top = position.bottom - window_pos.top + 5 +"px"; - list.style.left = position.x - window_pos.x +"px"; - list.style.width = position.width+"px"; - - if (!data.results.length) { - message = document.createElement('li'); - message.classList.add('scaled-font'); - message.textContent = "No filters found!"; - list.prepend(message); - } - }, - }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - } - }, - }, - } - })); - } } + diff --git a/js/computation_graph.js b/js/computation_graph.js index fc5ad46..11113bd 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -22,10 +22,10 @@ class ComputeNode { * Request update of this compute node. Pushes updates to children. */ update() { - if (!this.dirty) { + if (this.inputs_dirty_count != 0) { return; } - if (this.inputs_dirty_count != 0) { + if (!this.dirty) { return; } let calc_inputs = new Map(); @@ -128,6 +128,9 @@ class PrintNode extends ComputeNode { /** * Node for getting an input from an input field. + * Fires updates whenever the input field is updated. + * + * Signature: InputNode() => str */ class InputNode extends ComputeNode { constructor(name, input_field) { @@ -141,110 +144,3 @@ class InputNode extends ComputeNode { return this.input_field.value; } } - -/** - * Node for getting an item's stats from an item input field. - */ -class ItemInputNode extends InputNode { - /** - * Make an item stat pulling compute node. - * - * @param name: Name of this node. - * @param item_input_field: Input field (html element) to listen for item names from. - * @param none_item: Item object to use as the "none" for this field. - */ - constructor(name, item_input_field, none_item) { - super(name, item_input_field); - this.none_item = new Item(none_item); - this.none_item.statMap.set('NONE', true); - } - - compute_func(input_map) { - // built on the assumption of no one will type in CI/CR letter by letter - - let item_text = this.input_field.value; - if (!item_text) { - return this.none_item; - } - - let item; - - if (item_text.slice(0, 3) == "CI-") { - item = getCustomFromHash(item_text); - } - else if (item_text.slice(0, 3) == "CR-") { - item = getCraftFromHash(item_text); - } - else if (itemMap.has(item_text)) { - item = new Item(itemMap.get(item_text)); - } - else if (tomeMap.has(item_text)) { - item = new Item(tomeMap.get(item_text)); - } - - if (item) { - let type_match; - if (this.none_item.statMap.get('category') === 'weapon') { - type_match = item.statMap.get('category') === 'weapon'; - } else { - type_match = item.statMap.get('type') === this.none_item.statMap.get('type'); - } - if (type_match) { return item; } - } - return null; - } -} - -/** - * Node for updating item input fields from parsed items. - */ -class ItemInputDisplayNode extends ComputeNode { - - constructor(name, item_input_field, item_image) { - super(name); - this.input_field = item_input_field; - this.image = item_image; - this.fail_cb = true; - } - - compute_func(input_map) { - if (input_map.size !== 1) { throw "ItemInputDisplayNode accepts exactly one input (item)"; } - const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - - this.input_field.classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set', 'Crafted', 'Custom'); - this.input_field.classList.add("text-light"); - this.image.classList.remove('Normal-shadow', 'Unique-shadow', 'Rare-shadow', 'Legendary-shadow', 'Fabled-shadow', 'Mythic-shadow', 'Set-shadow', 'Crafted-shadow', 'Custom-shadow'); - - if (!item) { - this.input_field.classList.add("is-invalid"); - return null; - } - - if (item.statMap.has('NONE')) { - return null; - } - const tier = item.statMap.get('tier'); - this.input_field.classList.add(tier); - this.image.classList.add(tier + "-shadow"); - return null; - } -} - -/** - * Change the weapon to match correct type. - */ -class WeaponDisplayNode extends ComputeNode { - - constructor(name, image_field) { - super(name); - this.image = image_field; - } - - compute_func(input_map) { - if (input_map.size !== 1) { throw "WeaponDisplayNode accepts exactly one input (item)"; } - const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - - const type = item.statMap.get('type'); - this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png'); - } -} diff --git a/js/display.js b/js/display.js index 0ef1c02..76f85cf 100644 --- a/js/display.js +++ b/js/display.js @@ -46,7 +46,7 @@ function displaySetBonuses(parent_id,build) { } for (const [setName, count] of build.activeSetCounts) { - const active_set = sets[setName]; + const active_set = sets.get(setName); if (active_set["hidden"]) { continue; } let set_elem = document.createElement('p'); @@ -353,12 +353,12 @@ function displayExpandedItem(item, parent_id){ */ //allow the plus minus element to toggle upon click: âž•âž– - let plusminus = document.createElement("div"); - plusminus.id = parent_div.id.split("-")[0] + "-pm"; - plusminus.classList.add("col", "plus_minus", "text_end"); - plusminus.style.flexGrow = 0; - plusminus.textContent = "\u2795"; - row.appendChild(plusminus); + //let plusminus = document.createElement("div"); + //plusminus.id = parent_div.id.split("-")[0] + "-pm"; + //plusminus.classList.add("col", "plus_minus", "text_end"); + //plusminus.style.flexGrow = 0; + //plusminus.textContent = "\u2795"; + //row.appendChild(plusminus); if (item.get("custom")) { a_elem.href = "../custom/#" + item.get("hash"); @@ -595,12 +595,17 @@ function displayExpandedItem(item, parent_id){ } } -/* Displays stats about a recipe that are NOT displayed in the craft stats. -* Includes: mat name and amounts -* ingred names in an "array" with ingred effectiveness +/* +* Displays stats about a recipe that are NOT displayed in the craft stats. +* Includes: mat name and amounts, ingred names in an "array" with ingred effectiveness */ function displayRecipeStats(craft, parent_id) { let elem = document.getElementById(parent_id); + if (!elem.classList.contains("col")) { + elem.classList.add("col"); + } + + //local vars elem.textContent = ""; recipe = craft["recipe"]; mat_tiers = craft["mat_tiers"]; @@ -610,30 +615,33 @@ function displayRecipeStats(craft, parent_id) { } let effectiveness = craft["statMap"].get("ingredEffectiveness"); - let ldiv = document.createElement("div"); - ldiv.classList.add("itemleft"); - let title = document.createElement("p"); - title.classList.add("smalltitle"); + let title = document.createElement("div"); + title.classList.add("row", "box-title", "fw-bold", "justify-content-center"); title.textContent = "Recipe Stats"; - ldiv.appendChild(title); - let mats = document.createElement("p"); - mats.classList.add("itemp"); + elem.appendChild(title); + + let mats = document.createElement("div"); + mats.classList.add("row"); mats.textContent = "Crafting Materials: "; + elem.appendChild(mats); + for (let i = 0; i < 2; i++) { let tier = mat_tiers[i]; - let row = document.createElement("p"); - row.classList.add("left"); - let b = document.createElement("b"); + let row = document.createElement("div"); + row.classList.add("row", "px-0", "mx-0"); + let b = document.createElement("div"); let mat = recipe.get("materials")[i]; b.textContent = "- " + mat.get("amount") + "x " + mat.get("item").split(" ").slice(1).join(" "); - b.classList.add("space"); - let starsB = document.createElement("b"); - starsB.classList.add("T1-bracket"); - starsB.textContent = "["; + b.classList.add("col"); row.appendChild(b); + + let starsB = document.createElement("div"); + starsB.classList.add("T1-bracket", "col-auto", "px-0"); + starsB.textContent = "["; row.appendChild(starsB); for(let j = 0; j < 3; j ++) { - let star = document.createElement("b"); + let star = document.createElement("div"); + star.classList.add("col-auto", "px-0"); star.textContent = "\u272B"; if(j < tier) { star.classList.add("T1"); @@ -642,51 +650,57 @@ function displayRecipeStats(craft, parent_id) { } row.append(star); } - let starsE = document.createElement("b"); - starsE.classList.add("T1-bracket"); + let starsE = document.createElement("div"); + starsE.classList.add("T1-bracket", "col-auto", "px-0"); starsE.textContent = "]"; row.appendChild(starsE); - mats.appendChild(row); - } - ldiv.appendChild(mats); - let ingredTable = document.createElement("table"); - ingredTable.classList.add("itemtable"); - ingredTable.classList.add("ingredTable"); + elem.appendChild(row); + } + + let ingredTable = document.createElement("div"); + ingredTable.classList.add("row"); + for (let i = 0; i < 3; i++) { - let row = document.createElement("tr"); + let row = document.createElement("div"); + row.classList.add("row", "g-1", "justify-content-center"); + + for (let j = 0; j < 2; j++) { + if (j == 1) { + let spacer = document.createElement("div"); + spacer.classList.add("col-1"); + row.appendChild(spacer); + } let ingredName = ingreds[2 * i + j]; - let cell = document.createElement("td"); - cell.style.minWidth = "50%"; - cell.classList.add("center"); - cell.classList.add("box"); - cell.classList.add("tooltip"); - let b = document.createElement("b"); - b.textContent = ingredName; - b.classList.add("space"); - let eff = document.createElement("b"); + let col = document.createElement("div"); + col.classList.add("col-5", "rounded", "dark-6", "border", "border-3", "dark-shadow"); + + let temp_row = document.createElement("div"); + temp_row.classList.add("row"); + col.appendChild(temp_row); + + let ingred_div = document.createElement("div"); + ingred_div.classList.add("col"); + ingred_div.textContent = ingredName; + temp_row.appendChild(ingred_div); + + let eff_div = document.createElement("div"); + eff_div.classList.add("col-auto"); let e = effectiveness[2 * i + j]; if (e > 0) { - eff.classList.add("positive"); + eff_div.classList.add("positive"); } else if (e < 0) { - eff.classList.add("negative"); + eff_div.classList.add("negative"); } - eff.textContent = "[" + e + "%]"; - cell.appendChild(b); - cell.appendChild(eff); - row.appendChild(cell); + eff_div.textContent = "[" + e + "%]"; - let tooltip = document.createElement("div"); - tooltip.classList.add("tooltiptext"); - tooltip.classList.add("ing-tooltip"); - tooltip.classList.add("center"); - tooltip.id = "tooltip-" + (2*i + j); - cell.appendChild(tooltip); + temp_row.appendChild(eff_div); + + row.appendChild(col); } ingredTable.appendChild(row); } - elem.appendChild(ldiv); elem.appendChild(ingredTable); } @@ -696,23 +710,14 @@ function displayCraftStats(craft, parent_id) { displayExpandedItem(mock_item,parent_id); } -//Displays an ingredient in item format. However, an ingredient is too far from a normal item to display as one. +/* +* Displays an ingredient in item format. +* However, an ingredient is too far from a normal item to display as one. +*/ function displayExpandedIngredient(ingred, parent_id) { let parent_elem = document.getElementById(parent_id); parent_elem.textContent = ""; - let display_order = [ - "#cdiv", - "displayName", //tier will be displayed w/ name - "#table", - "ids", - "#ldiv", - "posMods", - "itemIDs", - "consumableIDs", - "#ldiv", - "lvl", - "skills", - ] + let item_order = [ "dura", "strReq", @@ -787,83 +792,85 @@ function displayExpandedIngredient(ingred, parent_id) { let active_elem; let elemental_format = false; let style; - for (const command of display_order) { - if (command.charAt(0) === "#") { - if (command === "#cdiv") { - active_elem = document.createElement('div'); - active_elem.classList.add('itemcenter'); + for (const command of sq2_ing_display_order) { + if (command.charAt(0) === "!") { + // TODO: This is sooo incredibly janky..... + if (command === "!elemental") { + elemental_format = !elemental_format; } - else if (command === "#ldiv") { - active_elem = document.createElement('div'); - active_elem.classList.add('itemleft'); + else if (command === "!spacer") { + let spacer = document.createElement('div'); + spacer.classList.add("row", "my-2"); + parent_elem.appendChild(spacer); + continue; } - else if (command === "#table") { - active_elem = document.createElement('table'); - active_elem.classList.add('itemtable'); - } - parent_elem.appendChild(active_elem); - }else { - let p_elem = document.createElement("p"); - p_elem.classList.add("left"); + } else { + let div = document.createElement("div"); + div.classList.add("row"); if (command === "displayName") { - p_elem.classList.add("title"); - p_elem.classList.remove("left"); - let title_elem = document.createElement("b"); + div.classList.add("box-title"); + let title_elem = document.createElement("div"); + title_elem.classList.add("col-auto", "justify-content-center", "pr-1"); title_elem.textContent = ingred.get("displayName"); - p_elem.appendChild(title_elem); - - let space = document.createElement("b"); - space.classList.add("space"); - p_elem.appendChild(space); + div.appendChild(title_elem); let tier = ingred.get("tier"); //tier in [0,3] let begin = document.createElement("b"); - begin.classList.add("T"+tier+"-bracket"); + begin.classList.add("T"+tier+"-bracket", "col-auto", "px-0"); begin.textContent = "["; - p_elem.appendChild(begin); + div.appendChild(begin); for (let i = 0; i < 3; i++) { let tier_elem = document.createElement("b"); - if(i < tier) {tier_elem.classList.add("T"+tier)} - else {tier_elem.classList.add("T0")} + if (i < tier) { + tier_elem.classList.add("T"+tier); + } else { + tier_elem.classList.add("T0"); + } + tier_elem.classList.add("px-0", "col-auto"); tier_elem.textContent = "\u272B"; - p_elem.appendChild(tier_elem); + div.appendChild(tier_elem); } let end = document.createElement("b"); - end.classList.add("T"+tier+"-bracket"); + end.classList.add("T"+tier+"-bracket", "px-0", "col-auto"); end.textContent = "]"; - p_elem.appendChild(end); + div.appendChild(end); }else if (command === "lvl") { - p_elem.textContent = "Crafting Lvl Min: " + ingred.get("lvl"); + div.textContent = "Crafting Lvl Min: " + ingred.get("lvl"); }else if (command === "posMods") { for (const [key,value] of ingred.get("posMods")) { - let p = document.createElement("p"); - p.classList.add("nomarginp"); + let posModRow = document.createElement("div"); + posModRow.classList.add("row"); if (value != 0) { - let title = document.createElement("b"); - title.textContent = posModPrefixes[key]; - let val = document.createElement("b"); + let posMod = document.createElement("div"); + posMod.classList.add("col-auto"); + posMod.textContent = posModPrefixes[key]; + posModRow.appendChild(posMod); + + let val = document.createElement("div"); + val.classList.add("col-auto", "px-0"); val.textContent = value + posModSuffixes[key]; if(value > 0) { val.classList.add("positive"); } else { val.classList.add("negative"); } - p.appendChild(title); - p.appendChild(val); - p_elem.appendChild(p); + posModRow.appendChild(val); + div.appendChild(posModRow); } } } else if (command === "itemIDs") { //dura, reqs for (const [key,value] of ingred.get("itemIDs")) { - let p = document.createElement("p"); - p.classList.add("nomarginp"); + let idRow = document.createElement("div"); + idRow.classList.add("row"); if (value != 0) { - let title = document.createElement("b"); + let title = document.createElement("div"); + title.classList.add("col-auto"); title.textContent = itemIDPrefixes[key]; - p.appendChild(title); + idRow.appendChild(title); } - let desc = document.createElement("b"); + let desc = document.createElement("div"); + desc.classList.add("col-auto"); if(value > 0) { if(key !== "dura") { desc.classList.add("negative"); @@ -880,20 +887,22 @@ function displayExpandedIngredient(ingred, parent_id) { desc.textContent = value; } if(value != 0){ - p.appendChild(desc); + idRow.appendChild(desc); } - p_elem.append(p); + div.appendChild(idRow); } } else if (command === "consumableIDs") { //dura, charges for (const [key,value] of ingred.get("consumableIDs")) { - let p = document.createElement("p"); - p.classList.add("nomarginp"); + let idRow = document.createElement("div"); + idRow.classList.add("row"); if (value != 0) { - let title = document.createElement("b"); + let title = document.createElement("div"); + title.classList.add("col-auto"); title.textContent = consumableIDPrefixes[key]; - p.appendChild(title); + idRow.appendChild(title); } - let desc = document.createElement("b"); + let desc = document.createElement("div"); + desc.classList.add("col-auto"); if(value > 0) { desc.classList.add("positive"); desc.textContent = "+"+value; @@ -902,32 +911,41 @@ function displayExpandedIngredient(ingred, parent_id) { desc.textContent = value; } if(value != 0){ - p.appendChild(desc); - let suffix = document.createElement("b"); + idRow.appendChild(desc); + let suffix = document.createElement("div"); + suffix.classList.add("col-auto"); suffix.textContent = consumableIDSuffixes[key]; - p.appendChild(suffix); + idRow.appendChild(suffix); } - p_elem.append(p); + div.appendChild(idRow); } }else if (command === "skills") { - p_elem.textContent = "Used in:"; + let row = document.createElement("div"); + row.classList.add("row"); + let title = document.createElement("div"); + title.classList.add("row"); + title.textContent = "Used in:"; + row.appendChild(title); for(const skill of ingred.get("skills")) { - let p = document.createElement("p"); - p.textContent = skill.charAt(0) + skill.substring(1).toLowerCase(); - p.classList.add("left"); - p_elem.append(p); + let skill_div = document.createElement("div"); + skill_div.classList.add("row"); + skill_div.textContent = skill.charAt(0) + skill.substring(1).toLowerCase(); + row.appendChild(skill_div); } + div.appendChild(row); } else if (command === "ids") { //warp for (let [key,value] of ingred.get("ids").get("maxRolls")) { if (value !== undefined && value != 0) { - let row = displayRolledID(ingred.get("ids"), key, false, "auto"); - active_elem.appendChild(row); + let row = displayRolledID(ingred.get("ids"), key, elemental_format); + row.classList.remove("col"); + row.classList.remove("col-12"); + div.appendChild(row); } } } else {//this shouldn't be happening } - active_elem.appendChild(p_elem); + parent_elem.appendChild(div); } } } @@ -1083,29 +1101,21 @@ function displayPoisonDamage(overallparent_elem, build) { overallparent_elem.append(overallpoisonDamage); } -function displayEquipOrder(parent_elem,buildOrder){ +function displayEquipOrder(parent_elem, buildOrder){ parent_elem.textContent = ""; const order = buildOrder.slice(); - let title_elem = document.createElement("p"); + let title_elem = document.createElement("b"); title_elem.textContent = "Equip order "; - title_elem.classList.add("title"); - title_elem.classList.add("Normal"); - title_elem.classList.add("itemp"); + title_elem.classList.add("Normal", "text-center"); parent_elem.append(title_elem); - parent_elem.append(document.createElement("br")); for (const item of order) { - let p_elem = document.createElement("p"); - p_elem.classList.add("itemp"); - p_elem.classList.add("left"); + let p_elem = document.createElement("b"); p_elem.textContent = item.get("displayName"); parent_elem.append(p_elem); } } - -function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ - console.log("Melee Stats"); - console.log(meleeStats); +function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { let tooltipinfo = meleeStats[13]; let attackSpeeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"]; //let damagePrefixes = ["Neutral Damage: ","Earth Damage: ","Thunder Damage: ","Water Damage: ","Fire Damage: ","Air Damage: "]; @@ -1123,7 +1133,7 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ stats[i][j] = stats[i][j].toFixed(2); } } - for (let i = 8; i < 11; ++i){ + for (let i = 8; i < 11; ++i) { stats[i] = stats[i].toFixed(2); } //tooltipelem, tooltiptext @@ -1132,24 +1142,18 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ //title let title_elem = document.createElement("p"); title_elem.classList.add("title"); - title_elem.classList.add("Normal"); - title_elem.classList.add("itemp"); title_elem.textContent = "Melee Stats"; parent_elem.append(title_elem); parent_elem.append(document.createElement("br")); //overall title - let title_elemavg = document.createElement("p"); - title_elemavg.classList.add("smalltitle"); - title_elemavg.classList.add("Normal"); + let title_elemavg = document.createElement("b"); title_elemavg.textContent = "Melee Stats"; overallparent_elem.append(title_elemavg); //average DPS let averageDamage = document.createElement("p"); averageDamage.classList.add("left"); - averageDamage.classList.add("itemp"); - averageDamage.classList.add("tooltip"); averageDamage.textContent = "Average DPS: " + stats[10]; tooltiptext = `= ((${stats[8]} * ${(stats[6][2]).toFixed(2)}) + (${stats[9]} * ${(stats[7][2]).toFixed(2)}))` tooltip = createTooltip(tooltip, "p", tooltiptext, averageDamage, ["melee-tooltip"]); @@ -1158,14 +1162,12 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ //overall average DPS let overallaverageDamage = document.createElement("p"); - overallaverageDamage.classList.add("itemp"); - let overallaverageDamageFirst = document.createElement("b"); + let overallaverageDamageFirst = document.createElement("span"); overallaverageDamageFirst.textContent = "Average DPS: " - let overallaverageDamageSecond = document.createElement("b"); + let overallaverageDamageSecond = document.createElement("span"); overallaverageDamageSecond.classList.add("Damage"); overallaverageDamageSecond.textContent = stats[10]; - tooltip = createTooltip(tooltip, "p", tooltiptext, overallaverageDamage, ["melee-tooltip", "summary-tooltip"]); overallaverageDamage.appendChild(overallaverageDamageFirst); overallaverageDamage.appendChild(overallaverageDamageSecond); @@ -1175,18 +1177,15 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ //attack speed let atkSpd = document.createElement("p"); atkSpd.classList.add("left"); - atkSpd.classList.add("itemp"); atkSpd.textContent = "Attack Speed: " + attackSpeeds[stats[11]]; parent_elem.append(atkSpd); parent_elem.append(document.createElement("br")); //overall attack speed let overallatkSpd = document.createElement("p"); - overallatkSpd.classList.add("center"); - overallatkSpd.classList.add("itemp"); - let overallatkSpdFirst = document.createElement("b"); + let overallatkSpdFirst = document.createElement("span"); overallatkSpdFirst.textContent = "Attack Speed: "; - let overallatkSpdSecond = document.createElement("b"); + let overallatkSpdSecond = document.createElement("span"); overallatkSpdSecond.classList.add("Damage"); overallatkSpdSecond.textContent = attackSpeeds[stats[11]]; overallatkSpd.appendChild(overallatkSpdFirst); @@ -1196,11 +1195,10 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ //Non-Crit: n->elem, total dmg, DPS let nonCritStats = document.createElement("p"); nonCritStats.classList.add("left"); - nonCritStats.classList.add("itemp"); nonCritStats.textContent = "Non-Crit Stats: "; nonCritStats.append(document.createElement("br")); - for (let i = 0; i < 6; i++){ - if(stats[i][1] != 0){ + for (let i = 0; i < 6; i++) { + if (stats[i][1] != 0) { let dmg = document.createElement("p"); dmg.textContent = stats[i][0] + " \u2013 " + stats[i][1]; dmg.classList.add(damageClasses[i]); @@ -1213,7 +1211,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ let normalDamage = document.createElement("p"); normalDamage.textContent = "Total: " + stats[6][0] + " \u2013 " + stats[6][1]; - normalDamage.classList.add("itemp"); let tooltiparr = ["Min: = ", "Max: = "] let arr = []; let arr2 = []; for (let i = 0; i < 6; i++) { @@ -1228,7 +1225,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ let normalDPS = document.createElement("p"); normalDPS.textContent = "Normal DPS: " + stats[8]; - normalDPS.classList.add("itemp"); normalDPS.classList.add("tooltip"); tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; tooltip = createTooltip(tooltip, "p", tooltiptext, normalDPS, ["melee-tooltip"]); @@ -1236,14 +1232,13 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ //overall average DPS let singleHitDamage = document.createElement("p"); - singleHitDamage.classList.add("itemp"); - let singleHitDamageFirst = document.createElement("b"); + let singleHitDamageFirst = document.createElement("span"); singleHitDamageFirst.textContent = "Single Hit Average: "; - let singleHitDamageSecond = document.createElement("b"); + let singleHitDamageSecond = document.createElement("span"); singleHitDamageSecond.classList.add("Damage"); singleHitDamageSecond.textContent = stats[12].toFixed(2); tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${stats[6][2].toFixed(2)} + ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${stats[7][2].toFixed(2)}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, singleHitDamage, ["melee-tooltip", "summary-tooltip"]); + // tooltip = createTooltip(tooltip, "p", tooltiptext, singleHitDamage, ["melee-tooltip", "summary-tooltip"]); singleHitDamage.appendChild(singleHitDamageFirst); singleHitDamage.appendChild(singleHitDamageSecond); @@ -1251,7 +1246,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ let normalChance = document.createElement("p"); normalChance.textContent = "Non-Crit Chance: " + (stats[6][2]*100).toFixed(2) + "%"; - normalChance.classList.add("itemp"); normalChance.append(document.createElement("br")); normalChance.append(document.createElement("br")); nonCritStats.append(normalChance); @@ -1262,7 +1256,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ //Crit: n->elem, total dmg, DPS let critStats = document.createElement("p"); critStats.classList.add("left"); - critStats.classList.add("itemp"); critStats.textContent = "Crit Stats: "; critStats.append(document.createElement("br")); for (let i = 0; i < 6; i++){ @@ -1278,7 +1271,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ } let critDamage = document.createElement("p"); critDamage.textContent = "Total: " + stats[7][0] + " \u2013 " + stats[7][1]; - critDamage.classList.add("itemp"); tooltiparr = ["Min: = ", "Max: = "] arr = []; arr2 = []; for (let i = 0; i < 6; i++) { @@ -1294,14 +1286,12 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){ let critDPS = document.createElement("p"); critDPS.textContent = "Crit DPS: " + stats[9]; - critDPS.classList.add("itemp"); tooltiptext = ` = ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; tooltip = createTooltip(tooltip, "p", tooltiptext, critDPS, ["melee-tooltip"]); critStats.append(critDPS); let critChance = document.createElement("p"); critChance.textContent = "Crit Chance: " + (stats[7][2]*100).toFixed(2) + "%"; - critChance.classList.add("itemp"); critChance.append(document.createElement("br")); critChance.append(document.createElement("br")); critStats.append(critChance); @@ -2249,6 +2239,7 @@ function stringPDF(id,val,base,amp) { document.getElementById(id + "-pdf").appendChild(b2); document.getElementById(id + "-pdf").appendChild(b3); } + function stringCDF(id,val,base,amp) { let p; let min; let max; let minr; let maxr; let minround; let maxround; if (base > 0) { diff --git a/js/utils.js b/js/utils.js index 164543d..5691ac9 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1,7 +1,8 @@ let getUrl = window.location; const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1]; -const zip = (a, b) => a.map((k, i) => [k, b[i]]); +const zip2 = (a, b) => a.map((k, i) => [k, b[i]]); +const zip3 = (a, b, c) => a.map((k, i) => [k, b[i], c[i]]); function clamp(num, low, high){ return Math.min(Math.max(num, low), high); From 7980980f9a806fbc89f3cabfb3663af77a792e6d Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 13:59:16 -0700 Subject: [PATCH 14/30] Build display working, still have to do edit ids / skillpoints --- builder/index.html | 1 - js/build.js | 10 +++--- js/build_constants.js | 1 + js/build_utils.js | 6 ++-- js/builder_graph.js | 6 ++++ js/display.js | 67 +++++++---------------------------------- js/display_constants.js | 63 ++++++++++++++++++++++++++++++-------- js/load_tome.js | 2 +- 8 files changed, 78 insertions(+), 78 deletions(-) diff --git a/builder/index.html b/builder/index.html index 73e20b1..283d301 100644 --- a/builder/index.html +++ b/builder/index.html @@ -1391,7 +1391,6 @@ - diff --git a/js/build.js b/js/build.js index 00300c1..f451ff8 100644 --- a/js/build.js +++ b/js/build.js @@ -221,19 +221,19 @@ class Build{ //EHP let ehp = [totalHp, totalHp]; let defMult = classDefenseMultipliers.get(this.weapon.statMap.get("type")); - ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); - ehp[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); + ehp[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); + ehp[1] /= (1-def_pct)*(2-defMult); defenseStats.push(ehp); //HPR let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); defenseStats.push(totalHpr); //EHPR let ehpr = [totalHpr, totalHpr]; - ehpr[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); - ehpr[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); + ehpr[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); + ehpr[1] /= (1-def_pct)*(2-defMult); defenseStats.push(ehpr); //skp stats - defenseStats.push([ (1 - ((1-def_pct) * (2 - this.defenseMultiplier)))*100, agi_pct*100]); + defenseStats.push([ def_pct*100, agi_pct*100]); //eledefs - TODO POWDERS let eledefs = [0, 0, 0, 0, 0]; for(const i in skp_elements){ //kinda jank but ok diff --git a/js/build_constants.js b/js/build_constants.js index 820beb7..eeaae5d 100644 --- a/js/build_constants.js +++ b/js/build_constants.js @@ -101,6 +101,7 @@ let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots']; let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace']; let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon']; let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon']; +let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1']; let spell_disp = ['spell0-info', 'spell1-info', 'spell2-info', 'spell3-info']; let other_disp = ['build-order', 'set-info', 'int-info']; diff --git a/js/build_utils.js b/js/build_utils.js index 4da3309..bbb2d89 100644 --- a/js/build_utils.js +++ b/js/build_utils.js @@ -51,16 +51,16 @@ const armorTypes = [ "helmet", "chestplate", "leggings", "boots" ]; const accessoryTypes = [ "ring", "bracelet", "necklace" ]; const weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ]; const consumableTypes = [ "potion", "scroll", "food"]; -const tomeTypes = ["armorTome", "weaponTome", "guildTome"]; //"dungeonTome", "gatheringTome", "slayingTome" +const tome_types = ['weaponTome', 'armorTome', 'guildTome']; const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"]; const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ]; //0.51, 0.82, 1.50, 2.05, 2.50, 3.11, 4.27 const classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"]; const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"] //I'm not sure why you would make a custom crafted but if you do you should be able to use it w/ the correct powder formula -const types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tomeTypes).map(x => x.substring(0,1).toUpperCase() + x.substring(1)); +const types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tome_types).map(x => x.substring(0,1).toUpperCase() + x.substring(1)); //weaponTypes.push("sword"); //console.log(types) -let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tomeTypes); +let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tome_types); let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ]; let skpReqs = skp_order.map(x => x + "Req"); diff --git a/js/builder_graph.js b/js/builder_graph.js index 083eb32..24c1d2d 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -383,6 +383,12 @@ class BuildDisplayNode extends ComputeNode { displayBuildStats('overall-stats', build, build_all_display_commands); displayBuildStats("offensive-stats", build, build_offensive_display_commands); displaySetBonuses("set-info", build); + let meleeStats = build.getMeleeStats(); + displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); + + displayDefenseStats(document.getElementById("defensive-stats"), build); + + displayPoisonDamage(document.getElementById("build-poison-stats"), build); } } diff --git a/js/display.js b/js/display.js index 76f85cf..3f02bd5 100644 --- a/js/display.js +++ b/js/display.js @@ -1116,7 +1116,6 @@ function displayEquipOrder(parent_elem, buildOrder){ } function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { - let tooltipinfo = meleeStats[13]; let attackSpeeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"]; //let damagePrefixes = ["Neutral Damage: ","Earth Damage: ","Thunder Damage: ","Water Damage: ","Fire Damage: ","Air Damage: "]; parent_elem.textContent = ""; @@ -1136,8 +1135,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { for (let i = 8; i < 11; ++i) { stats[i] = stats[i].toFixed(2); } - //tooltipelem, tooltiptext - let tooltip; let tooltiptext; //title let title_elem = document.createElement("p"); @@ -1155,9 +1152,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { let averageDamage = document.createElement("p"); averageDamage.classList.add("left"); averageDamage.textContent = "Average DPS: " + stats[10]; - tooltiptext = `= ((${stats[8]} * ${(stats[6][2]).toFixed(2)}) + (${stats[9]} * ${(stats[7][2]).toFixed(2)}))` - tooltip = createTooltip(tooltip, "p", tooltiptext, averageDamage, ["melee-tooltip"]); - averageDamage.appendChild(tooltip); parent_elem.append(averageDamage); //overall average DPS @@ -1203,31 +1197,16 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { dmg.textContent = stats[i][0] + " \u2013 " + stats[i][1]; dmg.classList.add(damageClasses[i]); dmg.classList.add("itemp"); - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(0,2).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, dmg, ["melee-tooltip"]); nonCritStats.append(dmg); } } let normalDamage = document.createElement("p"); normalDamage.textContent = "Total: " + stats[6][0] + " \u2013 " + stats[6][1]; - let tooltiparr = ["Min: = ", "Max: = "] - let arr = []; let arr2 = []; - for (let i = 0; i < 6; i++) { - if (stats[i][0] != 0) { - arr.push(stats[i][0]); - arr2.push(stats[i][1]); - } - } - tooltiptext = tooltiparr[0] + arr.join(" + ") + "\n" + tooltiparr[1] + arr2.join(" + "); - tooltip = createTooltip(tooltip, "p", tooltiptext, normalDamage, ["melee-tooltip"]); nonCritStats.append(normalDamage); let normalDPS = document.createElement("p"); normalDPS.textContent = "Normal DPS: " + stats[8]; - normalDPS.classList.add("tooltip"); - tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, normalDPS, ["melee-tooltip"]); nonCritStats.append(normalDPS); //overall average DPS @@ -1237,9 +1216,6 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { let singleHitDamageSecond = document.createElement("span"); singleHitDamageSecond.classList.add("Damage"); singleHitDamageSecond.textContent = stats[12].toFixed(2); - tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${stats[6][2].toFixed(2)} + ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${stats[7][2].toFixed(2)}`; - // tooltip = createTooltip(tooltip, "p", tooltiptext, singleHitDamage, ["melee-tooltip", "summary-tooltip"]); - singleHitDamage.appendChild(singleHitDamageFirst); singleHitDamage.appendChild(singleHitDamageSecond); overallparent_elem.append(singleHitDamage); @@ -1264,30 +1240,15 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { dmg.textContent = stats[i][2] + " \u2013 " + stats[i][3]; dmg.classList.add(damageClasses[i]); dmg.classList.add("itemp"); - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(2,4).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, dmg, ["melee-tooltip"]); critStats.append(dmg); } } let critDamage = document.createElement("p"); critDamage.textContent = "Total: " + stats[7][0] + " \u2013 " + stats[7][1]; - tooltiparr = ["Min: = ", "Max: = "] - arr = []; arr2 = []; - for (let i = 0; i < 6; i++) { - if (stats[i][0] != 0) { - arr.push(stats[i][2]); - arr2.push(stats[i][3]); - } - } - tooltiptext = tooltiparr[0] + arr.join(" + ") + "\n" + tooltiparr[1] + arr2.join(" + "); - tooltip = createTooltip(tooltip, "p", tooltiptext, critDamage, ["melee-tooltip"]); - critStats.append(critDamage); let critDPS = document.createElement("p"); critDPS.textContent = "Crit DPS: " + stats[9]; - tooltiptext = ` = ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, critDPS, ["melee-tooltip"]); critStats.append(critDPS); let critChance = document.createElement("p"); @@ -1343,8 +1304,6 @@ function displayDefenseStats(parent_elem, build, insertSummary){ statsTable.appendChild(hpRow); } - let tooltip; let tooltiptext; - let defMult = build.statMap.get("defMult"); if (!defMult) {defMult = 1} @@ -1360,9 +1319,6 @@ function displayDefenseStats(parent_elem, build, insertSummary){ boost.textContent = stats[1][0]; boost.classList.add("col"); boost.classList.add("text-end"); - tooltiptext = `= ${stats[0]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (1 - ${skillPointsToPercentage(build.total_skillpoints[4]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - ehpRow.appendChild(ehp); ehpRow.append(boost); @@ -1383,12 +1339,13 @@ function displayDefenseStats(parent_elem, build, insertSummary){ boost.textContent = stats[1][1]; boost.classList.add("col"); boost.classList.add("text-end"); - tooltiptext = `= ${stats[0]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - ehpRow.appendChild(ehp); ehpRow.append(boost); - statsTable.append(ehpRow); + if (insertSummary) { + parent_elem.appendChild(ehpRow) + } else { + statsTable.append(ehpRow); + } //total HPR let hprRow = document.createElement("div"); @@ -1424,12 +1381,14 @@ function displayDefenseStats(parent_elem, build, insertSummary){ boost.textContent = stats[3][0]; boost.classList.add("col"); boost.classList.add("text-end"); - tooltiptext = `= ${stats[2]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (1 - ${skillPointsToPercentage(build.total_skillpoints[4]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - ehprRow.appendChild(ehpr); ehprRow.append(boost); - statsTable.append(ehprRow); + + if (insertSummary) { + parent_elem.appendChild(ehprRow); + } else { + statsTable.appendChild(ehprRow); + } //eledefs let eledefs = stats[5]; @@ -1461,13 +1420,9 @@ function displayDefenseStats(parent_elem, build, insertSummary){ let defPct = build.statMap.get("defBonus")[i]/100; if (defRaw < 0) { defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct; - tooltiptext = `= min(0, ${defRaw} * (1 ${defPct}))` } else { defPct >= 0 ? defPct = "+ " + defPct: defPct = "- " + defPct; - tooltiptext = `= ${defRaw} * (1 ${defPct})` } - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - eledefElemRow.appendChild(boost); if (insertSummary) { diff --git a/js/display_constants.js b/js/display_constants.js index 93f77e6..4901e88 100644 --- a/js/display_constants.js +++ b/js/display_constants.js @@ -209,8 +209,8 @@ let posModSuffixes = { /* * Display commands */ -let build_overall_display_commands = [ - "#table", +let build_all_display_commands = [ + "#defense-stats", "str", "dex", "int", "def", "agi", "mr", "ms", "hprRaw", "hprPct", @@ -235,26 +235,51 @@ let build_overall_display_commands = [ "gXp", "gSpd", ]; -let item_display_commands = [ - "#cdiv", +let build_offensive_display_commands = [ + "str", "dex", "int", "def", "agi", + "mr", "ms", + "sdRaw", "sdPct", + "mdRaw", "mdPct", + "ref", "thorns", + "ls", + "poison", + "expd", + "spd", + "atkTier", + "rainbowRaw", + "!elemental", + "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", + "!elemental", + "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", +]; + +let build_basic_display_commands = [ + '#defense-stats', + // defense stats [hp, ehp, hpr, ] + // "sPot", // base * atkspd + spell raws + // melee potential + // "mPot", // melee% * (base * atkspd) + melee raws + "mr", "ms", + "ls", + "poison", + "spd", + "atkTier", +] + +let sq2_item_display_commands = [ "displayName", - //"type", //REPLACE THIS WITH SKIN - "#ldiv", "atkSpd", - "#ldiv", "!elemental", "hp", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", + "!spacer", "fDef", "wDef", "aDef", "tDef", "eDef", "!elemental", - "#ldiv", "classReq", "lvl", "strReq", "dexReq", "intReq", "defReq","agiReq", - "#ldiv", + "!spacer", "str", "dex", "int", "def", "agi", - "#table", - "str", "dex", "int", "def", "agi", //jank lmao "hpBonus", "hprRaw", "hprPct", "sdRaw", "sdPct", @@ -278,11 +303,25 @@ let item_display_commands = [ "spRegen", "eSteal", "gXp", "gSpd", - "#ldiv", "majorIds", + "!spacer", "slots", + "!spacer", "set", "lore", "quest", "restrict" ]; + +let sq2_ing_display_order = [ + "displayName", //tier will be displayed w/ name + "!spacer", + "ids", + "!spacer", + "posMods", + "itemIDs", + "consumableIDs", + "!spacer", + "lvl", + "skills", +] diff --git a/js/load_tome.js b/js/load_tome.js index 2186a60..62bb135 100644 --- a/js/load_tome.js +++ b/js/load_tome.js @@ -128,7 +128,7 @@ function init_tome_maps() { tomeIDMap = new Map(); tomeRedirectMap = new Map(); - for (const it of tomeTypes) { + for (const it of tome_types) { tomeLists.set(it, []); } From 4a54635e2d9b319dec82e05cad11d8c832469b4f Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 19:07:59 -0700 Subject: [PATCH 15/30] More cleanup --- js/builder_graph.js | 13 +++++++++ js/computation_graph.js | 2 +- js/display.js | 63 ++--------------------------------------- 3 files changed, 16 insertions(+), 62 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index 24c1d2d..663e14a 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -389,9 +389,17 @@ class BuildDisplayNode extends ComputeNode { displayDefenseStats(document.getElementById("defensive-stats"), build); displayPoisonDamage(document.getElementById("build-poison-stats"), build); + displayEquipOrder(document.getElementById("build-order"), build.equip_order); } } +/** + * Set the editble id fields. + */ +class EditableIDSetterNode extends ComputeNode { + +} + let item_nodes = []; let powder_nodes = []; let spelldmg_nodes = []; @@ -455,6 +463,11 @@ function builder_graph_init() { display_node.link_to(calc_node, 'spell-damage'); } + for (const input_node of item_nodes.concat(powder_nodes)) { + input_node.update(); + } + level_input.update(); + console.log("Set up graph"); } diff --git a/js/computation_graph.js b/js/computation_graph.js index 11113bd..68a1717 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -137,7 +137,7 @@ class InputNode extends ComputeNode { super(name); this.input_field = input_field; this.input_field.addEventListener("input", () => calcSchedule(this)); - calcSchedule(this); + //calcSchedule(this); Manually fire first update for better control } compute_func(input_map) { diff --git a/js/display.js b/js/display.js index 3f02bd5..c9ea548 100644 --- a/js/display.js +++ b/js/display.js @@ -35,15 +35,9 @@ function displaySetBonuses(parent_id,build) { let parent_div = document.getElementById(parent_id); let set_summary_elem = document.createElement('p'); - set_summary_elem.classList.add('itemcenter'); - set_summary_elem.textContent = "Set Bonuses:"; + set_summary_elem.classList.add('text-center'); + set_summary_elem.textContent = "Set Bonuses"; parent_div.append(set_summary_elem); - - if (build.activeSetCounts.size) { - parent_div.parentElement.style.display = "block"; - } else { - parent_div.parentElement.style.display = "none"; - } for (const [setName, count] of build.activeSetCounts) { const active_set = sets.get(setName); @@ -76,7 +70,6 @@ function displaySetBonuses(parent_id,build) { } } - function displayBuildStats(parent_id,build,command_group){ // Commands to "script" the creation of nice formatting. // #commands create a new element. @@ -1916,58 +1909,6 @@ function displayAdditionalInfo(elemID, item) { return; } -/** Displays all set bonuses (0/n, 1/n, ... n/n) for a given set - * - * @param {String} parent_id - id of the parent element - * @param {String} setName - the name of the set - */ - function displayAllSetBonuses(parent_id,setName) { - let parent_elem = document.getElementById(parent_id); - parent_elem.style.display = ""; - let set = sets[setName]; - let title_elem = document.createElement("p"); - title_elem.textContent = setName + " Set Bonuses"; - title_elem.classList.add("Set"); - title_elem.classList.add("title"); - parent_elem.appendChild(title_elem); - let grid_elem = document.createElement("div"); - grid_elem.style.display = "flex"; - grid_elem.style.flexDirection = "rows"; - grid_elem.style.flexWrap = "wrap"; - grid_elem.style.gap = "5px"; - parent_elem.appendChild(grid_elem); - - for (let i = 0; i < set.items.length; i++) { - - let set_elem = document.createElement('p'); - set_elem.classList.add("container"); - set_elem.style = "grid-item-"+(i+1); - set_elem.style.maxWidth = "max(180px, 15%)"; - set_elem.id = "set-"+setName+"-"+i; - grid_elem.appendChild(set_elem); - const bonus = set.bonuses[i]; - let mock_item = new Map(); - mock_item.set("fixID", true); - mock_item.set("displayName", setName+" Set: " + (i+1) + "/"+sets[setName].items.length); - set_elem.textContent = mock_item.get("displayName"); - let mock_minRolls = new Map(); - let mock_maxRolls = new Map(); - mock_item.set("minRolls", mock_minRolls); - mock_item.set("maxRolls", mock_maxRolls); - for (const id in bonus) { - if (rolledIDs.includes(id)) { - mock_minRolls.set(id, bonus[id]); - mock_maxRolls.set(id, bonus[id]); - } - else { - mock_item.set(id, bonus[id]); - } - } - mock_item.set("powders", []); - displayExpandedItem(mock_item, set_elem.id); - } - -} /** Displays the individual probabilities of each possible value of each rollable ID for this item. * From 4d7197fc53554482c58ba1fbc1c9af79ce212c34 Mon Sep 17 00:00:00 2001 From: hppeng Date: Sun, 19 Jun 2022 20:20:22 -0700 Subject: [PATCH 16/30] Add instructions --- js/builder_graph.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index 663e14a..fb2fc45 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -405,6 +405,8 @@ let powder_nodes = []; let spelldmg_nodes = []; function builder_graph_init() { + // Phase 1/2: Set up item input, propagate updates, etc. + // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) { let input_field = document.getElementById(eq+"-choice"); @@ -446,9 +448,28 @@ function builder_graph_init() { build_encode_node.link_to(powder_node, input); } + for (const input_node of item_nodes.concat(powder_nodes)) { + input_node.update(); + } + level_input.update(); + + // Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage + + // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) + // let stat_agg_node = + for (const field of editable_elems) { + // Create nodes that listens to each editable id input, the node name should match the "id" + + // stat_agg_node.link_to( ... ) + } + + // Also do something similar for skill points + for (let i = 0; i < 4; ++i) { let spell_node = new SpellSelectNode(i); spell_node.link_to(build_node, 'build'); + // link and rewrite spell_node to the stat agg node + // spell_node.link_to(stat_agg_node, 'stats') let calc_node = new SpellDamageCalcNode(i); calc_node.link_to(item_nodes[8], 'weapon-input'); @@ -462,11 +483,17 @@ function builder_graph_init() { display_node.link_to(spell_node, 'spell-info'); display_node.link_to(calc_node, 'spell-damage'); } + + // Create a node that binds the build to the edit ids text boxes + // (make it export to the textboxes) + // This node is a bit tricky since it has to make a "weak link" (directly mark dirty and call update() for each of the stat nodes). + // IMPORTANT: mark all children dirty, then update each child, in that order (2 loops). else performance issues will be bad + // let id_exporter_node = ... + // id_exporter_node.link_to(build_node, 'build') - for (const input_node of item_nodes.concat(powder_nodes)) { - input_node.update(); - } - level_input.update(); + // call node.update() for each skillpoint node and stat edit listener node manually + // NOTE: the text boxes for skill points are already filled out by decodeBuild() so this will fix them + // this will propagate the update to the `stat_agg_node`, and then to damage calc console.log("Set up graph"); } From 18d21e21ae5ddab8a2b0d89a84995d5d099f481c Mon Sep 17 00:00:00 2001 From: hppeng Date: Mon, 20 Jun 2022 06:12:22 -0700 Subject: [PATCH 17/30] Ready for dev branch merge --- js/build.js | 41 +---- js/build_encode_decode.js | 11 +- js/builder_graph.js | 347 ++++++++++++++++++++++++++++++++++---- js/computation_graph.js | 1 + js/custom.js | 1 + js/display.js | 24 ++- js/skillpoints.js | 1 - 7 files changed, 332 insertions(+), 94 deletions(-) diff --git a/js/build.js b/js/build.js index f451ff8..a655290 100644 --- a/js/build.js +++ b/js/build.js @@ -156,7 +156,8 @@ class Build{ } getBaseSpellCost(spellIdx, cost) { - // old intelligence: cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); + // old intelligence: + cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); cost += this.statMap.get("spRaw"+spellIdx); return Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100)); } @@ -206,44 +207,6 @@ class Build{ return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]); } - /* - Get all defensive stats for this build. - */ - getDefenseStats(){ - const stats = this.statMap; - let defenseStats = []; - let def_pct = skillPointsToPercentage(this.total_skillpoints[3]); - let agi_pct = skillPointsToPercentage(this.total_skillpoints[4]); - //total hp - let totalHp = stats.get("hp") + stats.get("hpBonus"); - if (totalHp < 5) totalHp = 5; - defenseStats.push(totalHp); - //EHP - let ehp = [totalHp, totalHp]; - let defMult = classDefenseMultipliers.get(this.weapon.statMap.get("type")); - ehp[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); - ehp[1] /= (1-def_pct)*(2-defMult); - defenseStats.push(ehp); - //HPR - let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); - defenseStats.push(totalHpr); - //EHPR - let ehpr = [totalHpr, totalHpr]; - ehpr[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); - ehpr[1] /= (1-def_pct)*(2-defMult); - defenseStats.push(ehpr); - //skp stats - defenseStats.push([ def_pct*100, agi_pct*100]); - //eledefs - TODO POWDERS - let eledefs = [0, 0, 0, 0, 0]; - for(const i in skp_elements){ //kinda jank but ok - eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.); - } - defenseStats.push(eledefs); - - //[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]] - return defenseStats; - } /* Get all stats for this build. Stores in this.statMap. @pre The build itself should be valid. No checking of validity of pieces is done here. diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index 12b45b1..834afdd 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -132,12 +132,16 @@ function decodeBuild(url_tag) { for (let i in powder_inputs) { setValue(powder_inputs[i], powdering[i]); } + for (let i in skillpoints) { + console.log(skillpoints[i]); + setValue(skp_order[i] + "-skp", skillpoints[i]); + } } } /* Stores the entire build in a string using B64 encoding and adds it to the URL. */ -function encodeBuild(build, powders) { +function encodeBuild(build, powders, skillpoints) { if (build) { let build_string; @@ -148,7 +152,6 @@ function encodeBuild(build, powders) { tome_string = ""; for (const item of build.items) { - if (item.statMap.get("custom")) { let custom = "CI-"+encodeCustom(item, true); build_string += Base64.fromIntN(custom.length, 3) + custom; @@ -167,8 +170,8 @@ function encodeBuild(build, powders) { } } - for (const skp of skp_order) { - build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 + for (const skp of skillpoints) { + build_string += Base64.fromIntN(skp, 2); // Maximum skillpoints: 2048 } build_string += Base64.fromIntN(build.level, 2); for (const _powderset of powders) { diff --git a/js/builder_graph.js b/js/builder_graph.js index fb2fc45..d1b7292 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -160,10 +160,17 @@ class BuildEncodeNode extends ComputeNode { input_map.get('boots-powder'), input_map.get('weapon-powder') ]; + const skillpoints = [ + input_map.get('str'), + input_map.get('dex'), + input_map.get('int'), + input_map.get('def'), + input_map.get('agi') + ]; // TODO: grr global state for copy button.. player_build = build; build_powders = powders; - return encodeBuild(build, powders); + return encodeBuild(build, powders, skillpoints); } } @@ -295,12 +302,50 @@ class SpellSelectNode extends ComputeNode { } } +/* + * Get all defensive stats for this build. + */ +function getDefenseStats(stats) { + let defenseStats = []; + let def_pct = skillPointsToPercentage(stats.get('def')); + let agi_pct = skillPointsToPercentage(stats.get('agi')); + //total hp + let totalHp = stats.get("hp") + stats.get("hpBonus"); + if (totalHp < 5) totalHp = 5; + defenseStats.push(totalHp); + //EHP + let ehp = [totalHp, totalHp]; + let defMult = stats.get("classDef"); + ehp[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); + ehp[1] /= (1-def_pct)*(2-defMult); + defenseStats.push(ehp); + //HPR + let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); + defenseStats.push(totalHpr); + //EHPR + let ehpr = [totalHpr, totalHpr]; + ehpr[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); + ehpr[1] /= (1-def_pct)*(2-defMult); + defenseStats.push(ehpr); + //skp stats + defenseStats.push([ def_pct*100, agi_pct*100]); + //eledefs - TODO POWDERS + let eledefs = [0, 0, 0, 0, 0]; + for(const i in skp_elements){ //kinda jank but ok + eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.); + } + defenseStats.push(eledefs); + + //[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]] + return defenseStats; +} + /** * Compute spell damage of spell parts. * Currently kinda janky / TODO while we rework the internal rep. of spells. * * Signature: SpellDamageCalcNode(weapon-input: Item, - * build: Build, + * stats: StatMap, * weapon-powder: List[powder], * spell-info: [Spell, SpellParts]) => List[SpellDamage] */ @@ -311,25 +356,31 @@ class SpellDamageCalcNode extends ComputeNode { compute_func(input_map) { const weapon = new Map(input_map.get('weapon-input').statMap); - const build = input_map.get('build'); const weapon_powder = input_map.get('weapon-powder'); const damage_mult = 1; // TODO: hook up const spell_info = input_map.get('spell-info'); const spell_parts = spell_info[1]; + const stats = input_map.get('stats'); + const skillpoints = [ + stats.get('str'), + stats.get('dex'), + stats.get('int'), + stats.get('def'), + stats.get('agi') + ]; weapon.set("powders", weapon_powder); let spell_results = [] - let stats = build.statMap; for (const part of spell_parts) { if (part.type === "damage") { let results = calculateSpellDamage(stats, part.conversion, stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct"), - part.multiplier / 100, weapon, build.total_skillpoints, damage_mult); + part.multiplier / 100, weapon, skillpoints, damage_mult); spell_results.push(results); } else if (part.type === "heal") { // TODO: wynn2 formula - let heal_amount = (part.strength * build.getDefenseStats()[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2); + let heal_amount = (part.strength * getDefenseStats(stats)[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2); spell_results.push(heal_amount); } else if (part.type === "total") { // TODO: remove "total" type @@ -345,7 +396,7 @@ class SpellDamageCalcNode extends ComputeNode { * Display spell damage from spell parts. * Currently kinda janky / TODO while we rework the internal rep. of spells. * - * Signature: SpellDisplayNode(build: Build, + * Signature: SpellDisplayNode(stats: StatMap, * spell-info: [Spell, SpellParts], * spell-damage: List[SpellDamage]) => null */ @@ -375,29 +426,231 @@ class SpellDisplayNode extends ComputeNode { * Signature: BuildDisplayNode(build: Build) => null */ class BuildDisplayNode extends ComputeNode { - constructor(spell_num) { super("builder-stats-display"); } + constructor() { super("builder-stats-display"); } compute_func(input_map) { - if (input_map.size !== 1) { throw "BuildDisplayNode accepts exactly one input (build)"; } - const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element - displayBuildStats('overall-stats', build, build_all_display_commands); - displayBuildStats("offensive-stats", build, build_offensive_display_commands); + const build = input_map.get('build'); + const stats = input_map.get('stats'); + displayBuildStats('overall-stats', build, build_all_display_commands, stats); + displayBuildStats("offensive-stats", build, build_offensive_display_commands, stats); displaySetBonuses("set-info", build); let meleeStats = build.getMeleeStats(); displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); - displayDefenseStats(document.getElementById("defensive-stats"), build); + displayDefenseStats(document.getElementById("defensive-stats"), stats); displayPoisonDamage(document.getElementById("build-poison-stats"), build); displayEquipOrder(document.getElementById("build-order"), build.equip_order); } } +/** + * Show warnings for skillpoints, level, set bonus for a build + * Also shosw skill point remaining and other misc. info + * + * Signature: DisplayBuildWarningNode(build: Build, str: int, dex: int, int: int, def: int, agi: int) => null + */ +class DisplayBuildWarningsNode extends ComputeNode { + constructor() { super("builder-show-warnings"); } + + compute_func(input_map) { + const build = input_map.get('build'); + const min_assigned = build.base_skillpoints; + const base_totals = build.total_skillpoints; + const skillpoints = [ + input_map.get('str'), + input_map.get('dex'), + input_map.get('int'), + input_map.get('def'), + input_map.get('agi') + ]; + let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."]; + let total_assigned = 0; + for (let i in skp_order){ //big bren + const assigned = skillpoints[i] - base_totals[i] + min_assigned[i] + setText(skp_order[i] + "-skp-assign", "Assign: " + assigned); + setValue(skp_order[i] + "-skp", skillpoints[i]); + let linebreak = document.createElement("br"); + linebreak.classList.add("itemp"); + setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i])); + document.getElementById(skp_order[i]+"-warnings").textContent = '' + if (assigned > 100) { + let skp_warning = document.createElement("p"); + skp_warning.classList.add("warning"); skp_warning.classList.add("small-text"); + skp_warning.textContent += "Cannot assign " + assigned + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually."; + document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning); + } + total_assigned += assigned; + } + + let summarybox = document.getElementById("summary-box"); + summarybox.textContent = ""; + let skpRow = document.createElement("p"); + + let remainingSkp = document.createElement("p"); + remainingSkp.classList.add("scaled-font"); + let remainingSkpTitle = document.createElement("b"); + remainingSkpTitle.textContent = "Assigned " + total_assigned + " skillpoints. Remaining skillpoints: "; + let remainingSkpContent = document.createElement("b"); + remainingSkpContent.textContent = "" + (levelToSkillPoints(build.level) - total_assigned); + remainingSkpContent.classList.add(levelToSkillPoints(build.level) - total_assigned < 0 ? "negative" : "positive"); + + remainingSkp.appendChild(remainingSkpTitle); + remainingSkp.appendChild(remainingSkpContent); + + summarybox.append(skpRow); + summarybox.append(remainingSkp); + if(total_assigned > levelToSkillPoints(build.level)){ + let skpWarning = document.createElement("span"); + //skpWarning.classList.add("itemp"); + skpWarning.classList.add("warning"); + skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!"; + let skpCount = document.createElement("p"); + skpCount.classList.add("warning"); + skpCount.textContent = "For level " + (build.level>101 ? "101+" : build.level) + ", there are only " + levelToSkillPoints(build.level) + " skill points available."; + summarybox.append(skpWarning); + summarybox.append(skpCount); + } + let lvlWarning; + for (const item of build.items) { + let item_lvl; + if (item.statMap.get("crafted")) { + //item_lvl = item.get("lvlLow") + "-" + item.get("lvl"); + item_lvl = item.statMap.get("lvlLow"); + } + else { + item_lvl = item.statMap.get("lvl"); + } + + if (build.level < item_lvl) { + if (!lvlWarning) { + lvlWarning = document.createElement("p"); + lvlWarning.classList.add("itemp"); + lvlWarning.classList.add("warning"); + lvlWarning.textContent = "WARNING: A level " + build.level + " player cannot use some piece(s) of this build." + } + let baditem = document.createElement("p"); + baditem.classList.add("nocolor"); + baditem.classList.add("itemp"); + baditem.textContent = item.get("displayName") + " requires level " + item_lvl + " to use."; + lvlWarning.appendChild(baditem); + } + } + if(lvlWarning){ + summarybox.append(lvlWarning); + } + for (const [setName, count] of build.activeSetCounts) { + const bonus = sets.get(setName).bonuses[count-1]; + // console.log(setName); + if (bonus["illegal"]) { + let setWarning = document.createElement("p"); + setWarning.classList.add("itemp"); + setWarning.classList.add("warning"); + setWarning.textContent = "WARNING: illegal item combination: " + setName + summarybox.append(setWarning); + } + } + } +} + +/** + * Aggregate stats from the build and from inputs. + * + * Signature: AggregateStatsNode(build: Build, *args) => StatMap + */ +class AggregateStatsNode extends ComputeNode { + constructor() { super("builder-aggregate-stats"); } + + compute_func(input_map) { + const build = input_map.get('build'); + const weapon = input_map.get('weapon'); + const output_stats = new Map(build.statMap); + for (const [k, v] of input_map.entries()) { + if (k === 'build') { + continue; + } + output_stats.set(k, v); + } + output_stats.set('classDef', classDefenseMultipliers.get(weapon.statMap.get("type"))); + return output_stats; + } +} + /** * Set the editble id fields. + * + * Signature: EditableIDSetterNode(build: Build) => null */ class EditableIDSetterNode extends ComputeNode { + constructor() { super("builder-id-setter"); } + compute_func(input_map) { + if (input_map.size !== 1) { throw "EditableIDSetterNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + for (const id of editable_item_fields) { + document.getElementById(id).value = build.statMap.get(id); + } + } +} + +/** + * Set skillpoint fields from build. + * This is separate because..... because of the way we work with edit ids vs skill points during the load sequence.... + * + * Signature: SkillPointSetterNode(build: Build) => null + */ +class SkillPointSetterNode extends ComputeNode { + constructor(notify_nodes) { + super("builder-skillpoint-setter"); + this.notify_nodes = notify_nodes; + } + + compute_func(input_map) { + console.log("mmm"); + if (input_map.size !== 1) { throw "SkillPointSetterNode accepts exactly one input (build)"; } + const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element + for (const [idx, elem] of skp_order.entries()) { + setText(elem + "-skp-base", "Original: " + build.base_skillpoints[idx]); + document.getElementById(elem+'-skp').value = build.total_skillpoints[idx]; + } + // NOTE: DO NOT merge these loops for performance reasons!!! + for (const node of this.notify_nodes) { + node.mark_dirty(); + } + for (const node of this.notify_nodes) { + node.update(); + } + } +} + +/** + * Get number (possibly summed) from a text input. + * + * Signature: SumNumberInputNode() => int + */ +class SumNumberInputNode extends InputNode { + compute_func(input_map) { + const value = this.input_field.value; + if (value === "") { value = 0; } + + let input_num = 0; + if (value.includes("+")) { + let skp = value.split("+"); + for (const s of skp) { + const val = parseInt(s,10); + if (isNaN(val)) { + return null; + } + input_num += val; + } + } else { + input_num = parseInt(value,10); + if (isNaN(input_num)) { + return null; + } + } + return input_num; + } } let item_nodes = []; @@ -433,7 +686,6 @@ function builder_graph_init() { build_node.link_to(input); } build_node.link_to(level_input); - new BuildDisplayNode().link_to(build_node, 'build'); let build_encode_node = new BuildEncodeNode(); build_encode_node.link_to(build_node, 'build'); @@ -448,48 +700,69 @@ function builder_graph_init() { build_encode_node.link_to(powder_node, input); } + // Edit IDs setter declared up here to set ids so they will be populated by default. + let edit_id_output = new EditableIDSetterNode(); + edit_id_output.link_to(build_node); + + // Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage + + let build_disp_node = new BuildDisplayNode() + build_disp_node.link_to(build_node, 'build'); + let build_warnings_node = new DisplayBuildWarningsNode(); + build_warnings_node.link_to(build_node, 'build'); + + // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) + let stat_agg_node = new AggregateStatsNode(); + stat_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon'); + let edit_input_nodes = []; + for (const field of editable_item_fields) { + // Create nodes that listens to each editable id input, the node name should match the "id" + const elem = document.getElementById(field); + const node = new SumNumberInputNode('builder-'+field+'-input', elem); + + stat_agg_node.link_to(node, field); + edit_input_nodes.push(node); + } + for (const skp of skp_order) { + const elem = document.getElementById(skp+'-skp'); + const node = new SumNumberInputNode('builder-'+skp+'-input', elem); + + stat_agg_node.link_to(node, skp); + build_encode_node.link_to(node, skp); + build_warnings_node.link_to(node, skp); + edit_input_nodes.push(node); + } + build_disp_node.link_to(stat_agg_node, 'stats'); + for (const input_node of item_nodes.concat(powder_nodes)) { input_node.update(); } level_input.update(); - // Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage - - // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) - // let stat_agg_node = - for (const field of editable_elems) { - // Create nodes that listens to each editable id input, the node name should match the "id" - - // stat_agg_node.link_to( ... ) - } - // Also do something similar for skill points for (let i = 0; i < 4; ++i) { let spell_node = new SpellSelectNode(i); spell_node.link_to(build_node, 'build'); - // link and rewrite spell_node to the stat agg node - // spell_node.link_to(stat_agg_node, 'stats') + // TODO: link and rewrite spell_node to the stat agg node + spell_node.link_to(stat_agg_node, 'stats') let calc_node = new SpellDamageCalcNode(i); - calc_node.link_to(item_nodes[8], 'weapon-input'); - calc_node.link_to(build_node, 'build'); - calc_node.link_to(powder_nodes[4], 'weapon-powder'); - calc_node.link_to(spell_node, 'spell-info'); + calc_node.link_to(item_nodes[8], 'weapon-input').link_to(stat_agg_node, 'stats') + .link_to(powder_nodes[4], 'weapon-powder').link_to(spell_node, 'spell-info'); spelldmg_nodes.push(calc_node); let display_node = new SpellDisplayNode(i); - display_node.link_to(build_node, 'build'); + display_node.link_to(build_node, 'build'); // TODO: same here.. display_node.link_to(spell_node, 'spell-info'); display_node.link_to(calc_node, 'spell-damage'); } + for (const node of edit_input_nodes) { + node.update(); + } - // Create a node that binds the build to the edit ids text boxes - // (make it export to the textboxes) - // This node is a bit tricky since it has to make a "weak link" (directly mark dirty and call update() for each of the stat nodes). - // IMPORTANT: mark all children dirty, then update each child, in that order (2 loops). else performance issues will be bad - // let id_exporter_node = ... - // id_exporter_node.link_to(build_node, 'build') + let skp_output = new SkillPointSetterNode(edit_input_nodes); + skp_output.link_to(build_node); // call node.update() for each skillpoint node and stat edit listener node manually // NOTE: the text boxes for skill points are already filled out by decodeBuild() so this will fix them diff --git a/js/computation_graph.js b/js/computation_graph.js index 68a1717..f99a966 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -94,6 +94,7 @@ class ComputeNode { this.inputs_dirty_count += 1; } parent_node.children.push(this); + return this; } } diff --git a/js/custom.js b/js/custom.js index 50ead5b..8c712af 100644 --- a/js/custom.js +++ b/js/custom.js @@ -175,6 +175,7 @@ function getCustomFromHash(hash) { } } statMap.set("hash", "CI-" + name); + statMap.set("custom", true); return new Custom(statMap); } } catch (error) { diff --git a/js/display.js b/js/display.js index c9ea548..30175c8 100644 --- a/js/display.js +++ b/js/display.js @@ -50,7 +50,7 @@ function displaySetBonuses(parent_id,build) { const bonus = active_set.bonuses[count-1]; let mock_item = new Map(); mock_item.set("fixID", true); - mock_item.set("displayName", setName+" Set: "+count+"/"+sets[setName].items.length); + mock_item.set("displayName", setName+" Set: "+count+"/"+sets.get(setName).items.length); let mock_minRolls = new Map(); let mock_maxRolls = new Map(); mock_item.set("minRolls", mock_minRolls); @@ -70,7 +70,7 @@ function displaySetBonuses(parent_id,build) { } } -function displayBuildStats(parent_id,build,command_group){ +function displayBuildStats(parent_id,build,command_group,stats){ // Commands to "script" the creation of nice formatting. // #commands create a new element. // !elemental is some janky hack for elemental damage. @@ -82,8 +82,6 @@ function displayBuildStats(parent_id,build,command_group){ if (parent_div != null) { setHTML(parent_id, ""); } - - let stats = build.statMap; let active_elem; let elemental_format = false; @@ -96,7 +94,7 @@ function displayBuildStats(parent_id,build,command_group){ if (command.charAt(0) === "#") { if (command === "#defense-stats") { - displayDefenseStats(parent_div, build, true); + displayDefenseStats(parent_div, stats, true); } } if (command.charAt(0) === "!") { @@ -126,7 +124,7 @@ function displayBuildStats(parent_id,build,command_group){ style === "positive" ? style = "negative" : style = "positive"; } if (id === "poison" && id_val > 0) { - id_val = Math.ceil(id_val*build.statMap.get("poisonPct")/100); + id_val = Math.ceil(id_val*stats.get("poisonPct")/100); } displayFixedID(parent_div, id, id_val, elemental_format, style); if (id === "poison" && id_val > 0) { @@ -140,7 +138,7 @@ function displayBuildStats(parent_id,build,command_group){ prefix_elem.textContent = "\u279C With Strength: "; let number_elem = document.createElement('b'); number_elem.classList.add(style); - number_elem.textContent = (id_val * (1+skillPointsToPercentage(build.total_skillpoints[0])) ).toFixed(0) + idSuffixes[id]; + number_elem.textContent = (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id]; value_elem.append(prefix_elem); value_elem.append(number_elem); row.appendChild(value_elem); @@ -156,7 +154,7 @@ function displayBuildStats(parent_id,build,command_group){ let prefix_elem = document.createElement('b'); prefix_elem.textContent = "\u279C Effective LS: "; - let defStats = build.getDefenseStats(); + let defStats = getDefenseStats(stats); let number_elem = document.createElement('b'); number_elem.classList.add(style); number_elem.textContent = Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s"; @@ -1253,8 +1251,8 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) { parent_elem.append(critStats); } -function displayDefenseStats(parent_elem, build, insertSummary){ - let defenseStats = build.getDefenseStats(); +function displayDefenseStats(parent_elem, statMap, insertSummary){ + let defenseStats = getDefenseStats(statMap); insertSummary = (typeof insertSummary !== 'undefined') ? insertSummary : false; if (!insertSummary) { parent_elem.textContent = ""; @@ -1297,7 +1295,7 @@ function displayDefenseStats(parent_elem, build, insertSummary){ statsTable.appendChild(hpRow); } - let defMult = build.statMap.get("defMult"); + let defMult = statMap.get("defMult"); if (!defMult) {defMult = 1} //EHP @@ -1409,8 +1407,8 @@ function displayDefenseStats(parent_elem, build, insertSummary){ boost.classList.add("col"); boost.classList.add("text-end"); - let defRaw = build.statMap.get("defRaw")[i]; - let defPct = build.statMap.get("defBonus")[i]/100; + let defRaw = statMap.get("defRaw")[i]; + let defPct = statMap.get("defBonus")[i]/100; if (defRaw < 0) { defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct; } else { diff --git a/js/skillpoints.js b/js/skillpoints.js index fcf24b6..5748a00 100644 --- a/js/skillpoints.js +++ b/js/skillpoints.js @@ -38,7 +38,6 @@ function calculate_skillpoints(equipment, weapon) { setCount = 0; activeSetCounts.set(setName, 1); } - console.log(sets); const new_bonus = sets.get(setName).bonuses[setCount]; //let skp_order = ["str","dex","int","def","agi"]; for (const i in skp_order) { From f7a1f4fc7e3be9276428e55ea07f4a7dcb369236 Mon Sep 17 00:00:00 2001 From: hppeng Date: Mon, 20 Jun 2022 07:37:14 -0700 Subject: [PATCH 18/30] Fix melee damage, fix edit ids spell calc --- builder/index.html | 54 ++++++++++++++++++------------------- js/build.js | 58 ---------------------------------------- js/builder_graph.js | 65 +++++++++++++++++++++++++++++++++++++++------ js/damage_calc.js | 2 +- js/display.js | 55 ++++++++++++++++++++------------------ js/optimize.js | 2 +- 6 files changed, 115 insertions(+), 121 deletions(-) diff --git a/builder/index.html b/builder/index.html index 283d301..2084f43 100644 --- a/builder/index.html +++ b/builder/index.html @@ -619,7 +619,7 @@ Spell Damage %:
- +
Original Value: 0 @@ -630,7 +630,7 @@ Spell Damage Raw:
- +
Original Value: 0 @@ -641,7 +641,7 @@ Melee Damage %:
- +
Original Value: 0 @@ -652,7 +652,7 @@ Melee Damage Raw:
- +
Original Value: 0 @@ -665,7 +665,7 @@ Poison:
- +
Original Value: 0 @@ -676,7 +676,7 @@ Damage %:
- +
Original Value: 0 @@ -687,7 +687,7 @@ Damage %:
- +
Original Value: 0 @@ -698,7 +698,7 @@ Damage %:
- +
Original Value: 0 @@ -711,7 +711,7 @@ Damage %:
- +
Original Value: 0 @@ -722,7 +722,7 @@ Damage %:
- +
Original Value: 0 @@ -733,7 +733,7 @@ + Tier:
- +
Original Value: 0 @@ -752,7 +752,7 @@ Defense %:
- +
Original Value: 0 @@ -763,7 +763,7 @@ Defense %:
- +
Original Value: 0 @@ -774,7 +774,7 @@ Defense %:
- +
Original Value: 0 @@ -785,7 +785,7 @@ Defense %:
- +
Original Value: 0 @@ -798,7 +798,7 @@ Defense %:
- +
Original Value: 0 @@ -809,7 +809,7 @@ Health Regen Raw:
- +
Original Value: 0 @@ -820,7 +820,7 @@ Health Regen %:
- +
Original Value: 0 @@ -831,7 +831,7 @@ Health Bonus:
- +
Original Value: 0 @@ -847,7 +847,7 @@ 1st Spell Cost %:
- +
Original Value: 0 @@ -858,7 +858,7 @@ 2nd Spell Cost %:
- +
Original Value: 0 @@ -869,7 +869,7 @@ 3rd Spell Cost %:
- +
Original Value: 0 @@ -880,7 +880,7 @@ 4th Spell Cost %:
- +
Original Value: 0 @@ -893,7 +893,7 @@ 1st Spell Cost Raw:
- +
Original Value: 0 @@ -904,7 +904,7 @@ 2nd Spell Cost Raw:
- +
Original Value: 0 @@ -915,7 +915,7 @@ 3rd Spell Cost Raw:
- +
Original Value: 0 @@ -926,7 +926,7 @@ 4th Spell Cost Raw:
- +
Original Value: 0 diff --git a/js/build.js b/js/build.js index a655290..78cf2ac 100644 --- a/js/build.js +++ b/js/build.js @@ -149,64 +149,6 @@ class Build{ return [this.equipment,this.weapon,this.tomes].flat(); } - /* Getters */ - - getSpellCost(spellIdx, cost) { - return Math.max(1, this.getBaseSpellCost(spellIdx, cost)); - } - - getBaseSpellCost(spellIdx, cost) { - // old intelligence: - cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); - cost += this.statMap.get("spRaw"+spellIdx); - return Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100)); - } - - - /* Get melee stats for build. - Returns an array in the order: - */ - getMeleeStats(){ - const stats = this.statMap; - const weapon_stats = this.weapon.statMap; - if (weapon_stats.get("tier") === "Crafted") { - stats.set("damageBases", [weapon_stats.get("nDamBaseHigh"),weapon_stats.get("eDamBaseHigh"),weapon_stats.get("tDamBaseHigh"),weapon_stats.get("wDamBaseHigh"),weapon_stats.get("fDamBaseHigh"),weapon_stats.get("aDamBaseHigh")]); - } - let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier"); - if(adjAtkSpd > 6){ - adjAtkSpd = 6; - }else if(adjAtkSpd < 0){ - adjAtkSpd = 0; - } - - let damage_mult = 1; - if (weapon_stats.get("type") === "relik") { - damage_mult = 0.99; // CURSE YOU WYNNCRAFT - //One day we will create WynnWynn and no longer have shaman 99% melee injustice. - //In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams. - } - // 0spellmult for melee damage. - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct"), 0, this.weapon.statMap, this.total_skillpoints, damage_mult * this.damageMultiplier); - - let dex = this.total_skillpoints[1]; - - let totalDamNorm = results[0]; - let totalDamCrit = results[1]; - totalDamNorm.push(1-skillPointsToPercentage(dex)); - totalDamCrit.push(skillPointsToPercentage(dex)); - let damages_results = results[2]; - - let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2]) - +(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2; - - //Now do math - let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd]; - let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd]; - let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex))); - //[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit] - return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]); - } - /* Get all stats for this build. Stores in this.statMap. @pre The build itself should be valid. No checking of validity of pieces is done here. diff --git a/js/builder_graph.js b/js/builder_graph.js index d1b7292..29ec4d6 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -260,7 +260,6 @@ class PowderInputNode extends InputNode { } input = input.slice(2); } - //console.log("POWDERING: " + powdering); return powdering; } } @@ -407,7 +406,7 @@ class SpellDisplayNode extends ComputeNode { } compute_func(input_map) { - const build = input_map.get('build'); + const stats = input_map.get('stats'); const spell_info = input_map.get('spell-info'); const damages = input_map.get('spell-damage'); const spell = spell_info[0]; @@ -416,10 +415,60 @@ class SpellDisplayNode extends ComputeNode { const i = this.spell_idx; let parent_elem = document.getElementById("spell"+i+"-info"); let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); - displaySpellDamage(parent_elem, overallparent_elem, build, spell, i+1, spell_parts, damages); + displaySpellDamage(parent_elem, overallparent_elem, stats, spell, i+1, spell_parts, damages); } } +/* Get melee stats for build. + Returns an array in the order: +*/ +function getMeleeStats(stats, weapon) { + const weapon_stats = weapon.statMap; + const skillpoints = [ + stats.get('str'), + stats.get('dex'), + stats.get('int'), + stats.get('def'), + stats.get('agi') + ]; + if (weapon_stats.get("tier") === "Crafted") { + stats.set("damageBases", [weapon_stats.get("nDamBaseHigh"),weapon_stats.get("eDamBaseHigh"),weapon_stats.get("tDamBaseHigh"),weapon_stats.get("wDamBaseHigh"),weapon_stats.get("fDamBaseHigh"),weapon_stats.get("aDamBaseHigh")]); + } + let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier"); + if(adjAtkSpd > 6){ + adjAtkSpd = 6; + }else if(adjAtkSpd < 0){ + adjAtkSpd = 0; + } + + let damage_mult = 1; + if (weapon_stats.get("type") === "relik") { + damage_mult = 0.99; // CURSE YOU WYNNCRAFT + //One day we will create WynnWynn and no longer have shaman 99% melee injustice. + //In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams. + } + // 0spellmult for melee damage. + let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct"), 0, weapon_stats, skillpoints, damage_mult); + + let dex = skillpoints[1]; + + let totalDamNorm = results[0]; + let totalDamCrit = results[1]; + totalDamNorm.push(1-skillPointsToPercentage(dex)); + totalDamCrit.push(skillPointsToPercentage(dex)); + let damages_results = results[2]; + + let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2]) + +(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2; + + //Now do math + let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd]; + let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd]; + let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex))); + //[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit] + return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]); +} + /** * Display build stats. * @@ -434,7 +483,9 @@ class BuildDisplayNode extends ComputeNode { displayBuildStats('overall-stats', build, build_all_display_commands, stats); displayBuildStats("offensive-stats", build, build_offensive_display_commands, stats); displaySetBonuses("set-info", build); - let meleeStats = build.getMeleeStats(); + let meleeStats = getMeleeStats(stats, build.weapon); + // TODO: move weapon out? + console.log(meleeStats); displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); displayDefenseStats(document.getElementById("defensive-stats"), stats); @@ -541,7 +592,6 @@ class DisplayBuildWarningsNode extends ComputeNode { } for (const [setName, count] of build.activeSetCounts) { const bonus = sets.get(setName).bonuses[count-1]; - // console.log(setName); if (bonus["illegal"]) { let setWarning = document.createElement("p"); setWarning.classList.add("itemp"); @@ -606,7 +656,6 @@ class SkillPointSetterNode extends ComputeNode { } compute_func(input_map) { - console.log("mmm"); if (input_map.size !== 1) { throw "SkillPointSetterNode accepts exactly one input (build)"; } const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element for (const [idx, elem] of skp_order.entries()) { @@ -656,6 +705,7 @@ class SumNumberInputNode extends InputNode { let item_nodes = []; let powder_nodes = []; let spelldmg_nodes = []; +let edit_input_nodes = []; function builder_graph_init() { // Phase 1/2: Set up item input, propagate updates, etc. @@ -714,7 +764,6 @@ function builder_graph_init() { // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) let stat_agg_node = new AggregateStatsNode(); stat_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon'); - let edit_input_nodes = []; for (const field of editable_item_fields) { // Create nodes that listens to each editable id input, the node name should match the "id" const elem = document.getElementById(field); @@ -753,7 +802,7 @@ function builder_graph_init() { spelldmg_nodes.push(calc_node); let display_node = new SpellDisplayNode(i); - display_node.link_to(build_node, 'build'); // TODO: same here.. + display_node.link_to(stat_agg_node, 'stats'); // TODO: same here.. display_node.link_to(spell_node, 'spell-info'); display_node.link_to(calc_node, 'spell-damage'); } diff --git a/js/damage_calc.js b/js/damage_calc.js index 9149a13..eef3825 100644 --- a/js/damage_calc.js +++ b/js/damage_calc.js @@ -105,7 +105,7 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, let staticBoost = (pctModifier / 100.); let skillBoost = [0]; for (let i in total_skillpoints) { - skillBoost.push(skillPointsToPercentage(total_skillpoints[i]) + buildStats.get("damageBonus")[i] / 100.); + skillBoost.push(skillPointsToPercentage(total_skillpoints[i]) + buildStats.get(skp_elements[i]+"DamPct") / 100.); } for (let i in damages) { diff --git a/js/display.js b/js/display.js index 30175c8..c87f689 100644 --- a/js/display.js +++ b/js/display.js @@ -941,46 +941,45 @@ function displayExpandedIngredient(ingred, parent_id) { } } -function displayNextCosts(spell, build, weapon) { - let int = build.total_skillpoints[2]; - let spells = spell_table[weapon.get("type")]; +function displayNextCosts(_stats, spell, spellIdx) { + let stats = new Map(_stats); + let intel = stats.get('int'); let row = document.createElement("div"); row.classList.add("spellcost-tooltip"); let init_cost = document.createElement("b"); - init_cost.textContent = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost); + init_cost.textContent = getSpellCost(stats, spellIdx, spell.cost); init_cost.classList.add("Mana"); let arrow = document.createElement("b"); arrow.textContent = "\u279C"; let next_cost = document.createElement("b"); - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1); + next_cost.textContent = (init_cost.textContent === "1" ? 1 : getSpellCost(stats, spellIdx, spell.cost) - 1); next_cost.classList.add("Mana"); let int_needed = document.createElement("b"); if (init_cost.textContent === "1") { int_needed.textContent = ": n/a (+0)"; }else { //do math - let target = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1; - let needed = int; + let target = getSpellCost(stats, spellIdx, spell.cost) - 1; + let needed = intel; let noUpdate = false; //forgive me... I couldn't inverse ceil, floor, and max. - while (build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) > target) { + while (getSpellCost(stats, spellIdx, spell.cost) > target) { if(needed > 150) { noUpdate = true; break; } needed++; - build.total_skillpoints[2] = needed; + stats.set('int', stats.get('int') + 1); } - let missing = needed - int; + let missing = needed - intel; //in rare circumstances, the next spell cost can jump. if (noUpdate) { - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)-1); + next_cost.textContent = (init_cost.textContent === "1" ? 1 : getSpellCost(stats, spellIdx, spell.cost)-1); }else { - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)); + next_cost.textContent = (init_cost.textContent === "1" ? 1 : getSpellCost(stats, spellIdx, spell.cost)); } - build.total_skillpoints[2] = int;//forgive me pt 2 int_needed.textContent = ": " + (needed > 150 ? ">150" : needed) + " int (+" + (needed > 150 ? "n/a" : missing) + ")"; } @@ -1583,12 +1582,23 @@ function displayPowderSpecials(parent_elem, powderSpecials, build) { } } -function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx, spell_parts, damages) { +function getSpellCost(stats, spellIdx, cost) { + return Math.max(1, getBaseSpellCost(stats, spellIdx, cost)); +} + +function getBaseSpellCost(stats, spellIdx, cost) { + // old intelligence: + cost = Math.ceil(cost * (1 - skillPointsToPercentage(stats.get('int')))); + cost += stats.get("spRaw"+spellIdx); + return Math.floor(cost * (1 + stats.get("spPct"+spellIdx) / 100)); +} + + +function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spellIdx, spell_parts, damages) { // TODO: remove spellIdx (just used to flag melee and cost) // TODO: move cost calc out parent_elem.textContent = ""; - const stats = build.statMap; let title_elem = document.createElement("p"); overallparent_elem.textContent = ""; @@ -1601,21 +1611,15 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell title_elemavg.appendChild(first); let second = document.createElement("span"); - second.textContent = build.getSpellCost(spellIdx, spell.cost); + second.textContent = getSpellCost(stats, spellIdx, spell.cost); second.classList.add("Mana"); - let int_redux = skillPointsToPercentage(build.total_skillpoints[2]).toFixed(2); - let spPct_redux = (build.statMap.get("spPct" + spellIdx)/100).toFixed(2); - let spRaw_redux = (build.statMap.get("spRaw" + spellIdx)).toFixed(2); - spPct_redux >= 0 ? spPct_redux = "+ " + spPct_redux : spPct_redux = "- " + Math.abs(spPct_redux); - spRaw_redux >= 0 ? spRaw_redux = "+ " + spRaw_redux : spRaw_redux = "- " + Math.abs(spRaw_redux); - title_elem.appendChild(second.cloneNode(true)); title_elemavg.appendChild(second); let third = document.createElement("span"); - third.textContent = ") [Base: " + build.getBaseSpellCost(spellIdx, spell.cost) + " ]"; + third.textContent = ") [Base: " + getBaseSpellCost(stats, spellIdx, spell.cost) + " ]"; title_elem.appendChild(third); let third_summary = document.createElement("span"); third_summary.textContent = ")"; @@ -1629,9 +1633,9 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell parent_elem.append(title_elem); overallparent_elem.append(title_elemavg); - overallparent_elem.append(displayNextCosts(spell, build, build.weapon.statMap)); + overallparent_elem.append(displayNextCosts(stats, spell, spellIdx)); - let critChance = skillPointsToPercentage(build.total_skillpoints[1]); + let critChance = skillPointsToPercentage(stats.get('dex')); let save_damages = []; @@ -1650,7 +1654,6 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell part_div.append(subtitle_elem); if (part.type === "damage") { - //console.log(build.expandedStats); let _results = damage; let totalDamNormal = _results[0]; let totalDamCrit = _results[1]; diff --git a/js/optimize.js b/js/optimize.js index 6185edd..fdce392 100644 --- a/js/optimize.js +++ b/js/optimize.js @@ -40,7 +40,7 @@ function optimizeStrDex() { for (const part of spell_parts) { if (part.type === "damage") { let _results = calculateSpellDamage(stats, part.conversion, - stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"), + stats.get("sdRaw"), stats.get("sdPct"), part.multiplier / 100, player_build.weapon, total_skillpoints, player_build.damageMultiplier, player_build.externalStats); let totalDamNormal = _results[0]; From ebcdf9ae271e91924983dfce558dd12c7efd9d50 Mon Sep 17 00:00:00 2001 From: hppeng Date: Mon, 20 Jun 2022 10:51:17 -0700 Subject: [PATCH 19/30] Powder specials and potion boosts --- builder/index.html | 72 +++++------ js/build_constants.js | 2 +- js/build_encode_decode.js | 6 +- js/builder.js | 47 +++++++- js/builder_graph.js | 244 +++++++++++++++++++++++++++++++------- js/damage_calc.js | 3 - js/display.js | 210 +++++++++++++++++--------------- js/load_tome.js | 1 - 8 files changed, 391 insertions(+), 194 deletions(-) diff --git a/builder/index.html b/builder/index.html index 2084f43..bc5c7d3 100644 --- a/builder/index.html +++ b/builder/index.html @@ -307,7 +307,7 @@
- +
@@ -944,27 +944,27 @@
-
-
-
-
-
@@ -1001,27 +1001,27 @@
-
-
-
-
-
@@ -1031,7 +1031,7 @@ Rage (Passive)
- +
@@ -1045,27 +1045,27 @@
-
-
-
-
-
@@ -1075,7 +1075,7 @@ Kill Streak (Passive)
- +
@@ -1089,27 +1089,27 @@
-
-
-
-
-
@@ -1119,7 +1119,7 @@ Concentration (Passive)
- +
@@ -1133,27 +1133,27 @@
-
-
-
-
-
@@ -1163,7 +1163,7 @@ Endurance (Passive)
- +
@@ -1177,27 +1177,27 @@
-
-
-
-
-
@@ -1207,7 +1207,7 @@ Dodge (Passive)
- +
diff --git a/js/build_constants.js b/js/build_constants.js index eeaae5d..edd88ac 100644 --- a/js/build_constants.js +++ b/js/build_constants.js @@ -84,7 +84,7 @@ let tome_names = [ "Armor Tome", "Guild Tome", ] -let equipmentInputs = equipment_fields.map(x => x + "-choice"); +let equipment_inputs = equipment_fields.map(x => x + "-choice"); let build_fields = equipment_fields.map(x => x+"-tooltip"); let tomeInputs = tome_fields.map(x => x + "-choice"); diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index 834afdd..2601830 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -8,7 +8,6 @@ function parsePowdering(powder_info) { powder_info = powder_info.slice(1); for (let j = 0; j < n_blocks; ++j) { let block = powder_info.slice(0,5); - console.log(block); let six_powders = Base64.toInt(block); for (let k = 0; k < 6 && six_powders != 0; ++k) { powders += powderNames.get((six_powders & 0x1f) - 1); @@ -84,7 +83,7 @@ function decodeBuild(url_tag) { } //constant in all versions for (let i in equipment) { - setValue(equipmentInputs[i], equipment[i]); + setValue(equipment_inputs[i], equipment[i]); } //level, skill point assignments, and powdering @@ -133,7 +132,6 @@ function decodeBuild(url_tag) { setValue(powder_inputs[i], powdering[i]); } for (let i in skillpoints) { - console.log(skillpoints[i]); setValue(skp_order[i] + "-skp", skillpoints[i]); } } @@ -147,7 +145,7 @@ function encodeBuild(build, powders, skillpoints) { let build_string; //V6 encoding - Tomes - build_version = 4; + build_version = 5; build_string = ""; tome_string = ""; diff --git a/js/builder.js b/js/builder.js index 0251fef..ec4f93f 100644 --- a/js/builder.js +++ b/js/builder.js @@ -69,20 +69,48 @@ function loadBuild() { } function resetFields(){ - for (let i in powderInputs) { - setValue(powderInputs[i], ""); + for (const i of powder_inputs) { + setValue(i, ""); } - for (let i in equipmentInputs) { - setValue(equipmentInputs[i], ""); + for (const i of equipment_inputs) { + setValue(i, ""); } setValue("str-skp", "0"); setValue("dex-skp", "0"); setValue("int-skp", "0"); setValue("def-skp", "0"); setValue("agi-skp", "0"); + for (const special_name of specialNames) { + for (let i = 1; i < 6; i++) { //toggle all pressed buttons of the same powder special off + //name is same, power is i + let elem = document.getElementById(special_name.replace(" ", "_")+'-'+i); + if (elem.classList.contains("toggleOn")) { + elem.classList.remove("toggleOn"); + } + } + } + for (const [key, value] of damageMultipliers) { + let elem = document.getElementById(key + "-boost") + if (elem.classList.contains("toggleOn")) { + elem.classList.remove("toggleOn"); + } + } + + const nodes_to_reset = item_nodes.concat(powder_nodes.concat(edit_input_nodes)); + for (const node of nodes_to_reset) { + node.mark_dirty(); + } + powder_special_input.mark_dirty(); + boosts_node.mark_dirty(); + + for (const node of nodes_to_reset) { + node.update(); + } + powder_special_input.update(); + boosts_node.update(); + setValue("level-choice", "106"); location.hash = ""; - calculateBuild(); } function toggleID() { @@ -130,6 +158,15 @@ function toggle_spell_tab(tab) { } } +function toggle_boost_tab(tab) { + for (const i of skp_order) { + document.querySelector("#"+i+"-boost").style.display = "none"; + document.getElementById(i + "-boost-tab").classList.remove("selected-btn"); + } + document.querySelector("#"+tab+"-boost").style.display = ""; + document.getElementById(tab + "-boost-tab").classList.add("selected-btn"); + +} let tabs = ['overall-stats', 'offensive-stats', 'defensive-stats']; function show_tab(tab) { diff --git a/js/builder_graph.js b/js/builder_graph.js index 29ec4d6..ae36651 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1,7 +1,136 @@ +let boosts_node; +/* Updates all spell boosts +*/ +function updateBoosts(buttonId) { + let elem = document.getElementById(buttonId); + if (elem.classList.contains("toggleOn")) { + elem.classList.remove("toggleOn"); + } else { + elem.classList.add("toggleOn"); + } + boosts_node.mark_dirty(); + boosts_node.update(); +} + +class BoostsInputNode extends ComputeNode { + constructor() { super('builder-boost-input'); } + + compute_func(input_map) { + let damage_boost = 0; + let def_boost = 0; + for (const [key, value] of damageMultipliers) { + let elem = document.getElementById(key + "-boost") + if (elem.classList.contains("toggleOn")) { + damage_boost += value; + if (key === "warscream") { def_boost += .20 } + if (key === "vanish") { def_boost += .15 } + } + } + return [damage_boost, def_boost]; + } +} + +let powder_special_input; +let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"]; + +function updatePowderSpecials(buttonId) { + //console.log(player_build.statMap); + + let name = (buttonId).split("-")[0]; + let power = (buttonId).split("-")[1]; // [1, 5] + + let elem = document.getElementById(buttonId); + if (elem.classList.contains("toggleOn")) { //toggle the pressed button off + elem.classList.remove("toggleOn"); + } else { + for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off + //name is same, power is i + if(document.getElementById(name.replace(" ", "_") + "-" + i).classList.contains("toggleOn")) { + document.getElementById(name.replace(" ", "_") + "-" + i).classList.remove("toggleOn"); + } + } + //toggle the pressed button on + elem.classList.add("toggleOn"); + } + + powder_special_input.mark_dirty(); + powder_special_input.update(); +} + +class PowderSpecialInputNode extends ComputeNode { + constructor() { super('builder-powder-special-input'); } + + compute_func(input_map) { + let powder_specials = []; // [ [special, power], [special, power]] + for (const sName of specialNames) { + for (let i = 1;i < 6; i++) { + if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) { + let powder_special = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))]; + powder_specials.push([powder_special, i]); + break; + } + } + } + return powder_specials; + } +} + +class PowderSpecialCalcNode extends ComputeNode { + constructor() { super('builder-powder-special-apply'); } + + compute_func(input_map) { + const powder_specials = input_map.get('powder-specials'); + let stats = new Map(); + for (const [special, power] of powder_specials) { + if (special["weaponSpecialEffects"].has("Damage Boost")) { + let name = special["weaponSpecialName"]; + if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost + stats.set("sdPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); + stats.set("mdPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); + stats.set("poisonPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); + } else if (name === "Wind Prison") { + stats.set("aDamPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); + } + } + } + return stats; + } +} + +class PowderSpecialDisplayNode extends ComputeNode { + constructor() { + super('builder-powder-special-display'); + this.fail_cb = true; + } + + compute_func(input_map) { + const powder_specials = input_map.get('powder-specials'); + const stats = input_map.get('stats'); + const weapon = input_map.get('weapon'); + displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap, true); + } +} + +/** + * Apply armor powders. + * Encoding shortcut assumes that all powders give +def to one element + * and -def to the element "behind" it in cycle ETWFA, which is true + * as of now and unlikely to change in the near future. + */ +function applyArmorPowders(expandedItem, powders) { + for(const id of powders){ + let powder = powderStats[id]; + let name = powderNames.get(id).charAt(0); + let prevName = skp_elements[(skp_elements.indexOf(name) + 4 )% 5]; + expandedItem.set(name+"Def", (expandedItem.get(name+"Def") || 0) + powder["defPlus"]); + expandedItem.set(prevName+"Def", (expandedItem.get(prevName+"Def") || 0) - powder["defMinus"]); + } +} + /** * Node for getting an item's stats from an item input field. * - * Signature: ItemInputNode() => Item | null + * Signature: ItemInputNode(powdering: Optional[list[powder]]) => Item | null */ class ItemInputNode extends InputNode { /** @@ -18,36 +147,36 @@ class ItemInputNode extends InputNode { } compute_func(input_map) { - // built on the assumption of no one will type in CI/CR letter by letter + const powdering = input_map.get('powdering'); + // built on the assumption of no one will type in CI/CR letter by letter let item_text = this.input_field.value; if (!item_text) { return this.none_item; } let item; - - if (item_text.slice(0, 3) == "CI-") { - item = getCustomFromHash(item_text); - } - else if (item_text.slice(0, 3) == "CR-") { - item = getCraftFromHash(item_text); - } - else if (itemMap.has(item_text)) { - item = new Item(itemMap.get(item_text)); - } - else if (tomeMap.has(item_text)) { - item = new Item(tomeMap.get(item_text)); - } + if (item_text.slice(0, 3) == "CI-") { item = getCustomFromHash(item_text); } + else if (item_text.slice(0, 3) == "CR-") { item = getCraftFromHash(item_text); } + else if (itemMap.has(item_text)) { item = new Item(itemMap.get(item_text)); } + else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); } if (item) { + if (powdering !== undefined) { + item.statMap.set('powders', powdering); + } let type_match; if (this.none_item.statMap.get('category') === 'weapon') { type_match = item.statMap.get('category') === 'weapon'; } else { type_match = item.statMap.get('type') === this.none_item.statMap.get('type'); } - if (type_match) { return item; } + if (type_match) { + if (item.statMap.get('category') === 'armor' && powdering !== undefined) { + applyArmorPowders(item.statMap, powdering); + } + return item; + } } return null; } @@ -142,11 +271,11 @@ class WeaponInputDisplayNode extends ComputeNode { * Encode the build into a url-able string. * * Signature: BuildEncodeNode(build: Build, - helmet-powder: List[powder], - chestplate-powder: List[powder], - leggings-powder: List[powder], - boots-powder: List[powder], - weapon-powder: List[powder]) => str + * helmet-powder: List[powder], + * chestplate-powder: List[powder], + * leggings-powder: List[powder], + * boots-powder: List[powder], + * weapon-powder: List[powder]) => str */ class BuildEncodeNode extends ComputeNode { constructor() { super("builder-encode"); } @@ -227,7 +356,7 @@ class BuildAssembleNode extends ComputeNode { for (const item of equipments) { all_none = all_none && item.statMap.has('NONE'); } - if (all_none) { + if (all_none && !location.hash) { return null; } return new Build(level, equipments, [], weapon); @@ -314,17 +443,17 @@ function getDefenseStats(stats) { defenseStats.push(totalHp); //EHP let ehp = [totalHp, totalHp]; - let defMult = stats.get("classDef"); - ehp[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); - ehp[1] /= (1-def_pct)*(2-defMult); + let defMult = (2 - stats.get("classDef")) * (1 - stats.get("defBonus")); + ehp[0] /= (1-def_pct)*(1-agi_pct)*defMult; + ehp[1] /= (1-def_pct)*defMult; defenseStats.push(ehp); //HPR let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); defenseStats.push(totalHpr); //EHPR let ehpr = [totalHpr, totalHpr]; - ehpr[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult); - ehpr[1] /= (1-def_pct)*(2-defMult); + ehpr[0] /= (1-def_pct)*(1-agi_pct)*defMult; + ehpr[1] /= (1-def_pct)*defMult; defenseStats.push(ehpr); //skp stats defenseStats.push([ def_pct*100, agi_pct*100]); @@ -345,7 +474,6 @@ function getDefenseStats(stats) { * * Signature: SpellDamageCalcNode(weapon-input: Item, * stats: StatMap, - * weapon-powder: List[powder], * spell-info: [Spell, SpellParts]) => List[SpellDamage] */ class SpellDamageCalcNode extends ComputeNode { @@ -355,11 +483,10 @@ class SpellDamageCalcNode extends ComputeNode { compute_func(input_map) { const weapon = new Map(input_map.get('weapon-input').statMap); - const weapon_powder = input_map.get('weapon-powder'); - const damage_mult = 1; // TODO: hook up const spell_info = input_map.get('spell-info'); const spell_parts = spell_info[1]; const stats = input_map.get('stats'); + const damage_mult = stats.get('damageMultiplier'); const skillpoints = [ stats.get('str'), stats.get('dex'), @@ -367,8 +494,6 @@ class SpellDamageCalcNode extends ComputeNode { stats.get('def'), stats.get('agi') ]; - - weapon.set("powders", weapon_powder); let spell_results = [] for (const part of spell_parts) { @@ -441,7 +566,7 @@ function getMeleeStats(stats, weapon) { adjAtkSpd = 0; } - let damage_mult = 1; + let damage_mult = stats.get("damageMultiplier"); if (weapon_stats.get("type") === "relik") { damage_mult = 0.99; // CURSE YOU WYNNCRAFT //One day we will create WynnWynn and no longer have shaman 99% melee injustice. @@ -485,7 +610,6 @@ class BuildDisplayNode extends ComputeNode { displaySetBonuses("set-info", build); let meleeStats = getMeleeStats(stats, build.weapon); // TODO: move weapon out? - console.log(meleeStats); displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); displayDefenseStats(document.getElementById("defensive-stats"), stats); @@ -576,13 +700,11 @@ class DisplayBuildWarningsNode extends ComputeNode { if (build.level < item_lvl) { if (!lvlWarning) { lvlWarning = document.createElement("p"); - lvlWarning.classList.add("itemp"); - lvlWarning.classList.add("warning"); + lvlWarning.classList.add("itemp"); lvlWarning.classList.add("warning"); lvlWarning.textContent = "WARNING: A level " + build.level + " player cannot use some piece(s) of this build." } let baditem = document.createElement("p"); - baditem.classList.add("nocolor"); - baditem.classList.add("itemp"); + baditem.classList.add("nocolor"); baditem.classList.add("itemp"); baditem.textContent = item.get("displayName") + " requires level " + item_lvl + " to use."; lvlWarning.appendChild(baditem); } @@ -594,8 +716,7 @@ class DisplayBuildWarningsNode extends ComputeNode { const bonus = sets.get(setName).bonuses[count-1]; if (bonus["illegal"]) { let setWarning = document.createElement("p"); - setWarning.classList.add("itemp"); - setWarning.classList.add("warning"); + setWarning.classList.add("itemp"); setWarning.classList.add("warning"); setWarning.textContent = "WARNING: illegal item combination: " + setName summarybox.append(setWarning); } @@ -612,15 +733,26 @@ class AggregateStatsNode extends ComputeNode { constructor() { super("builder-aggregate-stats"); } compute_func(input_map) { - const build = input_map.get('build'); - const weapon = input_map.get('weapon'); + const build = input_map.get('build'); input_map.delete('build'); + const powder_boost = input_map.get('powder-boost'); input_map.delete('powder-boost'); + const potion_boost = input_map.get('potion-boost'); input_map.delete('potion-boost'); + const weapon = input_map.get('weapon'); input_map.delete('weapon'); + const output_stats = new Map(build.statMap); + output_stats.set("damageMultiplier", 1 + potion_boost[0]); + output_stats.set("defBonus", potion_boost[1]); for (const [k, v] of input_map.entries()) { - if (k === 'build') { - continue; - } output_stats.set(k, v); } + for (const [k, v] of powder_boost.entries()) { + if (output_stats.has(k)) { + output_stats.set(k, v + output_stats.get(k)); + } + else { + output_stats.set(k, v); + } + } + output_stats.set('classDef', classDefenseMultipliers.get(weapon.statMap.get("type"))); return output_stats; } @@ -656,6 +788,7 @@ class SkillPointSetterNode extends ComputeNode { } compute_func(input_map) { + console.log("a"); if (input_map.size !== 1) { throw "SkillPointSetterNode accepts exactly one input (build)"; } const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element for (const [idx, elem] of skp_order.entries()) { @@ -750,6 +883,12 @@ function builder_graph_init() { build_encode_node.link_to(powder_node, input); } + item_nodes[0].link_to(powder_nodes[0], 'powdering'); + item_nodes[1].link_to(powder_nodes[1], 'powdering'); + item_nodes[2].link_to(powder_nodes[2], 'powdering'); + item_nodes[3].link_to(powder_nodes[3], 'powdering'); + item_nodes[8].link_to(powder_nodes[4], 'powdering'); + // Edit IDs setter declared up here to set ids so they will be populated by default. let edit_id_output = new EditableIDSetterNode(); edit_id_output.link_to(build_node); @@ -788,6 +927,19 @@ function builder_graph_init() { } level_input.update(); + // Powder specials. + powder_special_input = new PowderSpecialInputNode(); + let powder_special_calc = new PowderSpecialCalcNode().link_to(powder_special_input, 'powder-specials'); + new PowderSpecialDisplayNode().link_to(powder_special_input, 'powder-specials') + .link_to(stat_agg_node, 'stats').link_to(item_nodes[8], 'weapon'); + stat_agg_node.link_to(powder_special_calc, 'powder-boost'); + powder_special_input.update(); + + // Potion boost. + boosts_node = new BoostsInputNode(); + stat_agg_node.link_to(boosts_node, 'potion-boost'); + boosts_node.update(); + // Also do something similar for skill points for (let i = 0; i < 4; ++i) { @@ -798,7 +950,7 @@ function builder_graph_init() { let calc_node = new SpellDamageCalcNode(i); calc_node.link_to(item_nodes[8], 'weapon-input').link_to(stat_agg_node, 'stats') - .link_to(powder_nodes[4], 'weapon-powder').link_to(spell_node, 'spell-info'); + .link_to(spell_node, 'spell-info'); spelldmg_nodes.push(calc_node); let display_node = new SpellDisplayNode(i); diff --git a/js/damage_calc.js b/js/damage_calc.js index eef3825..490713f 100644 --- a/js/damage_calc.js +++ b/js/damage_calc.js @@ -72,9 +72,6 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, damages[element+1][1] += powder.max; } - - //console.log(tooltipinfo); - damages[0] = neutralRemainingRaw; let damageMult = damageMultiplier; diff --git a/js/display.js b/js/display.js index c87f689..61c57f0 100644 --- a/js/display.js +++ b/js/display.js @@ -1,18 +1,3 @@ -/** - * Apply armor powders. - * Encoding shortcut assumes that all powders give +def to one element - * and -def to the element "behind" it in cycle ETWFA, which is true - * as of now and unlikely to change in the near future. - */ -function applyArmorPowders(expandedItem, powders) { - for(const id of powders){ - let powder = powderStats[id]; - let name = powderNames.get(id).charAt(0); - let prevName = skp_elements[(skp_elements.indexOf(name) + 4 )% 5]; - expandedItem.set(name+"Def", (expandedItem.get(name+"Def") || 0) + powder["defPlus"]); - expandedItem.set(prevName+"Def", (expandedItem.get(prevName+"Def") || 0) - powder["defMinus"]); - } -} function apply_elemental_format(p_elem, id, suffix) { suffix = (typeof suffix !== 'undefined') ? suffix : ""; @@ -192,6 +177,11 @@ function displayExpandedItem(item, parent_id){ if (item.get("category") === "weapon") { let stats = new Map(); stats.set("atkSpd", item.get("atkSpd")); + stats.set("eDamPct", 0); + stats.set("tDamPct", 0); + stats.set("wDamPct", 0); + stats.set("fDamPct", 0); + stats.set("aDamPct", 0); stats.set("damageBonus", [0, 0, 0, 0, 0]); //SUPER JANK @HPP PLS FIX @@ -991,9 +981,15 @@ function displayNextCosts(_stats, spell, spellIdx) { } function displayRolledID(item, id, elemental_format) { - let row = document.createElement('tr'); - let min_elem = document.createElement('td'); - min_elem.classList.add('left'); + let row = document.createElement('div'); + row.classList.add('col'); + + let item_div = document.createElement('div'); + item_div.classList.add('row'); + + let min_elem = document.createElement('div'); + min_elem.classList.add('col', 'text-start'); + min_elem.style.cssText += "flex-grow: 0"; let id_min = item.get("minRolls").get(id) let style = id_min < 0 ? "negative" : "positive"; if(reversedIDs.includes(id)){ @@ -1001,10 +997,11 @@ function displayRolledID(item, id, elemental_format) { } min_elem.classList.add(style); min_elem.textContent = id_min + idSuffixes[id]; - row.appendChild(min_elem); + item_div.appendChild(min_elem); - let desc_elem = document.createElement('td'); - desc_elem.classList.add('center'); + let desc_elem = document.createElement('div'); + desc_elem.classList.add('col', 'text-center');//, 'text-nowrap'); + desc_elem.style.cssText += "flex-grow: 1"; //TODO elemental format jank if (elemental_format) { apply_elemental_format(desc_elem, id); @@ -1012,18 +1009,20 @@ function displayRolledID(item, id, elemental_format) { else { desc_elem.textContent = idPrefixes[id]; } - row.appendChild(desc_elem); + item_div.appendChild(desc_elem); - let max_elem = document.createElement('td'); + let max_elem = document.createElement('div'); let id_max = item.get("maxRolls").get(id) - max_elem.classList.add('right'); + max_elem.classList.add('col', 'text-end'); + max_elem.style.cssText += "flex-grow: 0"; style = id_max < 0 ? "negative" : "positive"; - if(reversedIDs.includes(id)){ + if (reversedIDs.includes(id)) { style === "positive" ? style = "negative" : style = "positive"; } max_elem.classList.add(style); max_elem.textContent = id_max + idSuffixes[id]; - row.appendChild(max_elem); + item_div.appendChild(max_elem); + row.appendChild(item_div); return row; } @@ -1458,55 +1457,60 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){ } } -function displayPowderSpecials(parent_elem, powderSpecials, build) { - parent_elem.textContent = "Powder Specials"; +function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overall=false) { + const skillpoints = [ + stats.get('str'), + stats.get('dex'), + stats.get('int'), + stats.get('def'), + stats.get('agi') + ]; + parent_elem.textContent = "" + let title = document.createElement("b"); + title.textContent = "Powder Specials"; + parent_elem.appendChild(title); let specials = powderSpecials.slice(); - let stats = build.statMap; let expandedStats = new Map(); //each entry of powderSpecials is [ps, power] for (special of specials) { //iterate through the special and display its effects. let powder_special = document.createElement("p"); - powder_special.classList.add("left"); let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); let specialTitle = document.createElement("p"); let specialEffects = document.createElement("p"); - specialTitle.classList.add("left"); - specialTitle.classList.add("itemp"); specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]); - specialEffects.classList.add("left"); - specialEffects.classList.add("itemp"); - specialEffects.classList.add("nocolor"); let effects = special[0]["weaponSpecialEffects"]; let power = special[1]; specialTitle.textContent = special[0]["weaponSpecialName"] + " " + Math.floor((power-1)*0.5 + 4) + (power % 2 == 0 ? ".5" : ""); - for (const [key,value] of effects) { - let effect = document.createElement("p"); - effect.classList.add("itemp"); - effect.textContent += key + ": " + value[power-1] + specialSuffixes.get(key); - if(key === "Damage"){ - effect.textContent += elementIcons[powderSpecialStats.indexOf(special[0])]; - } - if(special[0]["weaponSpecialName"] === "Wind Prison" && key === "Damage Boost") { - effect.textContent += " (only 1st hit)"; - } - specialEffects.appendChild(effect); - } + if (!overall || powderSpecialStats.indexOf(special[0]) == 2 || powderSpecialStats.indexOf(special[0]) == 3 || powderSpecialStats.indexOf(special[0]) == 4) { + for (const [key,value] of effects) { + let effect = document.createElement("p"); + effect.textContent += key + ": " + value[power-1] + specialSuffixes.get(key); + if(key === "Damage"){ + effect.textContent += elementIcons[powderSpecialStats.indexOf(special[0])]; + } + if(special[0]["weaponSpecialName"] === "Wind Prison" && key === "Damage Boost") { + effect.textContent += " (only 1st hit)"; + } + specialEffects.appendChild(effect); + } + } powder_special.appendChild(specialTitle); powder_special.appendChild(specialEffects); //if this special is an instant-damage special (Quake, Chain Lightning, Courage Burst), display the damage. let specialDamage = document.createElement("p"); + // specialDamage.classList.add("item-margin"); let spells = spell_table["powder"]; if (powderSpecialStats.indexOf(special[0]) == 0 || powderSpecialStats.indexOf(special[0]) == 1 || powderSpecialStats.indexOf(special[0]) == 3) { //Quake, Chain Lightning, or Courage let spell = (powderSpecialStats.indexOf(special[0]) == 3 ? spells[2] : spells[powderSpecialStats.indexOf(special[0])]); let part = spell["parts"][0]; let _results = calculateSpellDamage(stats, part.conversion, - stats.get("mdRaw"), stats.get("mdPct") + build.externalStats.get("mdPct"), - 0, build.weapon, build.total_skillpoints, build.damageMultiplier * ((part.multiplier[power-1] / 100)), build.externalStats);//part.multiplier[power] / 100 + stats.get("mdRaw"), stats.get("mdPct"), + 0, weapon, skillpoints, stats.get('damageMultiplier') * ((part.multiplier[power-1] / 100)));//part.multiplier[power] / 100 - let critChance = skillPointsToPercentage(build.total_skillpoints[1]); + let critChance = skillPointsToPercentage(skillpoints[1]); let save_damages = []; let totalDamNormal = _results[0]; @@ -1521,59 +1525,69 @@ function displayPowderSpecials(parent_elem, powderSpecials, build) { let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; - let averageLabel = document.createElement("p"); - averageLabel.textContent = "Average: "+averageDamage.toFixed(2); - averageLabel.classList.add("damageSubtitle"); - specialDamage.append(averageLabel); - - - let nonCritLabel = document.createElement("p"); - nonCritLabel.textContent = "Non-Crit Average: "+nonCritAverage.toFixed(2); - nonCritLabel.classList.add("damageSubtitle"); - specialDamage.append(nonCritLabel); + let averageWrap = document.createElement("p"); + let averageLabel = document.createElement("span"); + averageLabel.textContent = "Average: "; - for (let i = 0; i < 6; i++){ - if (results[i][1] > 0){ - let p = document.createElement("p"); - p.classList.add("damagep"); - p.classList.add(damageClasses[i]); - p.textContent = results[i][0]+"-"+results[i][1]; - specialDamage.append(p); - } - } - let normalDamage = document.createElement("p"); - normalDamage.textContent = "Total: " + totalDamNormal[0].toFixed(2) + "-" + totalDamNormal[1].toFixed(2); - normalDamage.classList.add("itemp"); - specialDamage.append(normalDamage); + let averageLabelDmg = document.createElement("span"); + averageLabelDmg.classList.add("Damage"); + averageLabelDmg.textContent = averageDamage.toFixed(2); - let nonCritChanceLabel = document.createElement("p"); - nonCritChanceLabel.textContent = "Non-Crit Chance: " + ((1-critChance)*100).toFixed(2) + "%"; - specialDamage.append(nonCritChanceLabel); - - let critLabel = document.createElement("p"); - critLabel.textContent = "Crit Average: "+critAverage.toFixed(2); - critLabel.classList.add("damageSubtitle"); + averageWrap.appendChild(averageLabel); + averageWrap.appendChild(averageLabelDmg); + specialDamage.appendChild(averageWrap); - specialDamage.append(critLabel); - for (let i = 0; i < 6; i++){ - if (results[i][1] > 0){ - let p = document.createElement("p"); - p.classList.add("damagep"); - p.classList.add(damageClasses[i]); - p.textContent = results[i][2]+"-"+results[i][3]; - specialDamage.append(p); + if (!overall) { + let nonCritLabel = document.createElement("p"); + nonCritLabel.textContent = "Non-Crit Average: "+nonCritAverage.toFixed(2); + nonCritLabel.classList.add("damageSubtitle"); + nonCritLabel.classList.add("item-margin"); + specialDamage.append(nonCritLabel); + + for (let i = 0; i < 6; i++){ + if (results[i][1] > 0){ + let p = document.createElement("p"); + p.classList.add("damagep"); + p.classList.add(damageClasses[i]); + p.textContent = results[i][0]+"-"+results[i][1]; + specialDamage.append(p); + } } + let normalDamage = document.createElement("p"); + normalDamage.textContent = "Total: " + totalDamNormal[0].toFixed(2) + "-" + totalDamNormal[1].toFixed(2); + normalDamage.classList.add("itemp"); + specialDamage.append(normalDamage); + + let nonCritChanceLabel = document.createElement("p"); + nonCritChanceLabel.textContent = "Non-Crit Chance: " + ((1-critChance)*100).toFixed(2) + "%"; + specialDamage.append(nonCritChanceLabel); + + let critLabel = document.createElement("p"); + critLabel.textContent = "Crit Average: "+critAverage.toFixed(2); + critLabel.classList.add("damageSubtitle"); + critLabel.classList.add("item-margin"); + + specialDamage.append(critLabel); + for (let i = 0; i < 6; i++){ + if (results[i][1] > 0){ + let p = document.createElement("p"); + p.classList.add("damagep"); + p.classList.add(damageClasses[i]); + p.textContent = results[i][2]+"-"+results[i][3]; + specialDamage.append(p); + } + } + let critDamage = document.createElement("p"); + critDamage.textContent = "Total: " + totalDamCrit[0].toFixed(2) + "-" + totalDamCrit[1].toFixed(2); + critDamage.classList.add("itemp"); + specialDamage.append(critDamage); + + let critChanceLabel = document.createElement("p"); + critChanceLabel.textContent = "Crit Chance: " + (critChance*100).toFixed(2) + "%"; + specialDamage.append(critChanceLabel); + + save_damages.push(averageDamage); } - let critDamage = document.createElement("p"); - critDamage.textContent = "Total: " + totalDamCrit[0].toFixed(2) + "-" + totalDamCrit[1].toFixed(2); - critDamage.classList.add("itemp"); - specialDamage.append(critDamage); - - let critChanceLabel = document.createElement("p"); - critChanceLabel.textContent = "Crit Chance: " + (critChance*100).toFixed(2) + "%"; - specialDamage.append(critChanceLabel); - - save_damages.push(averageDamage); powder_special.append(specialDamage); } diff --git a/js/load_tome.js b/js/load_tome.js index 62bb135..e6fded5 100644 --- a/js/load_tome.js +++ b/js/load_tome.js @@ -25,7 +25,6 @@ async function load_tome_local() { console.log("Successfully read local tome db."); } get_tx.oncomplete = function(event) { - console.log('b'); console.log(request.readyState); tomes = request.result; init_tome_maps(); From d1a468a227a60909fc6da3699850bcc05ac27f2b Mon Sep 17 00:00:00 2001 From: hppeng Date: Mon, 20 Jun 2022 10:52:46 -0700 Subject: [PATCH 20/30] Remove unused files (should be merged into builder.js... etc now.) --- js/sq2bs.js | 522 -------- js/sq2build.js | 692 ----------- js/sq2builder.js | 1127 ----------------- js/sq2display.js | 2407 ------------------------------------ js/sq2display_constants.js | 191 --- 5 files changed, 4939 deletions(-) delete mode 100644 js/sq2bs.js delete mode 100644 js/sq2build.js delete mode 100644 js/sq2builder.js delete mode 100644 js/sq2display.js delete mode 100644 js/sq2display_constants.js diff --git a/js/sq2bs.js b/js/sq2bs.js deleted file mode 100644 index dfdc701..0000000 --- a/js/sq2bs.js +++ /dev/null @@ -1,522 +0,0 @@ - -document.addEventListener('DOMContentLoaded', function() { - - for (const eq of equipment_keys) { - document.querySelector("#"+eq+"-choice").setAttribute("oninput", "update_field('"+ eq +"'); calcBuildSchedule();"); - document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); - } - - for (const eq of powderable_keys) { - document.querySelector("#"+eq+"-powder").setAttribute("oninput", "calcBuildSchedule(); update_field('"+ eq +"');"); - } - - for (const eq of tome_keys) { - document.querySelector("#" + eq + "-choice").setAttribute("oninput", "update_field('" + eq + "'); calcBuildSchedule();"); - document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); - } - - for (const i of spell_disp) { - document.querySelector("#"+i+"Avg").setAttribute("onclick", "toggle_spell_tab('"+i+"')"); - } - - document.querySelector("#level-choice").setAttribute("oninput", "calcBuildSchedule()") - document.querySelector("#weapon-choice").setAttribute("oninput", document.querySelector("#weapon-choice").getAttribute("oninput") + "resetArmorPowderSpecials();"); - // document.querySelector("#edit-IDs-button").setAttribute("onclick", "toggle_edit_id_tab()"); - - let skp_fields = document.getElementsByClassName("skp-update"); - - for (i = 0; i < skp_fields.length; i++) { - skp_fields[i].setAttribute("oninput", "updateStatSchedule()"); - } - - let masonry = Macy({ - container: "#masonry-container", - columns: 1, - mobileFirst: true, - breakAt: { - 1200: 4, - }, - margin: { - x: 20, - y: 20, - } - - }); - - let search_masonry = Macy({ - container: "#search-results", - columns: 1, - mobileFirst: true, - breakAt: { - 1200: 4, - }, - margin: { - x: 20, - y: 20, - } - - }); - - document.querySelector("#search-container").addEventListener("keyup", function(event) { - if (event.key === "Escape") { - document.querySelector("#search-container").style.display = "none"; - }; - }); - -}); - -// phanta scheduler -let calcBuildTask = null; -let updateStatTask = null; -let doSearchTask = null; - -function calcBuildSchedule(){ - if (calcBuildTask !== null) { - clearTimeout(calcBuildTask); - } - calcBuildTask = setTimeout(function(){ - calcBuildTask = null; - resetEditableIDs(); - calculateBuild(); - }, 500); -} - -function updateStatSchedule(){ - if (updateStatTask !== null) { - clearTimeout(updateStatTask); - } - updateStatTask = setTimeout(function(){ - updateStatTask = null; - updateStats(); - }, 500); -} - -function doSearchSchedule(){ - console.log("Search Schedule called"); - if (doSearchTask !== null) { - clearTimeout(doSearchTask); - } - doSearchTask = setTimeout(function(){ - doSearchTask = null; - doItemSearch(); - window.dispatchEvent(new Event('resize')); - }, 500); -} - -function sq2ResetFields(){ - for (let i in powderInputs) { - setValue(powderInputs[i], ""); - } - for (let i in equipmentInputs) { - setValue(equipmentInputs[i], ""); - } - - for (let i in tomeInputs) { - setValue(tomeInputs[i], ""); - } - setValue("str-skp", "0"); - setValue("dex-skp", "0"); - setValue("int-skp", "0"); - setValue("def-skp", "0"); - setValue("agi-skp", "0"); - setValue("level-choice", "106"); - location.hash = ""; - calculateBuild(); -} - -// equipment field dynamic styling -function update_field(field) { - // built on the assumption of no one will type in CI/CR letter by letter - // resets - document.querySelector("#"+field+"-choice").classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set', 'Crafted', 'Custom'); - document.querySelector("#"+field+"-choice").classList.add("text-light"); - document.querySelector("#" + field + "-img").classList.remove('Normal-shadow', 'Unique-shadow', 'Rare-shadow', 'Legendary-shadow', 'Fabled-shadow', 'Mythic-shadow', 'Set-shadow', 'Crafted-shadow', 'Custom-shadow'); - - item = document.querySelector("#"+field+"-choice").value - let powder_slots; - let tier; - let category; - let type; - - // get item info - if (item.slice(0, 3) == "CI-") { - item = getCustomFromHash(item); - powder_slots = item.statMap.get("slots"); - tier = item.statMap.get("tier"); - category = item.statMap.get("category"); - type = item.statMap.get("type"); - } - else if (item.slice(0, 3) == "CR-") { - item = getCraftFromHash(item); - powder_slots = item.statMap.get("slots"); - tier = item.statMap.get("tier"); - category = item.statMap.get("category"); - type = item.statMap.get("type"); - } - else if (itemMap.get(item)) { - item = itemMap.get(item); - if (!item) {return false;} - powder_slots = item.slots; - tier = item.tier; - category = item.category; - type = item.type; - } - else if (tomeMap.get(item)) { - tome = tomeMap.get(item); - if (!tome) {return false;} - powder_slots = 0; - tier = tome.tier; - category = tome.category; - type = tome.type; - } - else { - // item not found - document.querySelector("#"+field+"-choice").classList.add("text-light"); - if (item) { document.querySelector("#"+field+"-choice").classList.add("is-invalid"); } - - /*if (!accessory_keys.contains(type.toLowerCase())) { - document.querySelector("#"+type+"-powder").disabled = true; - }*/ - return false; - } - - - if ((type != field.replace(/[0-9]/g, '')) && (category != field.replace(/[0-9]/g, ''))) { - document.querySelector("#"+field+"-choice").classList.add("text-light"); - if (item) { document.querySelector("#"+field+"-choice").classList.add("is-invalid"); } - - //document.querySelector("#"+equipment_keys[i]+"-powder").disabled = true; - return false; - } - - // set item color - document.querySelector("#"+field+"-choice").classList.add(tier); - document.querySelector("#"+field+"-img").classList.add(tier + "-shadow"); - - - - if (powderable_keys.includes(field)) { - // set powder slots - document.querySelector("#"+field+"-powder").setAttribute("placeholder", powder_slots+" slots"); - - if (powder_slots == 0) { - document.querySelector("#"+field+"-powder").disabled = true; - } else { - document.querySelector("#"+field+"-powder").disabled = false; - } - - // powder error handling - document.querySelector("#" + field + "-powder").classList.remove("is-invalid"); - let powder_string = document.querySelector("#"+field+"-powder").value; - - if (powder_string.length % 2 != 0 || powder_string.length / 2 > powder_slots) { - document.querySelector("#"+field+"-powder").classList.add("is-invalid"); - } else { - for (i = 0; i < powder_string.length / 2; i++) { - if (skp_elements.includes(powder_string.substring(i*2, i*2+2).split("")[0]) == false || isNaN(powder_string.substring(i*2, i*2+2).split("")[1]) || parseInt(powder_string.substring(i*2, i*2+2).split("")[1]) < 1 || parseInt(powder_string.substring(i*2, i*2+2).split("")[1]) > 6) { - document.querySelector("#"+field+"-powder").classList.add("is-invalid"); - } - } - }; - } - - // set weapon img - if (category == 'weapon') { - document.querySelector("#weapon-img").setAttribute('src', '../media/items/new/generic-'+type+'.png'); - } -} -/* tabulars | man i hate this code but too lazy to fix /shrug */ - -let tabs = ['overall-stats', 'offensive-stats', 'defensive-stats']; - -function show_tab(tab) { - //console.log(itemFilters) - - //hide all tabs, then show the tab of the div clicked and highlight the correct button - for (const i in tabs) { - document.querySelector("#" + tabs[i]).style.display = "none"; - document.getElementById("tab-" + tabs[i].split("-")[0] + "-btn").classList.remove("selected-btn"); - } - document.querySelector("#" + tab).style.display = ""; - document.getElementById("tab-" + tab.split("-")[0] + "-btn").classList.add("selected-btn"); -} - -function toggle_spell_tab(tab) { - let arrow_img = document.querySelector("#" + "arrow_" + tab + "Avg"); - if (document.querySelector("#"+tab).style.display == "none") { - document.querySelector("#"+tab).style.display = ""; - arrow_img.src = arrow_img.src.replace("down", "up"); - } else { - document.querySelector("#"+tab).style.display = "none"; - arrow_img.src = arrow_img.src.replace("up", "down"); - } -} - -function toggle_boost_tab(tab) { - for (const i of skp_order) { - document.querySelector("#"+i+"-boost").style.display = "none"; - document.getElementById(i + "-boost-tab").classList.remove("selected-btn"); - } - document.querySelector("#"+tab+"-boost").style.display = ""; - document.getElementById(tab + "-boost-tab").classList.add("selected-btn"); - -} - -// toggle tab -function toggle_tab(tab) { - if (document.querySelector("#"+tab).style.display == "none") { - document.querySelector("#"+tab).style.display = ""; - } else { - document.querySelector("#"+tab).style.display = "none"; - } -} - -function collapse_element(elmnt) { - elem_list = document.querySelector(elmnt).children; - if (elem_list) { - for (elem of elem_list) { - if (elem.classList.contains("no-collapse")) { continue; } - if (elem.style.display == "none") { - elem.style.display = ""; - } else { - elem.style.display = "none"; - } - } - } - // macy quirk - window.dispatchEvent(new Event('resize')); - // weird bug where display: none overrides?? - document.querySelector(elmnt).style.removeProperty('display'); -} - -// search misc -function set_item(item) { - document.querySelector("#search-container").style.display = "none"; - let type; - // if (!player_build) {return false;} - if (item.get("category") === "weapon") { - type = "weapon"; - } else if (item.get("type") === "ring") { - if (!document.querySelector("#ring1-choice").value) { - type = "ring1"; - } else { - type = "ring2"; - } - } else { - type = item.get("type"); - } - document.querySelector("#"+type+"-choice").value = item.get("displayName"); - calcBuildSchedule(); - update_field(type); -} - -// disable boosts - -function reset_powder_specials() { - let specials = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"] - for (const special of specials) { - for (i = 1; i < 6; i++) { - if (document.querySelector("#"+special+"-"+i).classList.contains("toggleOn")) { - document.querySelector("#"+special+"-"+i).classList.remove("toggleOn"); - } - } - } -} - -// autocomplete initialize -function init_autocomplete() { - let dropdowns = new Map() - for (const eq of equipment_keys) { - if (tome_keys.includes(eq)) { - continue; - } - // build dropdown - let item_arr = []; - if (eq == 'weapon') { - for (const weaponType of weapon_keys) { - for (const weapon of itemLists.get(weaponType)) { - let item_obj = itemMap.get(weapon); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { - continue; - } - item_arr.push(weapon); - } - } - } else { - for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) { - let item_obj = itemMap.get(item); - if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") { - continue; - } - if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) { - continue; - } - item_arr.push(item) - } - } - - // create dropdown - dropdowns.set(eq, new autoComplete({ - data: { - src: item_arr - }, - selector: "#"+ eq +"-choice", - wrapper: false, - resultsList: { - maxResults: 1000, - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - // dynamic result loc - let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); - list.style.top = position.bottom + window.scrollY +"px"; - list.style.left = position.x+"px"; - list.style.width = position.width+"px"; - list.style.maxHeight = position.height * 2 +"px"; - - if (!data.results.length) { - message = document.createElement('li'); - message.classList.add('scaled-font'); - message.textContent = "No results found!"; - list.prepend(message); - } - }, - }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - element: (item, data) => { - item.classList.add(itemMap.get(data.value).tier); - }, - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - } - update_field(eq); - calcBuildSchedule(); - }, - }, - } - })); - } - - for (const eq of tome_keys) { - // build dropdown - let tome_arr = []; - for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) { - let tome_obj = tomeMap.get(tome); - if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") { - continue; - } - //this should suffice for tomes - jank - if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) { - continue; - } - let tome_name = tome; - tome_arr.push(tome_name); - } - - // create dropdown - dropdowns.set(eq, new autoComplete({ - data: { - src: tome_arr - }, - selector: "#"+ eq +"-choice", - wrapper: false, - resultsList: { - maxResults: 1000, - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - // dynamic result loc - let position = document.getElementById(eq+'-dropdown').getBoundingClientRect(); - list.style.top = position.bottom + window.scrollY +"px"; - list.style.left = position.x+"px"; - list.style.width = position.width+"px"; - list.style.maxHeight = position.height * 2 +"px"; - - if (!data.results.length) { - message = document.createElement('li'); - message.classList.add('scaled-font'); - message.textContent = "No results found!"; - list.prepend(message); - } - }, - }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - element: (tome, data) => { - tome.classList.add(tomeMap.get(data.value).tier); - }, - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - } - update_field(eq); - calcBuildSchedule(); - }, - }, - } - })); - } - - let filter_loc = ["filter1", "filter2", "filter3", "filter4"]; - for (const i of filter_loc) { - dropdowns.set(i+"-choice", new autoComplete({ - data: { - src: sq2ItemFilters, - }, - selector: "#"+i+"-choice", - wrapper: false, - resultsList: { - tabSelect: true, - noResults: true, - class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", - element: (list, data) => { - // dynamic result loc - console.log(i); - list.style.zIndex = "100"; - let position = document.getElementById(i+"-dropdown").getBoundingClientRect(); - window_pos = document.getElementById("search-container").getBoundingClientRect(); - list.style.top = position.bottom - window_pos.top + 5 +"px"; - list.style.left = position.x - window_pos.x +"px"; - list.style.width = position.width+"px"; - - if (!data.results.length) { - message = document.createElement('li'); - message.classList.add('scaled-font'); - message.textContent = "No filters found!"; - list.prepend(message); - } - }, - }, - resultItem: { - class: "scaled-font search-item", - selected: "dark-5", - }, - events: { - input: { - selection: (event) => { - if (event.detail.selection.value) { - event.target.value = event.detail.selection.value; - } - doSearchSchedule(); - }, - }, - } - })); - } -} - diff --git a/js/sq2build.js b/js/sq2build.js deleted file mode 100644 index 7551d34..0000000 --- a/js/sq2build.js +++ /dev/null @@ -1,692 +0,0 @@ - - -const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20], ["sword", 1.10]]); - -/** - * @description Error to catch items that don't exist. - * @module ItemNotFound - */ -class ItemNotFound { - /** - * @class - * @param {String} item the item name entered - * @param {String} type the type of item - * @param {Boolean} genElement whether to generate an element from inputs - * @param {String} override override for item type - */ - constructor(item, type, genElement, override) { - /** - * @public - * @type {String} - */ - this.message = `Cannot find ${override||type} named ${item}`; - if (genElement) - /** - * @public - * @type {Element} - */ - this.element = document.getElementById(`${type}-choice`).parentElement.querySelectorAll("p.error")[0]; - else - this.element = document.createElement("div"); - } -} - -/** - * @description Error to catch incorrect input. - * @module IncorrectInput - */ -class IncorrectInput { - /** - * @class - * @param {String} input the inputted text - * @param {String} format the correct format - * @param {String} sibling the id of the error node's sibling - */ - constructor(input, format, sibling) { - /** - * @public - * @type {String} - */ - this.message = `${input} is incorrect. Example: ${format}`; - /** - * @public - * @type {String} - */ - this.id = sibling; - } -} - -/** - * @description Error that inputs an array of items to generate errors of. - * @module ListError - * @extends Error - */ -class ListError extends Error { - /** - * @class - * @param {Array} errors array of errors - */ - constructor(errors) { - let ret = []; - if (typeof errors[0] == "string") { - super(errors[0]); - } else { - super(errors[0].message); - } - for (let i of errors) { - if (typeof i == "string") { - ret.push(new Error(i)); - } else { - ret.push(i); - } - } - /** - * @public - * @type {Object[]} - */ - this.errors = ret; - } -} - -/*Class that represents a wynn player's build. -*/ -class Build{ - - /** - * @description Construct a build. - * @param {Number} level : Level of the player. - * @param {String[]} equipment : List of equipment names that make up the build. - * In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon. - * @param {Number[]} powders : Powder application. List of lists of integers (powder IDs). - * In order: boots, Chestplate, Leggings, Boots, Weapon. - * @param {Object[]} inputerrors : List of instances of error-like classes. - * - * @param {Object[]} tomes: List of tomes. - * In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome. - * 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building) - */ - constructor(level,equipment, powders, externalStats, inputerrors=[], tomes){ - - let errors = inputerrors; - //this contains the Craft objects, if there are any crafted items. this.boots, etc. will contain the statMap of the Craft (which is built to be an expandedItem). - this.craftedItems = []; - this.customItems = []; - // NOTE: powders is just an array of arrays of powder IDs. Not powder objects. - this.powders = powders; - if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") { - const helmet = itemMap.get(equipment[0]); - this.powders[0] = this.powders[0].slice(0,helmet.slots); - this.helmet = expandItem(helmet, this.powders[0]); - } else { - try { - let helmet = getCustomFromHash(equipment[0]) ? getCustomFromHash(equipment[0]) : (getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : undefined); - if (helmet.statMap.get("type") !== "helmet") { - throw new Error("Not a helmet"); - } - this.powders[0] = this.powders[0].slice(0,helmet.statMap.get("slots")); - helmet.statMap.set("powders",this.powders[0].slice()); - this.helmet = helmet.statMap; - applyArmorPowders(this.helmet, this.powders[0]); - if (this.helmet.get("custom")) { - this.customItems.push(helmet); - } else if (this.helmet.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(helmet); - } - - } catch (Error) { - const helmet = itemMap.get("No Helmet"); - this.powders[0] = this.powders[0].slice(0,helmet.slots); - this.helmet = expandItem(helmet, this.powders[0]); - errors.push(new ItemNotFound(equipment[0], "helmet", true)); - } - } - if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") { - const chestplate = itemMap.get(equipment[1]); - this.powders[1] = this.powders[1].slice(0,chestplate.slots); - this.chestplate = expandItem(chestplate, this.powders[1]); - } else { - try { - let chestplate = getCustomFromHash(equipment[1]) ? getCustomFromHash(equipment[1]) : (getCraftFromHash(equipment[1]) ? getCraftFromHash(equipment[1]) : undefined); - if (chestplate.statMap.get("type") !== "chestplate") { - throw new Error("Not a chestplate"); - } - this.powders[1] = this.powders[1].slice(0,chestplate.statMap.get("slots")); - chestplate.statMap.set("powders",this.powders[1].slice()); - this.chestplate = chestplate.statMap; - applyArmorPowders(this.chesplate, this.powders[1]); - if (this.chestplate.get("custom")) { - this.customItems.push(chestplate); - } else if (this.chestplate.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(chestplate); - } - } catch (Error) { - console.log(Error); - const chestplate = itemMap.get("No Chestplate"); - this.powders[1] = this.powders[1].slice(0,chestplate.slots); - this.chestplate = expandItem(chestplate, this.powders[1]); - errors.push(new ItemNotFound(equipment[1], "chestplate", true)); - } - } - if (itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") { - const leggings = itemMap.get(equipment[2]); - this.powders[2] = this.powders[2].slice(0,leggings.slots); - this.leggings = expandItem(leggings, this.powders[2]); - } else { - try { - let leggings = getCustomFromHash(equipment[2]) ? getCustomFromHash(equipment[2]) : (getCraftFromHash(equipment[2]) ? getCraftFromHash(equipment[2]) : undefined); - if (leggings.statMap.get("type") !== "leggings") { - throw new Error("Not a leggings"); - } - this.powders[2] = this.powders[2].slice(0,leggings.statMap.get("slots")); - leggings.statMap.set("powders",this.powders[2].slice()); - this.leggings = leggings.statMap; - applyArmorPowders(this.leggings, this.powders[2]); - if (this.leggings.get("custom")) { - this.customItems.push(leggings); - } else if (this.leggings.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(leggings); - } - } catch (Error) { - const leggings = itemMap.get("No Leggings"); - this.powders[2] = this.powders[2].slice(0,leggings.slots); - this.leggings = expandItem(leggings, this.powders[2]); - errors.push(new ItemNotFound(equipment[2], "leggings", true)); - } - } - if (itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") { - const boots = itemMap.get(equipment[3]); - this.powders[3] = this.powders[3].slice(0,boots.slots); - this.boots = expandItem(boots, this.powders[3]); - } else { - try { - let boots = getCustomFromHash(equipment[3]) ? getCustomFromHash(equipment[3]) : (getCraftFromHash(equipment[3]) ? getCraftFromHash(equipment[3]) : undefined); - if (boots.statMap.get("type") !== "boots") { - throw new Error("Not a boots"); - } - this.powders[3] = this.powders[3].slice(0,boots.statMap.get("slots")); - boots.statMap.set("powders",this.powders[3].slice()); - this.boots = boots.statMap; - applyArmorPowders(this.boots, this.powders[3]); - if (this.boots.get("custom")) { - this.customItems.push(boots); - } else if (this.boots.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(boots); - } - } catch (Error) { - const boots = itemMap.get("No Boots"); - this.powders[3] = this.powders[3].slice(0,boots.slots); - this.boots = expandItem(boots, this.powders[3]); - errors.push(new ItemNotFound(equipment[3], "boots", true)); - } - } - if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") { - const ring = itemMap.get(equipment[4]); - this.ring1 = expandItem(ring, []); - }else{ - try { - let ring = getCustomFromHash(equipment[4]) ? getCustomFromHash(equipment[4]) : (getCraftFromHash(equipment[4]) ? getCraftFromHash(equipment[4]) : undefined); - if (ring.statMap.get("type") !== "ring") { - throw new Error("Not a ring"); - } - this.ring1 = ring.statMap; - if (this.ring1.get("custom")) { - this.customItems.push(ring); - } else if (this.ring1.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(ring); - } - } catch (Error) { - const ring = itemMap.get("No Ring 1"); - this.ring1 = expandItem(ring, []); - errors.push(new ItemNotFound(equipment[4], "ring1", true, "ring")); - } - } - if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") { - const ring = itemMap.get(equipment[5]); - this.ring2 = expandItem(ring, []); - }else{ - try { - let ring = getCustomFromHash(equipment[5]) ? getCustomFromHash(equipment[5]) : (getCraftFromHash(equipment[5]) ? getCraftFromHash(equipment[5]) : undefined); - if (ring.statMap.get("type") !== "ring") { - throw new Error("Not a ring"); - } - this.ring2 = ring.statMap; - if (this.ring2.get("custom")) { - this.customItems.push(ring); - } else if (this.ring2.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(ring); - } - } catch (Error) { - const ring = itemMap.get("No Ring 2"); - this.ring2 = expandItem(ring, []); - errors.push(new ItemNotFound(equipment[5], "ring2", true, "ring")); - } - } - if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") { - const bracelet = itemMap.get(equipment[6]); - this.bracelet = expandItem(bracelet, []); - }else{ - try { - let bracelet = getCustomFromHash(equipment[6]) ? getCustomFromHash(equipment[6]) : (getCraftFromHash(equipment[6]) ? getCraftFromHash(equipment[6]) : undefined); - if (bracelet.statMap.get("type") !== "bracelet") { - throw new Error("Not a bracelet"); - } - this.bracelet = bracelet.statMap; - if (this.bracelet.get("custom")) { - this.customItems.push(bracelet); - } else if (this.bracelet.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(bracelet); - } - } catch (Error) { - const bracelet = itemMap.get("No Bracelet"); - this.bracelet = expandItem(bracelet, []); - errors.push(new ItemNotFound(equipment[6], "bracelet", true)); - } - } - if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") { - const necklace = itemMap.get(equipment[7]); - this.necklace = expandItem(necklace, []); - }else{ - try { - let necklace = getCustomFromHash(equipment[7]) ? getCustomFromHash(equipment[7]) : (getCraftFromHash(equipment[7]) ? getCraftFromHash(equipment[7]) : undefined); - if (necklace.statMap.get("type") !== "necklace") { - throw new Error("Not a necklace"); - } - this.necklace = necklace.statMap; - if (this.necklace.get("custom")) { - this.customItems.push(necklace); - } else if (this.necklace.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(necklace); - } - } catch (Error) { - const necklace = itemMap.get("No Necklace"); - this.necklace = expandItem(necklace, []); - errors.push(new ItemNotFound(equipment[7], "necklace", true)); - } - } - if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") { - const weapon = itemMap.get(equipment[8]); - this.powders[4] = this.powders[4].slice(0,weapon.slots); - this.weapon = expandItem(weapon, this.powders[4]); - }else{ - try { - let weapon = getCustomFromHash(equipment[8]) ? getCustomFromHash(equipment[8]) : (getCraftFromHash(equipment[8]) ? getCraftFromHash(equipment[8]) : undefined); - if (weapon.statMap.get("category") !== "weapon") { - throw new Error("Not a weapon"); - } - this.weapon = weapon.statMap; - if (this.weapon.get("custom")) { - this.customItems.push(weapon); - } else if (this.weapon.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(weapon); - } - this.powders[4] = this.powders[4].slice(0,this.weapon.get("slots")); - this.weapon.set("powders",this.powders[4].slice()); - // document.getElementsByClassName("powder-specials")[0].style.display = "grid"; - } catch (Error) { - const weapon = itemMap.get("No Weapon"); - this.powders[4] = this.powders[4].slice(0,weapon.slots); - this.weapon = expandItem(weapon, this.powders[4]); - // document.getElementsByClassName("powder-specials")[0].style.display = "none"; - errors.push(new ItemNotFound(equipment[8], "weapon", true)); - } - } - - //cannot craft tomes - - if(tomeMap.get(tomes[0]) && tomeMap.get(tomes[0]).type === "weaponTome") { - const weaponTome1 = tomeMap.get(tomes[0]); - this.weaponTome1 = expandItem(weaponTome1, []); - } else { - try { - let weaponTome1 = getCustomFromHash(tomes[0]) ? getCustomFromHash(tomes[0]) : undefined; - if (weaponTome1.statMap.get("type") !== "weaponTome") { - throw new Error("Not a Weapon Tome"); - } - if (this.weaponTome1.get("custom")) { - this.customItems.push(weaponTome1); - } //can't craft tomes - - } catch (Error) { - const weaponTome1 = tomeMap.get("No Weapon Tome"); - this.weaponTome1 = expandItem(weaponTome1, []); - errors.push(new ItemNotFound(tomes[0], "weaponTome1", true)); - } - } - if(tomeMap.get(tomes[1]) && tomeMap.get(tomes[1]).type === "weaponTome") { - const weaponTome2 = tomeMap.get(tomes[1]); - this.weaponTome2 = expandItem(weaponTome2, []); - } else { - try { - let weaponTome2 = getCustomFromHash(tomes[1]) ? getCustomFromHash(tomes[1]) : undefined; - if (weaponTome2.statMap.get("type") !== "weaponTome") { - throw new Error("Not a Weapon Tome"); - } - if (this.weaponTome2.get("custom")) { - this.customItems.push(weaponTome2); - } //can't craft tomes - - } catch (Error) { - const weaponTome2 = tomeMap.get("No Weapon Tome"); - this.weaponTome2 = expandItem(weaponTome2, []); - errors.push(new ItemNotFound(tomes[1], "weaponTome2", true)); - } - } - if(tomeMap.get(tomes[2]) && tomeMap.get(tomes[2]).type === "armorTome") { - const armorTome1 = tomeMap.get(tomes[2]); - this.armorTome1 = expandItem(armorTome1, []); - } else { - try { - let armorTome1 = getCustomFromHash(tomes[2]) ? getCustomFromHash(tomes[2]) : undefined; - if (armorTome1.statMap.get("type") !== "armorTome") { - throw new Error("Not an Armor Tome"); - } - if (this.armorTome1.get("custom")) { - this.customItems.push(armorTome1); - } //can't craft tomes - - } catch (Error) { - const armorTome1 = tomeMap.get("No Armor Tome"); - this.armorTome1 = expandItem(armorTome1, []); - errors.push(new ItemNotFound(tomes[2], "armorTome1", true)); - } - } - if(tomeMap.get(tomes[3]) && tomeMap.get(tomes[3]).type === "armorTome") { - const armorTome2 = tomeMap.get(tomes[3]); - this.armorTome2 = expandItem(armorTome2, []); - } else { - try { - let armorTome2 = getCustomFromHash(tomes[3]) ? getCustomFromHash(tomes[3]) : undefined; - if (armorTome2.statMap.get("type") !== "armorTome") { - throw new Error("Not an Armor Tome"); - } - if (this.armorTome2.get("custom")) { - this.customItems.push(armorTome2); - } //can't craft tomes - - } catch (Error) { - const armorTome2 = tomeMap.get("No Armor Tome"); - this.armorTome2 = expandItem(armorTome2, []); - errors.push(new ItemNotFound(tomes[3], "armorTome2", true)); - } - } - if(tomeMap.get(tomes[4]) && tomeMap.get(tomes[4]).type === "armorTome") { - const armorTome3 = tomeMap.get(tomes[4]); - this.armorTome3 = expandItem(armorTome3, []); - } else { - try { - let armorTome3 = getCustomFromHash(tomes[4]) ? getCustomFromHash(tomes[4]) : undefined; - if (armorTome3.statMap.get("type") !== "armorTome") { - throw new Error("Not an Armor Tome"); - } - if (this.armorTome3.get("custom")) { - this.customItems.push(armorTome3); - } //can't craft tomes - - } catch (Error) { - const armorTome3 = tomeMap.get("No Armor Tome"); - this.armorTome3 = expandItem(armorTome3, []); - errors.push(new ItemNotFound(tomes[4], "armorTome3", true)); - } - } - if(tomeMap.get(tomes[5]) && tomeMap.get(tomes[5]).type === "armorTome") { - const armorTome4 = tomeMap.get(tomes[5]); - this.armorTome4 = expandItem(armorTome4, []); - } else { - try { - let armorTome4 = getCustomFromHash(tomes[5]) ? getCustomFromHash(tomes[5]) : undefined; - if (armorTome4.statMap.get("type") !== "armorTome") { - throw new Error("Not an Armor Tome"); - } - if (this.armorTome4.get("custom")) { - this.customItems.push(armorTome4); - } //can't craft tomes - - } catch (Error) { - const armorTome4 = tomeMap.get("No Armor Tome"); - this.armorTome4 = expandItem(armorTome4, []); - errors.push(new ItemNotFound(tomes[5], "armorTome4", true)); - } - } - if(tomeMap.get(tomes[6]) && tomeMap.get(tomes[6]).type === "guildTome") { - const guildTome1 = tomeMap.get(tomes[6]); - this.guildTome1 = expandItem(guildTome1, []); - } else { - try { - let guildTome1 = getCustomFromHash(tomes[6]) ? getCustomFromHash(tomes[6]) : undefined; - if (guildTome1.statMap.get("type") !== "guildTome1") { - throw new Error("Not an Guild Tome"); - } - if (this.guildTome1.get("custom")) { - this.customItems.push(guildTome1); - } //can't craft tomes - - } catch (Error) { - const guildTome1 = tomeMap.get("No Guild Tome"); - this.guildTome1 = expandItem(guildTome1, []); - errors.push(new ItemNotFound(tomes[6], "guildTome1", true)); - } - } - - //console.log(this.craftedItems) - - if (level < 1) { //Should these be constants? - this.level = 1; - } else if (level > 106) { - this.level = 106; - } else if (level <= 106 && level >= 1) { - this.level = level; - } else if (typeof level === "string") { - this.level = level; - errors.push(new IncorrectInput(level, "a number", "level-choice")); - } else { - errors.push("Level is not a string or number."); - } - document.getElementById("level-choice").value = this.level; - - this.availableSkillpoints = levelToSkillPoints(this.level); - this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ]; - this.tomes = [this.weaponTome1, this.weaponTome2, this.armorTome1, this.armorTome2, this.armorTome3, this.armorTome4, this.guildTome1]; - this.items = this.equipment.concat([this.weapon]).concat(this.tomes); - // return [equip_order, best_skillpoints, final_skillpoints, best_total]; - let result = calculate_skillpoints(this.equipment.concat(this.tomes), this.weapon); - console.log(result); - this.equip_order = result[0]; - // How many skillpoints the player had to assign (5 number) - this.base_skillpoints = result[1]; - // How many skillpoints the build ended up with (5 number) - this.total_skillpoints = result[2]; - // How many skillpoints assigned (1 number, sum of base_skillpoints) - this.assigned_skillpoints = result[3]; - this.activeSetCounts = result[4]; - - // For strength boosts like warscream, vanish, etc. - this.damageMultiplier = 1.0; - this.defenseMultiplier = 1.0; - - // For other external boosts ;-; - this.externalStats = externalStats; - - this.initBuildStats(); - - // Remove every error before adding specific ones - for (let i of document.getElementsByClassName("error")) { - i.textContent = ""; - } - this.errors = errors; - if (errors.length > 0) this.errored = true; - } - - /*Returns build in string format - */ - toString(){ - return [this.equipment,this.weapon,this.tomes].flat(); - } - - /* Getters */ - - getSpellCost(spellIdx, cost) { - return Math.max(1, this.getBaseSpellCost(spellIdx, cost)); - } - - getBaseSpellCost(spellIdx, cost) { - cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); - cost += this.statMap.get("spRaw"+spellIdx); - return Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100)); - } - - - /* Get melee stats for build. - Returns an array in the order: - */ - getMeleeStats(){ - const stats = this.statMap; - if (this.weapon.get("tier") === "Crafted") { - stats.set("damageBases", [this.weapon.get("nDamBaseHigh"),this.weapon.get("eDamBaseHigh"),this.weapon.get("tDamBaseHigh"),this.weapon.get("wDamBaseHigh"),this.weapon.get("fDamBaseHigh"),this.weapon.get("aDamBaseHigh")]); - } - let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier"); - if(adjAtkSpd > 6){ - adjAtkSpd = 6; - }else if(adjAtkSpd < 0){ - adjAtkSpd = 0; - } - - let damage_mult = 1; - if (this.weapon.get("type") === "relik") { - damage_mult = 0.99; // CURSE YOU WYNNCRAFT - //One day we will create WynnWynn and no longer have shaman 99% melee injustice. - //In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams. - } - // 0spellmult for melee damage. - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct") + this.externalStats.get("mdPct"), 0, this.weapon, this.total_skillpoints, damage_mult * this.damageMultiplier, this.externalStats); - - let dex = this.total_skillpoints[1]; - - let totalDamNorm = results[0]; - let totalDamCrit = results[1]; - totalDamNorm.push(1-skillPointsToPercentage(dex)); - totalDamCrit.push(skillPointsToPercentage(dex)); - let damages_results = results[2]; - - let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2]) - +(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2; - - //Now do math - let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd]; - let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd]; - let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex))); - //[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit] - return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]); - } - - /* - Get all defensive stats for this build. - */ - getDefenseStats(){ - const stats = this.statMap; - let defenseStats = []; - let def_pct = skillPointsToPercentage(this.total_skillpoints[3]); - let agi_pct = skillPointsToPercentage(this.total_skillpoints[4]); - //total hp - let totalHp = stats.get("hp") + stats.get("hpBonus"); - if (totalHp < 5) totalHp = 5; - defenseStats.push(totalHp); - //EHP - let ehp = [totalHp, totalHp]; - let defMult = classDefenseMultipliers.get(this.weapon.get("type")); - ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); - ehp[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); - defenseStats.push(ehp); - //HPR - let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); - defenseStats.push(totalHpr); - //EHPR - let ehpr = [totalHpr, totalHpr]; - ehpr[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); - ehpr[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); - defenseStats.push(ehpr); - //skp stats - defenseStats.push([ (1 - ((1-def_pct) * (2 - this.defenseMultiplier)))*100, agi_pct*100]); - //eledefs - TODO POWDERS - let eledefs = [0, 0, 0, 0, 0]; - for(const i in skp_elements){ //kinda jank but ok - eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.); - } - defenseStats.push(eledefs); - - //[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]] - return defenseStats; - } - - /* Get all stats for this build. Stores in this.statMap. - @pre The build itself should be valid. No checking of validity of pieces is done here. - */ - initBuildStats(){ - - let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "def", "agi"]; - - //Create a map of this build's stats - let statMap = new Map(); - - for (const staticID of staticIDs) { - statMap.set(staticID, 0); - } - statMap.set("hp", levelToHPBase(this.level)); - - let major_ids = new Set(); - for (const item of this.items){ - for (let [id, value] of item.get("maxRolls")) { - if (staticIDs.includes(id)) { - continue; - } - statMap.set(id,(statMap.get(id) || 0)+value); - } - for (const staticID of staticIDs) { - if (item.get(staticID)) { - statMap.set(staticID, statMap.get(staticID) + item.get(staticID)); - } - } - if (item.get("majorIds")) { - for (const major_id of item.get("majorIds")) { - major_ids.add(major_id); - } - } - } - statMap.set("activeMajorIDs", major_ids); - for (const [setName, count] of this.activeSetCounts) { - const bonus = sets[setName].bonuses[count-1]; - for (const id in bonus) { - if (skp_order.includes(id)) { - // pass. Don't include skillpoints in ids - } - else { - statMap.set(id,(statMap.get(id) || 0)+bonus[id]); - } - } - } - statMap.set("poisonPct", 100); - - // The stuff relevant for damage calculation!!! @ferricles - statMap.set("atkSpd", this.weapon.get("atkSpd")); - - for (const x of skp_elements) { - this.externalStats.set(x + "DamPct", 0); - } - this.externalStats.set("mdPct", 0); - this.externalStats.set("sdPct", 0); - this.externalStats.set("damageBonus", [0, 0, 0, 0, 0]); - this.externalStats.set("defBonus",[0, 0, 0, 0, 0]); - this.externalStats.set("poisonPct", 0); - this.statMap = statMap; - - this.aggregateStats(); - } - - aggregateStats() { - let statMap = this.statMap; - statMap.set("damageRaw", [this.weapon.get("nDam"), this.weapon.get("eDam"), this.weapon.get("tDam"), this.weapon.get("wDam"), this.weapon.get("fDam"), this.weapon.get("aDam")]); - statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]); - statMap.set("defRaw", [statMap.get("eDef"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]); - statMap.set("defBonus", [statMap.get("eDefPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]); - statMap.set("defMult", classDefenseMultipliers.get(this.weapon.get("type"))); - } -} diff --git a/js/sq2builder.js b/js/sq2builder.js deleted file mode 100644 index 31a0657..0000000 --- a/js/sq2builder.js +++ /dev/null @@ -1,1127 +0,0 @@ -const url_tag = location.hash.slice(1); -// console.log(url_base); -// console.log(url_tag); - - -const BUILD_VERSION = "7.0.19"; - -let player_build; - - -// THIS IS SUPER DANGEROUS, WE SHOULD NOT BE KEEPING THIS IN SO MANY PLACES -let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison", - "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", - "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", - "hprRaw", "hprPct", "hpBonus", "atkTier", - "spPct1", "spRaw1", "spPct2", "spRaw2", - "spPct3", "spRaw3", "spPct4", "spRaw4" ]; - -let editable_elems = []; - -for (let i of editable_item_fields) { - let elem = document.getElementById(i); - elem.addEventListener("change", (event) => { - elem.classList.add("highlight"); - }); - editable_elems.push(elem); -} - -for (let i of skp_order) { - let elem = document.getElementById(i+"-skp"); - elem.addEventListener("change", (event) => { - elem.classList.add("highlight"); - }); - editable_elems.push(elem); -} - -function clear_highlights() { - for (let i of editable_elems) { - i.classList.remove("highlight"); - } -} - - -let equipment_fields = [ - "helmet", - "chestplate", - "leggings", - "boots", - "ring1", - "ring2", - "bracelet", - "necklace", - "weapon" -]; -let tome_fields = [ - "weaponTome1", - "weaponTome2", - "armorTome1", - "armorTome2", - "armorTome3", - "armorTome4", - "guildTome1", -] -let equipment_names = [ - "Helmet", - "Chestplate", - "Leggings", - "Boots", - "Ring 1", - "Ring 2", - "Bracelet", - "Necklace", - "Weapon" -]; - -let tome_names = [ - "Weapon Tome", - "Weapon Tome", - "Armor Tome", - "Armor Tome", - "Armor Tome", - "Armor Tome", - "Guild Tome", -] -let equipmentInputs = equipment_fields.map(x => x + "-choice"); -let buildFields = equipment_fields.map(x => x+"-tooltip").concat(tome_fields.map(x => x + "-tooltip")); -let tomeInputs = tome_fields.map(x => x + "-choice"); - -let powderInputs = [ - "helmet-powder", - "chestplate-powder", - "leggings-powder", - "boots-powder", - "weapon-powder", -]; - -function getItemNameFromID(id) { - if (redirectMap.has(id)) { - return getItemNameFromID(redirectMap.get(id)); - } - return idMap.get(id); -} - -function getTomeNameFromID(id) { - if (tomeRedirectMap.has(id)) { - return getTomeNameFromID(tomeRedirectMap.get(id)); - } - return tomeIDMap.get(id); -} - -function parsePowdering(powder_info) { - // TODO: Make this run in linear instead of quadratic time... ew - let powdering = []; - for (let i = 0; i < 5; ++i) { - let powders = ""; - let n_blocks = Base64.toInt(powder_info.charAt(0)); - powder_info = powder_info.slice(1); - for (let j = 0; j < n_blocks; ++j) { - let block = powder_info.slice(0,5); - let six_powders = Base64.toInt(block); - for (let k = 0; k < 6 && six_powders != 0; ++k) { - powders += powderNames.get((six_powders & 0x1f) - 1); - six_powders >>>= 5; - } - powder_info = powder_info.slice(5); - } - powdering[i] = powders; - } - return [powdering, powder_info]; -} - -/* - * Populate fields based on url, and calculate build. - */ -function decodeBuild(url_tag) { - if (url_tag) { - //default values - let equipment = [null, null, null, null, null, null, null, null, null]; - let tomes = [null, null, null, null, null, null, null]; - let powdering = ["", "", "", "", ""]; - let info = url_tag.split("_"); - let version = info[0]; - let save_skp = false; - let skillpoints = [0, 0, 0, 0, 0]; - let level = 106; - - version_number = parseInt(version) - //equipment (items) - // TODO: use filters - if (version_number < 4) { - let equipments = info[1]; - for (let i = 0; i < 9; ++i ) { - let equipment_str = equipments.slice(i*3,i*3+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - } - info[1] = equipments.slice(27); - } - else if (version_number == 4) { - let info_str = info[1]; - let start_idx = 0; - for (let i = 0; i < 9; ++i ) { - if (info_str.slice(start_idx,start_idx+3) === "CR-") { - equipment[i] = info_str.slice(start_idx, start_idx+20); - start_idx += 20; - } else { - let equipment_str = info_str.slice(start_idx, start_idx+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - start_idx += 3; - } - } - info[1] = info_str.slice(start_idx); - } - else if (version_number <= 6) { - let info_str = info[1]; - let start_idx = 0; - for (let i = 0; i < 9; ++i ) { - if (info_str.slice(start_idx,start_idx+3) === "CR-") { - equipment[i] = info_str.slice(start_idx, start_idx+20); - start_idx += 20; - } else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") { - let len = Base64.toInt(info_str.slice(start_idx,start_idx+3)); - equipment[i] = info_str.slice(start_idx+3,start_idx+3+len); - start_idx += (3+len); - } else { - let equipment_str = info_str.slice(start_idx, start_idx+3); - equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); - start_idx += 3; - } - } - info[1] = info_str.slice(start_idx); - } - //constant in all versions - for (let i in equipment) { - setValue(equipmentInputs[i], equipment[i]); - } - - //level, skill point assignments, and powdering - if (version_number == 1) { - let powder_info = info[1]; - let res = parsePowdering(powder_info); - powdering = res[0]; - } else if (version_number == 2) { - save_skp = true; - let skillpoint_info = info[1].slice(0, 10); - for (let i = 0; i < 5; ++i ) { - skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); - } - - let powder_info = info[1].slice(10); - let res = parsePowdering(powder_info); - powdering = res[0]; - } else if (version_number <= 6){ - level = Base64.toInt(info[1].slice(10,12)); - setValue("level-choice",level); - save_skp = true; - let skillpoint_info = info[1].slice(0, 10); - for (let i = 0; i < 5; ++i ) { - skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); - } - - let powder_info = info[1].slice(12); - - let res = parsePowdering(powder_info); - powdering = res[0]; - info[1] = res[1]; - } - // Tomes. - if (version_number == 6) { - //tome values do not appear in anything before v6. - for (let i = 0; i < 7; ++i) { - let tome_str = info[1].charAt(i); - setValue(tomeInputs[i], getTomeNameFromID(Base64.toInt(tome_str))); - } - info[1] = info[1].slice(7); - } - - for (let i in powderInputs) { - setValue(powderInputs[i], powdering[i]); - } - - calculateBuild(save_skp, skillpoints); - } -} - -/* Stores the entire build in a string using B64 encoding and adds it to the URL. -*/ -function encodeBuild() { - - if (player_build) { - let build_string; - - //V6 encoding - Tomes - build_version = 4; - build_string = ""; - tome_string = ""; - - let crafted_idx = 0; - let custom_idx = 0; - for (const item of player_build.items) { - - if (item.get("custom")) { - let custom = "CI-"+encodeCustom(player_build.customItems[custom_idx],true); - build_string += Base64.fromIntN(custom.length, 3) + custom; - custom_idx += 1; - build_version = Math.max(build_version, 5); - } else if (item.get("crafted")) { - build_string += "CR-"+encodeCraft(player_build.craftedItems[crafted_idx]); - crafted_idx += 1; - } else if (item.get("category") === "tome") { - let tome_id = item.get("id"); - if (tome_id <= 60) { - // valid normal tome. ID 61-63 is for NONE tomes. - build_version = Math.max(build_version, 6); - } - tome_string += Base64.fromIntN(tome_id, 1); - } else { - build_string += Base64.fromIntN(item.get("id"), 3); - } - } - - for (const skp of skp_order) { - build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048 - } - build_string += Base64.fromIntN(player_build.level, 2); - for (const _powderset of player_build.powders) { - let n_bits = Math.ceil(_powderset.length / 6); - build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders. - // Slice copy. - let powderset = _powderset.slice(); - while (powderset.length != 0) { - let firstSix = powderset.slice(0,6).reverse(); - let powder_hash = 0; - for (const powder of firstSix) { - powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first. - } - build_string += Base64.fromIntN(powder_hash, 5); - powderset = powderset.slice(6); - } - } - build_string += tome_string; - - return build_version.toString() + "_" + build_string; - } -} - -function calculateBuild(save_skp, skp){ - try { - resetEditableIDs(); - if (player_build) { - reset_powder_specials(); - updateBoosts("skip", false); - updatePowderSpecials("skip", false); - } - let weaponName = getValue(equipmentInputs[8]); - //bruh @hpp - if (weaponName.startsWith("Morph-")) { - let equipment = [ "Morph-Stardust", "Morph-Steel", "Morph-Iron", "Morph-Gold", "Morph-Topaz", "Morph-Emerald", "Morph-Amethyst", "Morph-Ruby", weaponName.substring(6) ]; - for (let i in equipment) { - setValue(equipmentInputs[i], equipment[i]); - } - } - - //updatePowderSpecials("skip"); //jank pt 1 - save_skp = (typeof save_skp !== 'undefined') ? save_skp : false; - /* TODO: implement level changing - Make this entire function prettier - */ - let equipment = [ null, null, null, null, null, null, null, null, null ]; - for (let i in equipment) { - let equip = getValue(equipmentInputs[i]).trim(); - if (equip === "") { - equip = "No " + equipment_names[i] - } - else { - setValue(equipmentInputs[i], equip); - } - equipment[i] = equip; - } - let powderings = []; - let errors = []; - for (const i in powderInputs) { - // read in two characters at a time. - // TODO: make this more robust. - let input = getValue(powderInputs[i]).trim(); - let powdering = []; - let errorederrors = []; - while (input) { - let first = input.slice(0, 2); - let powder = powderIDs.get(first); - if (powder === undefined) { - errorederrors.push(first); - } else { - powdering.push(powder); - } - input = input.slice(2); - } - if (errorederrors.length > 0) { - if (errorederrors.length > 1) - errors.push(new IncorrectInput(errorederrors.join(""), "t6w6", powderInputs[i])); - else - errors.push(new IncorrectInput(errorederrors[0], "t6 or e3", powderInputs[i])); - } - //console.log("POWDERING: " + powdering); - powderings.push(powdering); - } - let tomes = [ null, null, null, null, null, null, null]; - for (let i in tomes) { - let equip = getValue(tomeInputs[i]).trim(); - if (equip === "") { - equip = "No " + tome_names[i] - } - else { - setValue(tomeInputs[i], equip); - } - tomes[i] = equip; - } - - - let level = document.getElementById("level-choice").value; - player_build = new Build(level, equipment, powderings, new Map(), errors, tomes); - console.log(player_build); - - //isn't this deprecated? - for (let i of document.getElementsByClassName("hide-container-block")) { - i.style.display = "block"; - } - for (let i of document.getElementsByClassName("hide-container-grid")) { - i.style.display = "grid"; - } - - console.log(player_build.toString()); - displaysq2EquipOrder(document.getElementById("build-order"),player_build.equip_order); - - const assigned = player_build.base_skillpoints; - const skillpoints = player_build.total_skillpoints; - for (let i in skp_order){ //big bren - setText(skp_order[i] + "-skp-base", "Original: " + skillpoints[i]); - } - - if (save_skp) { - // TODO: reduce duplicated code, @updateStats - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; - for (let i in skp_order) { - let manual_assigned = skp[i]; - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; - } - player_build.assigned_skillpoints += delta_total; - } - - updateEditableIDs(); - calculateBuildStats(); - if (player_build.errored) - throw new ListError(player_build.errors); - } - catch (error) { - console.log(error); - } -} - -function handleBuilderError(error) { - if (error instanceof ListError) { - for (let i of error.errors) { - if (i instanceof ItemNotFound) { - i.element.textContent = i.message; - } else if (i instanceof IncorrectInput) { - if (document.getElementById(i.id) !== null) { - document.getElementById(i.id).parentElement.querySelectorAll("p.error")[0].textContent = i.message; - } - } else { - let msg = i.stack; - let lines = msg.split("\n"); - let header = document.getElementById("header"); - header.textContent = ""; - for (const line of lines) { - let p = document.createElement("p"); - p.classList.add("itemp"); - p.textContent = line; - header.appendChild(p); - } - let p2 = document.createElement("p"); - p2.textContent = "If you believe this is an error, contact hppeng on forums or discord."; - header.appendChild(p2); - } - } - } else { - let msg = error.stack; - let lines = msg.split("\n"); - let header = document.getElementById("header"); - header.textContent = ""; - for (const line of lines) { - let p = document.createElement("p"); - p.classList.add("itemp"); - p.textContent = line; - header.appendChild(p); - } - let p2 = document.createElement("p"); - p2.textContent = "If you believe this is an error, contact hppeng on forums or discord."; - header.appendChild(p2); - } -} - -/* Updates all build statistics based on (for now) the skillpoint input fields and then calculates build stats. -*/ -function updateStats() { - - let specialNames = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"]; - for (const sName of specialNames) { - for (let i = 1; i < 6; i++) { - let elem = document.getElementById(sName + "-" + i); - let name = sName.replace("_", " "); - if (elem.classList.contains("toggleOn")) { //toggle the pressed button off - elem.classList.remove("toggleOn"); - let special = powderSpecialStats[specialNames.indexOf(sName)]; - console.log(special); - if (special["weaponSpecialEffects"].has("Damage Boost")) { - if (name === "Courage" || name === "Curse") { //courage is universal damage boost - //player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100; - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1]; - } - } - } - } - } - - - - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; - for (let i in skp_order) { - let value = document.getElementById(skp_order[i] + "-skp").value; - if (value === ""){value = 0; setValue(skp_order[i] + "-skp", value)} - let manual_assigned = 0; - if (value.includes("+")) { - let skp = value.split("+"); - for (const s of skp) { - manual_assigned += parseInt(s,10); - } - } else { - manual_assigned = parseInt(value,10); - } - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; - } - player_build.assigned_skillpoints += delta_total; - if(player_build){ - updatePowderSpecials("skip", false); - updateArmorPowderSpecials("skip", false); - updateBoosts("skip", false); - for (let id of editable_item_fields) { - player_build.statMap.set(id, parseInt(getValue(id))); - } - } - player_build.aggregateStats(); - console.log(player_build.statMap); - calculateBuildStats(); -} - - -/* Updates all IDs in the edit IDs section. Resets each input and original value text to the correct text according to the current build. -*/ -function updateEditableIDs() { - if (player_build) { - for (const id of editable_item_fields) { - let edit_input = document.getElementById(id); - let val = player_build.statMap.get(id); - edit_input.value = val; - edit_input.placeholder = val; - - let value_label = document.getElementById(id + "-base"); - value_label.textContent = "Original Value: " + val; - //a hack to make resetting easier - value_label.value = val; - } - } -} - -/* Resets all IDs in the edit IDs section to their "original" values. -*/ -function resetEditableIDs() { - if (player_build) { - for (const id of editable_item_fields) { - let edit_input = document.getElementById(id); - let value_label = document.getElementById(id + "-base"); - - edit_input.value = value_label.value; - edit_input.placeholder = value_label.value; - } - } else { - //no player build, reset to 0 - for (const id of editable_item_fields) { - let edit_input = document.getElementById(id); - - edit_input.value = 0; - edit_input.placeholder = 0; - } - } -} - -/* Updates all spell boosts -*/ -function updateBoosts(buttonId, recalcStats) { - let elem = document.getElementById(buttonId); - let name = buttonId.split("-")[0]; - if(buttonId !== "skip") { - if (elem.classList.contains("toggleOn")) { - player_build.damageMultiplier -= damageMultipliers.get(name); - if (name === "warscream") { - player_build.defenseMultiplier -= .20; - } - if (name === "vanish") { - player_build.defenseMultiplier -= .15; - } - elem.classList.remove("toggleOn"); - }else{ - player_build.damageMultiplier += damageMultipliers.get(name); - if (name === "warscream") { - player_build.defenseMultiplier += .20; - } - if (name === "vanish") { - player_build.defenseMultiplier += .15; - } - elem.classList.add("toggleOn"); - } - updatePowderSpecials("skip", false); //jank pt 1 - } else { - for (const [key, value] of damageMultipliers) { - let elem = document.getElementById(key + "-boost") - if (elem.classList.contains("toggleOn")) { - elem.classList.remove("toggleOn"); - player_build.damageMultiplier -= value; - if (key === "warscream") { player_build.defenseMultiplier -= .20 } - if (key === "vanish") { player_build.defenseMultiplier -= .15 } - } - } - } - if (recalcStats) { - calculateBuildStats(); - } -} - -/* Updates ACTIVE powder special boosts (weapons) -*/ -function updatePowderSpecials(buttonId, recalcStats) { - //console.log(player_build.statMap); - - let name = (buttonId).split("-")[0]; - let power = (buttonId).split("-")[1]; // [1, 5] - let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"]; - let powderSpecials = []; // [ [special, power], [special, power]] - - - if(name !== "skip"){ - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { //toggle the pressed button off - elem.classList.remove("toggleOn"); - let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))]; - if (special.weaponSpecialEffects.has("Damage Boost")) { - name = name.replace("_", " "); - if (name === "Courage" || name === "Curse") { //courage and curse are universal damage boost - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - //poison? - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[power-1]; - } - } - } else { - for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off - //name is same, power is i - if(document.getElementById(name.replace(" ", "_") + "-" + i).classList.contains("toggleOn")) { - document.getElementById(name.replace(" ", "_") + "-" + i).classList.remove("toggleOn"); - let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))]; - if (special.weaponSpecialEffects.has("Damage Boost")) { - name = name.replace("_", " "); //might be redundant - if (name === "Courage" || name === "Curse") { //courage is universal damage boost - //player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100; - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]); - player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1]; - } - } - } - } - //toggle the pressed button on - elem.classList.add("toggleOn"); - } - } - - for (const sName of specialNames) { - for (let i = 1;i < 6; i++) { - if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) { - let powderSpecial = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))]; - powderSpecials.push([powderSpecial, i]); - break; - } - } - } - - - if (name !== "skip") { - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { - let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))]; - if (special["weaponSpecialEffects"].has("Damage Boost")) { - let name = special["weaponSpecialName"]; - if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost - player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - } else if (name === "Wind Prison") { - player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]); - player_build.externalStats.get("damageBonus")[4] += special.weaponSpecialEffects.get("Damage Boost")[power-1]; - } - } - } - } - - if (recalcStats) { - calculateBuildStats(); - } - displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true); -} - - -/* Updates PASSIVE powder special boosts (armors) -*/ -function updateArmorPowderSpecials(elem_id, recalc_stats) { - //we only update the powder special + external stats if the player has a build - if (elem_id !== "skip") { - if (player_build !== undefined && player_build.weapon !== undefined && player_build.weapon.get("name") !== "No Weapon") { - let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi - - //update the label associated w/ the slider - let elem = document.getElementById(elem_id); - let label = document.getElementById(elem_id + "_label"); - let prev_label = document.getElementById(elem_id + "_prev"); - - let value = elem.value; - - //for use in editing build stats - let prev_value = prev_label.value; - let value_diff = value - prev_value; - - //update the "previous" label - prev_label.value = value; - - label.textContent = label.textContent.split(":")[0] + ": " + value; - - let dmg_id = elem_chars[skp_names.indexOf(wynn_elem)] + "DamPct"; - let new_dmgboost = player_build.externalStats.get(dmg_id) + value_diff; - - //update build external stats - the second one is the relevant one for damage calc purposes - player_build.externalStats.set(dmg_id, new_dmgboost); - player_build.externalStats.get("damageBonus")[skp_names.indexOf(wynn_elem)] = new_dmgboost; - - //update the slider's graphics - let bg_color = elem_colors[skp_names.indexOf(wynn_elem)]; - let pct = Math.round(100 * value / powderSpecialStats[skp_names.indexOf(wynn_elem)].cap); - elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`; - - } - } else { - if (player_build !== undefined) { - for (let i = 0; i < skp_names.length; ++i) { - skp_name = skp_names[i]; - skp_char = elem_chars[i]; - player_build.externalStats.set(skp_char + "DamPct", player_build.externalStats.get(skp_char + "DamPct") - document.getElementById(skp_name+"_boost_armor").value); - player_build.externalStats.get("damageBonus")[i] -= document.getElementById(skp_name+"_boost_armor").value; - } - } - } - - - if (recalc_stats && player_build) { - //calc build stats and display powder special - calculateBuildStats(); - // displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true); - } - -} - -function resetArmorPowderSpecials() { - for (const skp of skp_names) { - document.getElementById(skp + "_boost_armor").value = 0; - document.getElementById(skp + "_boost_armor_prev").value = 0; - document.getElementById(skp + "_boost_armor").style.background = `linear-gradient(to right, #AAAAAA, #AAAAAA 0%, #AAAAAA 100%)`; - document.getElementById(skp + "_boost_armor_label").textContent = `% ${capitalizeFirst(elem_names[skp_names.indexOf(skp)])} Damage Boost: 0` - } -} - -/* Calculates all build statistics and updates the entire display. -*/ -function calculateBuildStats() { - const assigned = player_build.base_skillpoints; - const skillpoints = player_build.total_skillpoints; - let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."]; - for (let i in skp_order){ //big bren - setText(skp_order[i] + "-skp-assign", "Assign: " + assigned[i]); - setValue(skp_order[i] + "-skp", skillpoints[i]); - let linebreak = document.createElement("br"); - linebreak.classList.add("itemp"); - document.getElementById(skp_order[i] + "-skp-label"); - setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i])); - document.getElementById(skp_order[i]+"-warnings").textContent = '' - if (assigned[i] > 100) { - let skp_warning = document.createElement("p"); - skp_warning.classList.add("warning"); - skp_warning.classList.add("small-text") - skp_warning.textContent += "Cannot assign " + assigned[i] + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually."; - document.getElementById(skp_order[i]+"-warnings").textContent = '' - document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning); - } - } - - let summarybox = document.getElementById("summary-box"); - summarybox.textContent = ""; - let skpRow = document.createElement("p"); - - let remainingSkp = document.createElement("p"); - remainingSkp.classList.add("scaled-font"); - let remainingSkpTitle = document.createElement("b"); - remainingSkpTitle.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Remaining skillpoints: "; - let remainingSkpContent = document.createElement("b"); - remainingSkpContent.textContent = "" + (levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints); - remainingSkpContent.classList.add(levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints < 0 ? "negative" : "positive"); - - remainingSkp.appendChild(remainingSkpTitle); - remainingSkp.appendChild(remainingSkpContent); - - - summarybox.append(skpRow); - summarybox.append(remainingSkp); - if(player_build.assigned_skillpoints > levelToSkillPoints(player_build.level)){ - let skpWarning = document.createElement("span"); - //skpWarning.classList.add("itemp"); - skpWarning.classList.add("warning"); - skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!"; - let skpCount = document.createElement("p"); - skpCount.classList.add("warning"); - skpCount.textContent = "For level " + (player_build.level>101 ? "101+" : player_build.level) + ", there are only " + levelToSkillPoints(player_build.level) + " skill points available."; - summarybox.append(skpWarning); - summarybox.append(skpCount); - } - let lvlWarning; - for (const item of player_build.items) { - let item_lvl; - if (item.get("crafted")) { - //item_lvl = item.get("lvlLow") + "-" + item.get("lvl"); - item_lvl = item.get("lvlLow"); - } - else { - item_lvl = item.get("lvl"); - } - - if (player_build.level < item_lvl) { - if (!lvlWarning) { - lvlWarning = document.createElement("p"); - lvlWarning.classList.add("itemp"); - lvlWarning.classList.add("warning"); - lvlWarning.textContent = "WARNING: A level " + player_build.level + " player cannot use some piece(s) of this build." - } - let baditem = document.createElement("p"); - baditem.classList.add("nocolor"); - baditem.classList.add("itemp"); - baditem.textContent = item.get("displayName") + " requires level " + item.get("lvl") + " to use."; - lvlWarning.appendChild(baditem); - } - } - if(lvlWarning){ - summarybox.append(lvlWarning); - } - for (const [setName, count] of player_build.activeSetCounts) { - const bonus = sets[setName].bonuses[count-1]; - // console.log(setName); - if (bonus["illegal"]) { - let setWarning = document.createElement("p"); - setWarning.classList.add("itemp"); - setWarning.classList.add("warning"); - setWarning.textContent = "WARNING: illegal item combination: " + setName - summarybox.append(setWarning); - } - } - - for (let i in player_build.items) { - displaysq2ExpandedItem(player_build.items[i], buildFields[i]); - collapse_element("#"+equipment_keys[i]+"-tooltip"); - } - - displaysq2ArmorStats(player_build); - displaysq2BuildStats('overall-stats', player_build, build_all_display_commands); - displaysq2BuildStats("offensive-stats",player_build, build_offensive_display_commands); - displaysq2SetBonuses("set-info",player_build); - displaysq2WeaponStats(player_build); - - let meleeStats = player_build.getMeleeStats(); - displaysq2MeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats); - - displaysq2DefenseStats(document.getElementById("defensive-stats"),player_build); - - displaysq2PoisonDamage(document.getElementById("build-poison-stats"),player_build); - - let spells = spell_table[player_build.weapon.get("type")]; - for (let i = 0; i < 4; ++i) { - let parent_elem = document.getElementById("spell"+i+"-info"); - let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); - displaysq2SpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1); - } - - location.hash = encodeBuild(); - clear_highlights(); -} - -function copyBuild() { - if (player_build) { - copyTextToClipboard(url_base+location.hash); - document.getElementById("copy-button").textContent = "Copied!"; - } -} - -function shareBuild() { - if (player_build) { - let text = url_base+location.hash+"\n"+ - "WynnBuilder build:\n"+ - "> "+player_build.helmet.get("displayName")+"\n"+ - "> "+player_build.chestplate.get("displayName")+"\n"+ - "> "+player_build.leggings.get("displayName")+"\n"+ - "> "+player_build.boots.get("displayName")+"\n"+ - "> "+player_build.ring1.get("displayName")+"\n"+ - "> "+player_build.ring2.get("displayName")+"\n"+ - "> "+player_build.bracelet.get("displayName")+"\n"+ - "> "+player_build.necklace.get("displayName")+"\n"+ - "> "+player_build.weapon.get("displayName")+" ["+player_build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]"; - copyTextToClipboard(text); - document.getElementById("share-button").textContent = "Copied!"; - } -} - -function populateBuildList() { - const buildList = document.getElementById("build-choice"); - const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds")); - - for (const buildName of Object.keys(savedBuilds).sort()) { - const buildOption = document.createElement("option"); - buildOption.setAttribute("value", buildName); - buildList.appendChild(buildOption); - } -} - -function saveBuild() { - if (player_build) { - const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds")); - const saveName = document.getElementById("build-name").value; - const encodedBuild = encodeBuild(); - if ((!Object.keys(savedBuilds).includes(saveName) - || document.getElementById("saved-error").textContent !== "") && encodedBuild !== "") { - savedBuilds[saveName] = encodedBuild.replace("#", ""); - window.localStorage.setItem("builds", JSON.stringify(savedBuilds)); - - document.getElementById("saved-error").textContent = ""; - document.getElementById("saved-build").textContent = "Build saved locally"; - - const buildList = document.getElementById("build-choice"); - const buildOption = document.createElement("option"); - buildOption.setAttribute("value", saveName); - buildList.appendChild(buildOption); - } else { - document.getElementById("saved-build").textContent = ""; - if (encodedBuild === "") { - document.getElementById("saved-error").textContent = "Empty build"; - } - else { - document.getElementById("saved-error").textContent = "Exists. Overwrite?"; - } - } - } -} - -function loadBuild() { - let savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds")); - let saveName = document.getElementById("build-name").value; - - if (Object.keys(savedBuilds).includes(saveName)) { - decodeBuild(savedBuilds[saveName]) - document.getElementById("loaded-error").textContent = ""; - document.getElementById("loaded-build").textContent = "Build loaded"; - } else { - document.getElementById("loaded-build").textContent = ""; - document.getElementById("loaded-error").textContent = "Build doesn't exist"; - } -} - -function resetFields(){ - for (let i in powderInputs) { - setValue(powderInputs[i], ""); - } - for (let i in equipmentInputs) { - setValue(equipmentInputs[i], ""); - } - setValue("str-skp", "0"); - setValue("dex-skp", "0"); - setValue("int-skp", "0"); - setValue("def-skp", "0"); - setValue("agi-skp", "0"); - setValue("level-choice", "106"); - location.hash = ""; - calculateBuild(); -} - -function toggleID() { - let button = document.getElementById("show-id-button"); - let targetDiv = document.getElementById("id-edit"); - if (button.classList.contains("toggleOn")) { //toggle the pressed button off - targetDiv.style.display = "none"; - button.classList.remove("toggleOn"); - } - else { - targetDiv.style.display = "block"; - button.classList.add("toggleOn"); - } -} - -function toggleButton(button_id) { - let button = document.getElementById(button_id); - if (button) { - if (button.classList.contains("toggleOn")) { - button.classList.remove("toggleOn"); - } else { - button.classList.add("toggleOn"); - } - } -} - -function optimizeStrDex() { - if (!player_build) { - return; - } - const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints; - const base_skillpoints = player_build.base_skillpoints; - const max_str_boost = 100 - base_skillpoints[0]; - const max_dex_boost = 100 - base_skillpoints[1]; - if (Math.min(remaining, max_str_boost, max_dex_boost) < 0) return; // Unwearable - - const base_total_skillpoints = player_build.total_skillpoints; - let str_bonus = remaining; - let dex_bonus = 0; - let best_skillpoints = player_build.total_skillpoints; - let best_damage = 0; - for (let i = 0; i <= remaining; ++i) { - let total_skillpoints = base_total_skillpoints.slice(); - total_skillpoints[0] += Math.min(max_str_boost, str_bonus); - total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus); - - // Calculate total 3rd spell damage - let spell = spell_table[player_build.weapon.get("type")][2]; - const stats = player_build.statMap; - let critChance = skillPointsToPercentage(total_skillpoints[1]); - let save_damages = []; - let spell_parts; - if (spell.parts) { - spell_parts = spell.parts; - } - else { - spell_parts = spell.variants.DEFAULT; - for (const majorID of stats.get("activeMajorIDs")) { - if (majorID in spell.variants) { - spell_parts = spell.variants[majorID]; - break; - } - } - } - let total_damage = 0; - for (const part of spell_parts) { - if (part.type === "damage") { - let _results = calculateSpellDamage(stats, part.conversion, - stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"), - part.multiplier / 100, player_build.weapon, total_skillpoints, - player_build.damageMultiplier, player_build.externalStats); - let totalDamNormal = _results[0]; - let totalDamCrit = _results[1]; - let results = _results[2]; - let tooltipinfo = _results[3]; - - for (let i = 0; i < 6; ++i) { - for (let j in results[i]) { - results[i][j] = results[i][j].toFixed(2); - } - } - let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0; - let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; - let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; - - save_damages.push(averageDamage); - if (part.summary == true) { - total_damage = averageDamage; - } - } else if (part.type === "total") { - total_damage = 0; - for (let i in part.factors) { - total_damage += save_damages[i] * part.factors[i]; - } - } - } // END Calculate total 3rd spell damage (total_damage) - if (total_damage > best_damage) { - best_damage = total_damage; - best_skillpoints = total_skillpoints.slice(); - } - - str_bonus -= 1; - dex_bonus += 1; - - } - // TODO: reduce duplicated code, @calculateBuild - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; - for (let i in skp_order) { - let manual_assigned = best_skillpoints[i]; - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; - } - player_build.assigned_skillpoints += delta_total; - - try { - calculateBuildStats(); - if (player_build.errored) - throw new ListError(player_build.errors); - } - catch (error) { - handleBuilderError(error); - } -} - -// TODO: Learn and use await -function init() { - console.log("builder.js init"); - init_autocomplete(); - decodeBuild(url_tag); - for (const i of equipment_keys) { - update_field(i); - } -} -function init2() { - load_ing_init(init); -} -function init3() { - load_tome_init(init2) -} - - -load_init(init3); diff --git a/js/sq2display.js b/js/sq2display.js deleted file mode 100644 index 10b1701..0000000 --- a/js/sq2display.js +++ /dev/null @@ -1,2407 +0,0 @@ -function displaysq2BuildStats(parent_id,build,command_group){ - // Commands to "script" the creation of nice formatting. - // #commands create a new element. - // !elemental is some janky hack for elemental damage. - // normals just display a thing. - - let display_commands = command_group; - //console.log(display_commands); - - - let parent_div = document.getElementById(parent_id); - // Clear the parent div. - if (parent_div != null) { - setHTML(parent_id, ""); - } - - let stats = build.statMap; - //console.log(build.statMap); - - let active_elem; - let elemental_format = false; - - //TODO this is put here for readability, consolidate with definition in build.js - let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef"]; - - for (const command of display_commands) { - // style instructions - - if (command.charAt(0) === "#") { - if (command === "#defense-stats") { - displaysq2DefenseStats(parent_div, build, true); - } - } - if (command.charAt(0) === "!") { - // TODO: This is sooo incredibly janky..... - if (command === "!elemental") { - elemental_format = !elemental_format; - } - } - - // id instruction - else { - let id = command; - if (stats.get(id)) { - let style = null; - - // TODO: add pos and neg style - if (!staticIDs.includes(id)) { - style = "positive"; - if (stats.get(id) < 0) { - style = "negative"; - } - } - - // ignore - let id_val = stats.get(id); - if (reversedIDs.includes(id)) { - style === "positive" ? style = "negative" : style = "positive"; - } - if (id === "poison" && id_val > 0) { - id_val = Math.ceil(id_val*build.statMap.get("poisonPct")/100); - } - displaysq2FixedID(parent_div, id, id_val, elemental_format, style); - if (id === "poison" && id_val > 0) { - let row = document.createElement('div'); - row.classList.add("row") - let value_elem = document.createElement('div'); - value_elem.classList.add('col'); - value_elem.classList.add('text-end'); - - let prefix_elem = document.createElement('b'); - prefix_elem.textContent = "\u279C With Strength: "; - let number_elem = document.createElement('b'); - number_elem.classList.add(style); - number_elem.textContent = (id_val * (1+skillPointsToPercentage(build.total_skillpoints[0])) ).toFixed(0) + idSuffixes[id]; - value_elem.append(prefix_elem); - value_elem.append(number_elem); - row.appendChild(value_elem); - - parent_div.appendChild(row); - } - - // sp thingy - } else if (skp_order.includes(id)) { - let total_assigned = build.total_skillpoints[skp_order.indexOf(id)]; - let base_assigned = build.base_skillpoints[skp_order.indexOf(id)]; - let diff = total_assigned - base_assigned; - let style; - if (diff > 0) { - style = "positive"; - } else if (diff < 0) { - style = "negative"; - } - if (diff != 0) { - displaysq2FixedID(parent_div, id, diff, false, style); - } - } - } - } -} - -function displaysq2ExpandedItem(item, parent_id){ - // Commands to "script" the creation of nice formatting. - // #commands create a new element. - // !elemental is some janky hack for elemental damage. - // normals just display a thing. - if (item.get("category") === "weapon") { - let stats = new Map(); - stats.set("atkSpd", item.get("atkSpd")); - stats.set("damageBonus", [0, 0, 0, 0, 0]); - - //SUPER JANK @HPP PLS FIX - let damage_keys = [ "nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_" ]; - if (item.get("tier") !== "Crafted") { - stats.set("damageRaw", [item.get("nDam"), item.get("eDam"), item.get("tDam"), item.get("wDam"), item.get("fDam"), item.get("aDam")]); - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], 0, 0, 0, item, [0, 0, 0, 0, 0], 1, undefined); - let damages = results[2]; - let total_damage = 0; - for (const i in damage_keys) { - total_damage += damages[i][0] + damages[i][1]; - item.set(damage_keys[i], damages[i][0]+"-"+damages[i][1]); - } - total_damage = total_damage / 2; - item.set("basedps", total_damage); - - } else { - stats.set("damageRaw", [item.get("nDamLow"), item.get("eDamLow"), item.get("tDamLow"), item.get("wDamLow"), item.get("fDamLow"), item.get("aDamLow")]); - stats.set("damageBases", [item.get("nDamBaseLow"),item.get("eDamBaseLow"),item.get("tDamBaseLow"),item.get("wDamBaseLow"),item.get("fDamBaseLow"),item.get("aDamBaseLow")]); - let resultsLow = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], 0, 0, 0, item, [0, 0, 0, 0, 0], 1, undefined); - let damagesLow = resultsLow[2]; - stats.set("damageRaw", [item.get("nDam"), item.get("eDam"), item.get("tDam"), item.get("wDam"), item.get("fDam"), item.get("aDam")]); - stats.set("damageBases", [item.get("nDamBaseHigh"),item.get("eDamBaseHigh"),item.get("tDamBaseHigh"),item.get("wDamBaseHigh"),item.get("fDamBaseHigh"),item.get("aDamBaseHigh")]); - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], 0, 0, 0, item, [0, 0, 0, 0, 0], 1, undefined); - let damages = results[2]; - console.log(damages); - - let total_damage_min = 0; - let total_damage_max = 0; - for (const i in damage_keys) { - total_damage_min += damagesLow[i][0] + damagesLow[i][1]; - total_damage_max += damages[i][0] + damages[i][1]; - item.set(damage_keys[i], damagesLow[i][0]+"-"+damagesLow[i][1]+"\u279c"+damages[i][0]+"-"+damages[i][1]); - } - total_damage_min = total_damage_min / 2; - total_damage_max = total_damage_max / 2; - item.set("basedps", [total_damage_min, total_damage_max]); - } - } else if (item.get("category") === "armor") { - } - - let display_commands = sq2_item_display_commands; - - // Clear the parent div. - setHTML(parent_id, ""); - let parent_div = document.getElementById(parent_id); - parent_div.classList.add("border", "border-2", "border-dark"); - - let fix_id = item.has("fixID") && item.get("fixID"); - let elemental_format = false; - for (let i = 0; i < display_commands.length; i++) { - const command = display_commands[i]; - if (command.charAt(0) === "!") { - // TODO: This is sooo incredibly janky..... - if (command === "!elemental") { - elemental_format = !elemental_format; - } - else if (command === "!spacer") { - let spacer = document.createElement('div'); - spacer.classList.add("row", "my-2"); - parent_div.appendChild(spacer); - continue; - } - } - else { - let id = command; - if(nonRolledIDs.includes(id)){//nonRolledID & non-0/non-null/non-und ID - if (!item.get(id)) { - if (! (item.get("crafted") && skp_order.includes(id) && - (item.get("maxRolls").get(id) || item.get("minRolls").get(id)))) { - continue; - } - } - if (id === "slots") { - let p_elem = document.createElement("div"); - p_elem.classList.add("col"); - - // PROPER POWDER DISPLAYING - let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]); - - let powderPrefix = document.createElement("b"); - powderPrefix.textContent = "Powder Slots: " + item.get(id) + " ["; - p_elem.appendChild(powderPrefix); - - let powders = item.get("powders"); - for (let i = 0; i < powders.length; i++) { - let powder = document.createElement("b"); - powder.textContent = numerals.get((powders[i]%6)+1)+" "; - powder.classList.add(damageClasses[Math.floor(powders[i]/6)+1]+"_powder"); - p_elem.appendChild(powder); - } - - let powderSuffix = document.createElement("b"); - powderSuffix.textContent = "]"; - p_elem.appendChild(powderSuffix); - parent_div.appendChild(p_elem); - } else if (id === "set") { - if (item.get("hideSet")) { continue; } - - let p_elem = document.createElement("div"); - p_elem.classList.add("col"); - p_elem.textContent = "Set: " + item.get(id).toString(); - parent_div.appendChild(p_elem); - } else if (id === "majorIds") { - //console.log(item.get(id)); - for (let majorID of item.get(id)) { - let p_elem = document.createElement("div"); - p_elem.classList.add("col"); - - let title_elem = document.createElement("b"); - let b_elem = document.createElement("b"); - if (majorID.includes(":")) { - let name = majorID.substring(0, majorID.indexOf(":")+1); - let mid = majorID.substring(majorID.indexOf(":")+1); - if (name.charAt(0) !== "+") {name = "+" + name} - title_elem.classList.add("Legendary"); - title_elem.textContent = name; - b_elem.classList.add("Crafted"); - b_elem.textContent = mid; - p_elem.appendChild(title_elem); - p_elem.appendChild(b_elem); - } else { - let name = item.get(id).toString() - if (name.charAt(0) !== "+") {name = "+" + name} - b_elem.classList.add("Legendary"); - b_elem.textContent = name; - p_elem.appendChild(b_elem); - } - parent_div.appendChild(p_elem); - } - } else if (id === "lvl" && item.get("tier") === "Crafted") { - let p_elem = document.createElement("div"); - p_elem.classList.add("col"); - p_elem.textContent = "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id); - parent_div.appendChild(p_elem); - } else if (id === "displayName") { - let row = document.createElement("div"); - - let a_elem = document.createElement("a"); - row.classList.add("row", "justify-content-center"); - a_elem.classList.add("col-auto", "text-center", "item-title", "p-0"); - a_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal"); - // a_elem.style.textGrow = 1; - - row.appendChild(a_elem); - - /* - FUNCTIONALITY FOR THIS FEATURE HAS SINCE BEEN REMOVED (WITH SQ2). - IF WE WANT TO USE IT IN THE FUTURE, I'VE LEFT THE CODE TO ADD IT IN HERE - */ - - //allow the plus minus element to toggle upon click: âž•âž– - // let plusminus = document.createElement("div"); - // plusminus.id = parent_div.id.split("-")[0] + "-pm"; - // plusminus.classList.add("col", "plus_minus", "text_end"); - // plusminus.style.flexGrow = 0; - // plusminus.textContent = "\u2795"; - // row.appendChild(plusminus); - - if (item.get("custom")) { - a_elem.href = "../custom/#" + item.get("hash"); - a_elem.textContent = item.get("displayName"); - } else if (item.get("crafted")) { - a_elem.href = "../crafter/#" + item.get("hash"); - a_elem.textContent = item.get(id); - } else { - a_elem.href = "../item/#" + item.get("displayName"); - a_elem.textContent = item.get("displayName"); - } - parent_div.appendChild(row); - - let nolink_row = document.createElement("div"); - let p_elem = document.createElement("p"); - nolink_row.classList.add("row", "justify-content-center"); - nolink_row.style.display = "none"; - p_elem.classList.add("col-auto", "text-center", "item-title", "p-0"); - p_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal"); - if (item.get("custom")) { - p_elem.textContent = item.get("displayName"); - } else if (item.get("crafted")) { - p_elem.textContent = item.get(id); - } else { - p_elem.textContent = item.get("displayName"); - } - - nolink_row.appendChild(p_elem); - parent_div.appendChild(nolink_row); - - let img = document.createElement("img"); - if (item && item.has("type")) { - img.src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png"; - img.alt = item.get("type"); - img.style = " z=index: 1; position: relative;"; - let container = document.createElement("div"); - - let bckgrd = document.createElement("div"); - bckgrd.classList.add("col", "px-0", "d-flex", "align-items-center", "justify-content-center");// , "no-collapse"); - bckgrd.style = "border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "hsl(0, 0%, 16%) 80%); margin-left: auto; margin-right: auto;" - bckgrd.classList.add("scaled-bckgrd"); - parent_div.appendChild(container); - container.appendChild(bckgrd); - bckgrd.appendChild(img); - } - } else { - let p_elem; - if ( !(item.get("tier") === "Crafted" && item.get("category") === "armor" && id === "hp") && (!skp_order.includes(id)) || (skp_order.includes(id) && item.get("tier") !== "Crafted" && parent_div.nodeName === "table") ) { //skp warp - p_elem = displaysq2FixedID(parent_div, id, item.get(id), elemental_format); - } else if (item.get("tier") === "Crafted" && item.get("category") === "armor" && id === "hp") { - p_elem = displaysq2FixedID(parent_div, id, item.get(id+"Low")+"-"+item.get(id), elemental_format); - } - if (id === "lore") { - p_elem.style = "font-style: italic"; - } else if (skp_order.includes(id)) { //id = str, dex, int, def, or agi - if ( item.get("tier") !== "Crafted") { - row = document.createElement("div"); - row.classList.add("col"); - - let title = document.createElement("b"); - title.textContent = idPrefixes[id] + " "; - let boost = document.createElement("b"); - if (item.get(id) < 0) { - boost.classList.add("negative"); - } else { //boost = 0 SHOULD not come up - boost.classList.add("positive"); - } - boost.textContent = item.get(id); - row.appendChild(title); - row.appendChild(boost); - parent_div.appendChild(row); - } else if ( item.get("tier") === "Crafted") { - let row = displaysq2RolledID(item, id, elemental_format); - parent_div.appendChild(row); - } - } else if (id === "restrict") { - p_elem.classList.add("restrict"); - } - } - } - else if ( rolledIDs.includes(id) && - ((item.get("maxRolls") && item.get("maxRolls").get(id)) - || (item.get("minRolls") && item.get("minRolls").get(id)))) { - let style = "positive"; - if (item.get("minRolls").get(id) < 0) { - style = "negative"; - } - if(reversedIDs.includes(id)){ - style === "positive" ? style = "negative" : style = "positive"; - } - if (fix_id) { - p_elem = document.createElement("div"); - p_elem.classList.add("col", "text-nowrap"); - if (id == "dex") { - console.log("dex activated at fix_id") - } - displaysq2FixedID(p_elem, id, item.get("minRolls").get(id), elemental_format, style); - parent_div.appendChild(p_elem); - } - else { - let row = displaysq2RolledID(item, id, elemental_format); - parent_div.appendChild(row); - } - }else{ - // :/ - } - } - } - //Show powder specials ;-; - let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots", "ring", "bracelet", "necklace"]; - if(nonConsumables.includes(item.get("type"))) { - let powder_special = document.createElement("div"); - powder_special.classList.add("col"); - let powders = item.get("powders"); - let element = ""; - let power = 0; - for (let i = 0; i < powders.length; i++) { - let firstPowderType = skp_elements[Math.floor(powders[i]/6)]; - if (element !== "") break; - else if (powders[i]%6 > 2) { //t4+ - for (let j = i+1; j < powders.length; j++) { - let currentPowderType = skp_elements[Math.floor(powders[j]/6)] - if (powders[j] % 6 > 2 && firstPowderType === currentPowderType) { - element = currentPowderType; - power = Math.round(((powders[i] % 6 + powders[j] % 6 + 2) / 2 - 4) * 2); - break; - } - } - } - } - if (element !== "") {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]" - let powderSpecial = powderSpecialStats[ skp_elements.indexOf(element)]; - let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); - let specialTitle = document.createElement("span"); - let specialEffects = document.createElement("span"); - addClasses(specialTitle, [damageClasses[skp_elements.indexOf(element) + 1]]); - let effects; - if (item.get("category") === "weapon") {//weapon - effects = powderSpecial["weaponSpecialEffects"]; - specialTitle.textContent = powderSpecial["weaponSpecialName"]; - }else if (item.get("category") === "armor") {//armor - effects = powderSpecial["armorSpecialEffects"]; - specialTitle.textContent += powderSpecial["armorSpecialName"] + ": "; - } - for (const [key,value] of effects.entries()) { - if (key !== "Description") { - let effect = document.createElement("p"); - effect.classList.add("m-0"); - effect.textContent = key + ": " + value[power] + specialSuffixes.get(key); - if(key === "Damage"){ - effect.textContent += elementIcons[skp_elements.indexOf(element)]; - } - if (element === "w" && item.get("category") === "armor") { - effect.textContent += " / Mana Used"; - } - specialEffects.appendChild(effect); - }else{ - specialTitle.textContent += "[ " + effects.get("Description") + " ]"; - } - } - powder_special.appendChild(specialTitle); - powder_special.appendChild(specialEffects); - parent_div.appendChild(powder_special); - } - } - - if(item.get("tier") && item.get("tier") === "Crafted") { - let dura_elem = document.createElement("div"); - dura_elem.classList.add("col"); - let dura = []; - let suffix = ""; - if(nonConsumables.includes(item.get("type"))) { - dura = item.get("durability"); - dura_elem.textContent = "Durability: " - } else { - dura = item.get("duration"); - dura_elem.textContent = "Duration: " - suffix = " sec." - let charges = document.createElement("b"); - charges.textContent = "Charges: " + item.get("charges"); - parent_div.appendChild(charges); - } - - if (typeof(dura) === "string") { - dura_elem.textContent += dura + suffix; - } else { - dura_elem.textContent += dura[0]+"-"+dura[1] + suffix; - } - parent_div.append(dura_elem); - - } - //Show item tier - if (item.get("tier") && item.get("tier") !== " ") { - let item_desc_elem = document.createElement("div"); - item_desc_elem.classList.add("col"); - item_desc_elem.classList.add(item.get("tier")); - if (tome_types.includes(item.get("type"))) { - tome_type_map = new Map([["weaponTome", "Weapon Tome"],["armorTome", "Armor Tome"],["guildTome", "Guild Tome"]]); - item_desc_elem.textContent = item.get("tier")+" "+tome_type_map.get(item.get("type")); - } else { - item_desc_elem.textContent = item.get("tier")+" "+item.get("type"); - } - parent_div.append(item_desc_elem); - } - - //Show item hash if applicable - if (item.get("crafted") || item.get("custom")) { - let item_desc_elem = document.createElement("p"); - item_desc_elem.classList.add('itemp'); - item_desc_elem.style.maxWidth = "100%"; - item_desc_elem.style.wordWrap = "break-word"; - item_desc_elem.style.wordBreak = "break-word"; - item_desc_elem.textContent = item.get("hash"); - parent_div.append(item_desc_elem); - } - - if (item.get("category") === "weapon") { - let damage_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))]; - let total_damages = item.get("basedps"); - let base_dps_elem = document.createElement("p"); - base_dps_elem.classList.add("left"); - base_dps_elem.classList.add("itemp"); - if (item.get("tier") === "Crafted") { - let base_dps_min = total_damages[0] * damage_mult; - let base_dps_max = total_damages[1] * damage_mult; - - // base_dps_elem.textContent = "Base DPS: "+base_dps_min.toFixed(3)+"\u279c"+base_dps_max.toFixed(3); - base_dps_elem.textContent = base_dps_min.toFixed(3)+"\u279c"+base_dps_max.toFixed(3); - } - else { - let bdps = total_damages * damage_mult; - base_dps_elem.textContent = (bdps ? bdps : 0); - } - parent_div.appendChild(document.createElement("p")); - parent_div.appendChild(base_dps_elem); - } -} - -function displaysq2RolledID(item, id, elemental_format) { - let row = document.createElement('div'); - row.classList.add('col'); - - let item_div = document.createElement('div'); - item_div.classList.add('row'); - - let min_elem = document.createElement('div'); - min_elem.classList.add('col', 'text-start'); - min_elem.style.cssText += "flex-grow: 0"; - let id_min = item.get("minRolls").get(id) - let style = id_min < 0 ? "negative" : "positive"; - if(reversedIDs.includes(id)){ - style === "positive" ? style = "negative" : style = "positive"; - } - min_elem.classList.add(style); - min_elem.textContent = id_min + idSuffixes[id]; - item_div.appendChild(min_elem); - - let desc_elem = document.createElement('div'); - desc_elem.classList.add('col', 'text-center');//, 'text-nowrap'); - desc_elem.style.cssText += "flex-grow: 1"; - //TODO elemental format jank - if (elemental_format) { - apply_sq2_elemental_format(desc_elem, id); - } - else { - desc_elem.textContent = idPrefixes[id]; - } - item_div.appendChild(desc_elem); - - let max_elem = document.createElement('div'); - let id_max = item.get("maxRolls").get(id) - max_elem.classList.add('col', 'text-end'); - max_elem.style.cssText += "flex-grow: 0"; - style = id_max < 0 ? "negative" : "positive"; - if (reversedIDs.includes(id)) { - style === "positive" ? style = "negative" : style = "positive"; - } - max_elem.classList.add(style); - max_elem.textContent = id_max + idSuffixes[id]; - item_div.appendChild(max_elem); - row.appendChild(item_div); - return row; -} - -function displaysq2WeaponStats(build) { - // let base_damage = build.get('damageRaw'); - let damage_keys = [ "nDam", "eDam", "tDam", "wDam", "fDam", "aDam" ]; - - // pP base calc (why do i still use pP) - let item = build.weapon; - let stats = new Map(); - stats.set("atkSpd", item.get("atkSpd")); - stats.set("damageBonus", [0, 0, 0, 0, 0]); - stats.set("damageRaw", [item.get("nDam"), item.get("eDam"), item.get("tDam"), item.get("wDam"), item.get("fDam"), item.get("aDam")]); - - //needed for damage calc CR powders - if (build.weapon.get("tier") === "Crafted") { - stats.set("damageBases", [item.get("nDamBaseHigh"), item.get("eDamBaseHigh"), item.get("tDamBaseHigh"), item.get("wDamBaseHigh"), item.get("fDamBaseHigh"), item.get("aDamBaseHigh")]); - } - - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], 0, 0, 0, build.weapon, [0, 0, 0, 0, 0], 1, undefined); - let powdered_base = results[2]; - - const powdered_map = new Map(); - powdered_map.set('nDam', powdered_base[0][0]+'-'+powdered_base[0][1]); - powdered_map.set('eDam', powdered_base[1][0]+'-'+powdered_base[1][1]); - powdered_map.set('tDam', powdered_base[2][0]+'-'+powdered_base[2][1]); - powdered_map.set('wDam', powdered_base[3][0]+'-'+powdered_base[3][1]); - powdered_map.set('fDam', powdered_base[4][0]+'-'+powdered_base[4][1]); - powdered_map.set('aDam', powdered_base[5][0]+'-'+powdered_base[5][1]); - - // display - - //I think this is res's code? in any case it doesn't work - ferri - /* for (const i in damage_keys) { - document.getElementById(damage_keys[i]+"-base").textContent = powdered_map.get(damage_keys[i]); - } */ - - let tot = 0; - //sum up elemental damages to get sum of base dps's - for (let i = 0; i < 6; i++) { - tot += powdered_base[i][0] + powdered_base[i][1]; - } - tot /= 2; - let dps = Math.max(0, Math.round(tot * baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))] )); //atkspeeds - - // document.getElementById("weapon-dps").textContent = "base dps: " + (isNaN(dps) ? 0 : dps); - document.getElementById("weapon-dps").textContent = (isNaN(dps) ? 0 : dps); - document.getElementById("weapon-lv").textContent = item.get("lvl"); - - if (item.get("type")) { - document.getElementById("weapon-img").src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png"; - } -} - -function displaysq2FixedID(active, id, value, elemental_format, style) { - if (style) { - let row = document.createElement('div'); - row.classList.add("row"); - let desc_elem = document.createElement('div'); - desc_elem.classList.add('col'); - desc_elem.classList.add('text-start'); - - if (elemental_format) { - apply_sq2_elemental_format(desc_elem, id); - } - else { - desc_elem.textContent = idPrefixes[id]; - } - row.appendChild(desc_elem); - - let value_elem = document.createElement('div'); - value_elem.classList.add('col'); - value_elem.classList.add('text-end'); - value_elem.classList.add(style); - value_elem.textContent = value + idSuffixes[id]; - row.appendChild(value_elem); - active.appendChild(row); - return row; - } - else { - // HACK TO AVOID DISPLAYING ZERO DAMAGE! TODO - if (value === "0-0" || value === "0-0\u279c0-0") { - return; - } - let p_elem = document.createElement('div'); - p_elem.classList.add('col'); - if (elemental_format) { - apply_sq2_elemental_format(p_elem, id, value); - } - else { - p_elem.textContent = idPrefixes[id].concat(value, idSuffixes[id]); - } - active.appendChild(p_elem); - return p_elem; - } -} - -function displaysq2PoisonDamage(overallparent_elem, build) { - overallparent_elem.textContent = ""; - - //Title - let title_elemavg = document.createElement("b"); - title_elemavg.textContent = "Poison Stats"; - overallparent_elem.append(title_elemavg); - - let overallpoisonDamage = document.createElement("p"); - let overallpoisonDamageFirst = document.createElement("span"); - let overallpoisonDamageSecond = document.createElement("span"); - let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct"))/100 /3); - overallpoisonDamageFirst.textContent = "Poison Tick: "; - overallpoisonDamageSecond.textContent = Math.max(poison_tick,0); - overallpoisonDamageSecond.classList.add("Damage"); - - overallpoisonDamage.appendChild(overallpoisonDamageFirst); - overallpoisonDamage.appendChild(overallpoisonDamageSecond); - overallparent_elem.append(overallpoisonDamage); -} - -function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ - // console.log("Melee Stats"); - // console.log(meleeStats); - let tooltipinfo = meleeStats[13]; - let attackSpeeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"]; - //let damagePrefixes = ["Neutral Damage: ","Earth Damage: ","Thunder Damage: ","Water Damage: ","Fire Damage: ","Air Damage: "]; - parent_elem.textContent = ""; - overallparent_elem.textContent = ""; - const stats = meleeStats.slice(); - - for (let i = 0; i < 6; ++i) { - for (let j in stats[i]) { - stats[i][j] = stats[i][j].toFixed(2); - } - } - for (let i = 6; i < 8; ++i) { - for (let j = 0; j < 2; j++) { - stats[i][j] = stats[i][j].toFixed(2); - } - } - for (let i = 8; i < 11; ++i){ - stats[i] = stats[i].toFixed(2); - } - //tooltipelem, tooltiptext - let tooltip; let tooltiptext; - - //title - let title_elem = document.createElement("p"); - title_elem.classList.add("title"); - title_elem.textContent = "Melee Stats"; - parent_elem.append(title_elem); - parent_elem.append(document.createElement("br")); - - //overall title - let title_elemavg = document.createElement("b"); - title_elemavg.textContent = "Melee Stats"; - overallparent_elem.append(title_elemavg); - - //average DPS - let averageDamage = document.createElement("p"); - averageDamage.classList.add("left"); - averageDamage.textContent = "Average DPS: " + stats[10]; - tooltiptext = `= ((${stats[8]} * ${(stats[6][2]).toFixed(2)}) + (${stats[9]} * ${(stats[7][2]).toFixed(2)}))` - tooltip = createTooltip(tooltip, "p", tooltiptext, averageDamage, ["melee-tooltip"]); - averageDamage.appendChild(tooltip); - parent_elem.append(averageDamage); - - //overall average DPS - let overallaverageDamage = document.createElement("p"); - let overallaverageDamageFirst = document.createElement("span"); - overallaverageDamageFirst.textContent = "Average DPS: " - - let overallaverageDamageSecond = document.createElement("span"); - overallaverageDamageSecond.classList.add("Damage"); - overallaverageDamageSecond.textContent = stats[10]; - overallaverageDamage.appendChild(overallaverageDamageFirst); - overallaverageDamage.appendChild(overallaverageDamageSecond); - - overallparent_elem.append(overallaverageDamage); - //overallparent_elem.append(document.createElement("br")); - - //attack speed - let atkSpd = document.createElement("p"); - atkSpd.classList.add("left"); - atkSpd.textContent = "Attack Speed: " + attackSpeeds[stats[11]]; - parent_elem.append(atkSpd); - parent_elem.append(document.createElement("br")); - - //overall attack speed - let overallatkSpd = document.createElement("p"); - let overallatkSpdFirst = document.createElement("span"); - overallatkSpdFirst.textContent = "Attack Speed: "; - let overallatkSpdSecond = document.createElement("span"); - overallatkSpdSecond.classList.add("Damage"); - overallatkSpdSecond.textContent = attackSpeeds[stats[11]]; - overallatkSpd.appendChild(overallatkSpdFirst); - overallatkSpd.appendChild(overallatkSpdSecond); - overallparent_elem.append(overallatkSpd); - - //Non-Crit: n->elem, total dmg, DPS - let nonCritStats = document.createElement("p"); - nonCritStats.classList.add("left"); - nonCritStats.textContent = "Non-Crit Stats: "; - nonCritStats.append(document.createElement("br")); - for (let i = 0; i < 6; i++){ - if(stats[i][1] != 0){ - let dmg = document.createElement("p"); - dmg.textContent = stats[i][0] + " \u2013 " + stats[i][1]; - dmg.classList.add(damageClasses[i]); - dmg.classList.add("itemp"); - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(0,2).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, dmg, ["melee-tooltip"]); - nonCritStats.append(dmg); - } - } - - let normalDamage = document.createElement("p"); - normalDamage.textContent = "Total: " + stats[6][0] + " \u2013 " + stats[6][1]; - let tooltiparr = ["Min: = ", "Max: = "] - let arr = []; let arr2 = []; - for (let i = 0; i < 6; i++) { - if (stats[i][0] != 0) { - arr.push(stats[i][0]); - arr2.push(stats[i][1]); - } - } - tooltiptext = tooltiparr[0] + arr.join(" + ") + "\n" + tooltiparr[1] + arr2.join(" + "); - tooltip = createTooltip(tooltip, "p", tooltiptext, normalDamage, ["melee-tooltip"]); - nonCritStats.append(normalDamage); - - let normalDPS = document.createElement("p"); - normalDPS.textContent = "Normal DPS: " + stats[8]; - normalDPS.classList.add("tooltip"); - tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, normalDPS, ["melee-tooltip"]); - nonCritStats.append(normalDPS); - - //overall average DPS - let singleHitDamage = document.createElement("p"); - let singleHitDamageFirst = document.createElement("span"); - singleHitDamageFirst.textContent = "Single Hit Average: "; - let singleHitDamageSecond = document.createElement("span"); - singleHitDamageSecond.classList.add("Damage"); - singleHitDamageSecond.textContent = stats[12].toFixed(2); - tooltiptext = ` = ((${stats[6][0]} + ${stats[6][1]}) / 2) * ${stats[6][2].toFixed(2)} + ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${stats[7][2].toFixed(2)}`; - // tooltip = createTooltip(tooltip, "p", tooltiptext, singleHitDamage, ["melee-tooltip", "summary-tooltip"]); - - singleHitDamage.appendChild(singleHitDamageFirst); - singleHitDamage.appendChild(singleHitDamageSecond); - overallparent_elem.append(singleHitDamage); - - let normalChance = document.createElement("p"); - normalChance.textContent = "Non-Crit Chance: " + (stats[6][2]*100).toFixed(2) + "%"; - normalChance.append(document.createElement("br")); - normalChance.append(document.createElement("br")); - nonCritStats.append(normalChance); - - parent_elem.append(nonCritStats); - parent_elem.append(document.createElement("br")); - - //Crit: n->elem, total dmg, DPS - let critStats = document.createElement("p"); - critStats.classList.add("left"); - critStats.textContent = "Crit Stats: "; - critStats.append(document.createElement("br")); - for (let i = 0; i < 6; i++){ - if(stats[i][3] != 0) { - dmg = document.createElement("p"); - dmg.textContent = stats[i][2] + " \u2013 " + stats[i][3]; - dmg.classList.add(damageClasses[i]); - dmg.classList.add("itemp"); - tooltiptext = tooltipinfo.get("damageformulas")[i].slice(2,4).join("\n"); - tooltip = createTooltip(tooltip, "p", tooltiptext, dmg, ["melee-tooltip"]); - critStats.append(dmg); - } - } - let critDamage = document.createElement("p"); - critDamage.textContent = "Total: " + stats[7][0] + " \u2013 " + stats[7][1]; - tooltiparr = ["Min: = ", "Max: = "] - arr = []; arr2 = []; - for (let i = 0; i < 6; i++) { - if (stats[i][0] != 0) { - arr.push(stats[i][2]); - arr2.push(stats[i][3]); - } - } - tooltiptext = tooltiparr[0] + arr.join(" + ") + "\n" + tooltiparr[1] + arr2.join(" + "); - tooltip = createTooltip(tooltip, "p", tooltiptext, critDamage, ["melee-tooltip"]); - - critStats.append(critDamage); - - let critDPS = document.createElement("p"); - critDPS.textContent = "Crit DPS: " + stats[9]; - tooltiptext = ` = ((${stats[7][0]} + ${stats[7][1]}) / 2) * ${baseDamageMultiplier[stats[11]]}`; - tooltip = createTooltip(tooltip, "p", tooltiptext, critDPS, ["melee-tooltip"]); - critStats.append(critDPS); - - let critChance = document.createElement("p"); - critChance.textContent = "Crit Chance: " + (stats[7][2]*100).toFixed(2) + "%"; - critChance.append(document.createElement("br")); - critChance.append(document.createElement("br")); - critStats.append(critChance); - - parent_elem.append(critStats); -} - -function displaysq2ArmorStats(build) { - let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace']; - - for (const i in armor_keys) { - document.getElementById(armor_keys[i]+'-health').textContent = build[armor_keys[i]].get('hp'); - document.getElementById(armor_keys[i]+'-lv').textContent = build[armor_keys[i]].get('lvl'); - } -} - -function displaysq2DefenseStats(parent_elem, build, insertSummary){ - let defenseStats = build.getDefenseStats(); - insertSummary = (typeof insertSummary !== 'undefined') ? insertSummary : false; - if (!insertSummary) { - parent_elem.textContent = ""; - } - const stats = defenseStats.slice(); - - // parent_elem.append(document.createElement("br")); - let statsTable = document.createElement("div"); - - //[total hp, ehp, total hpr, ehpr, [def%, agi%], [edef,tdef,wdef,fdef,adef]] - for(const i in stats){ - if(typeof stats[i] === "number"){ - stats[i] = stats[i].toFixed(2); - }else{ - for(const j in stats[i]){ - stats[i][j] = stats[i][j].toFixed(2); - } - } - } - - //total HP - let hpRow = document.createElement("div"); - hpRow.classList.add('row'); - let hp = document.createElement("div"); - hp.classList.add('col'); - hp.classList.add("Health"); - hp.classList.add("text-start"); - hp.textContent = "Total HP:"; - let boost = document.createElement("div"); - boost.classList.add('col'); - boost.textContent = stats[0]; - boost.classList.add("text-end"); - - hpRow.appendChild(hp); - hpRow.append(boost); - - if (insertSummary) { - parent_elem.appendChild(hpRow); - } else { - statsTable.appendChild(hpRow); - } - - let tooltip; let tooltiptext; - - let defMult = build.statMap.get("defMult"); - if (!defMult) {defMult = 1} - - //EHP - let ehpRow = document.createElement("div"); - ehpRow.classList.add("row"); - let ehp = document.createElement("div"); - ehp.classList.add("col"); - ehp.classList.add("text-start"); - ehp.textContent = "Effective HP:"; - - boost = document.createElement("div"); - boost.textContent = stats[1][0]; - boost.classList.add("col"); - boost.classList.add("text-end"); - tooltiptext = `= ${stats[0]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (1 - ${skillPointsToPercentage(build.total_skillpoints[4]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - - ehpRow.appendChild(ehp); - ehpRow.append(boost); - - if (insertSummary) { - parent_elem.appendChild(ehpRow) - } else { - statsTable.append(ehpRow); - } - - ehpRow = document.createElement("div"); - ehpRow.classList.add("row"); - ehp = document.createElement("div"); - ehp.classList.add("col"); - ehp.classList.add("text-start"); - ehp.textContent = "Effective HP (no agi):"; - - boost = document.createElement("div"); - boost.textContent = stats[1][1]; - boost.classList.add("col"); - boost.classList.add("text-end"); - tooltiptext = `= ${stats[0]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - - ehpRow.appendChild(ehp); - ehpRow.append(boost); - statsTable.append(ehpRow); - - //total HPR - let hprRow = document.createElement("div"); - hprRow.classList.add("row") - let hpr = document.createElement("div"); - hpr.classList.add("Health"); - hpr.classList.add("col"); - hpr.classList.add("text-start"); - hpr.textContent = "HP Regen (Total):"; - boost = document.createElement("div"); - boost.textContent = stats[2]; - boost.classList.add("col"); - boost.classList.add("text-end"); - - hprRow.appendChild(hpr); - hprRow.appendChild(boost); - - if (insertSummary) { - parent_elem.appendChild(hprRow); - } else { - statsTable.appendChild(hprRow); - } - - //EHPR - let ehprRow = document.createElement("div"); - ehprRow.classList.add("row") - let ehpr = document.createElement("div"); - ehpr.classList.add("col"); - ehpr.classList.add("text-start"); - ehpr.textContent = "Effective HP Regen:"; - - boost = document.createElement("div"); - boost.textContent = stats[3][0]; - boost.classList.add("col"); - boost.classList.add("text-end"); - tooltiptext = `= ${stats[2]} / ((1 - ${skillPointsToPercentage(build.total_skillpoints[3]).toFixed(3)}) * (1 - ${skillPointsToPercentage(build.total_skillpoints[4]).toFixed(3)}) * (2 - ${defMult}) * (2 - ${build.defenseMultiplier}))` - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - - ehprRow.appendChild(ehpr); - ehprRow.append(boost); - statsTable.append(ehprRow); - /* - ehprRow = document.createElement("tr"); - ehpr = document.createElement("td"); - ehpr.classList.add("left"); - ehpr.textContent = "Effective HP Regen (no agi):"; - - boost = document.createElement("td"); - boost.textContent = stats[3][1]; - boost.classList.add("right"); - - ehprRow.appendChild(ehpr); - ehprRow.append(boost); - statsTable.append(ehprRow); */ - - //eledefs - let eledefs = stats[5]; - for (let i = 0; i < eledefs.length; i++){ - let eledefElemRow = document.createElement("div"); - eledefElemRow.classList.add("row") - - let eledef = document.createElement("div"); - eledef.classList.add("col"); - eledef.classList.add("text-start"); - let eledefTitle = document.createElement("span"); - eledefTitle.textContent = damageClasses[i+1]; - eledefTitle.classList.add(damageClasses[i+1]); - - let defense = document.createElement("span"); - defense.textContent = " Def (Total): "; - - eledef.appendChild(eledefTitle); - eledef.appendChild(defense); - eledefElemRow.appendChild(eledef); - - let boost = document.createElement("div"); - boost.textContent = eledefs[i]; - boost.classList.add(eledefs[i] >= 0 ? "positive" : "negative"); - boost.classList.add("col"); - boost.classList.add("text-end"); - - let defRaw = build.statMap.get("defRaw")[i]; - let defPct = build.statMap.get("defBonus")[i]/100; - if (defRaw < 0) { - defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct; - tooltiptext = `= min(0, ${defRaw} * (1 ${defPct}))` - } else { - defPct >= 0 ? defPct = "+ " + defPct: defPct = "- " + defPct; - tooltiptext = `= ${defRaw} * (1 ${defPct})` - } - // tooltip = createTooltip(tooltip, "p", tooltiptext, boost, ["def-tooltip"]); - - eledefElemRow.appendChild(boost); - - if (insertSummary) { - parent_elem.appendChild(eledefElemRow); - } else { - statsTable.appendChild(eledefElemRow); - } - } - - if (!insertSummary) { - //skp - let defRow = document.createElement("div"); - defRow.classList.add("row"); - let defElem = document.createElement("div"); - defElem.classList.add("col"); - defElem.classList.add("text-start"); - defElem.textContent = "Damage Absorbed %:"; - boost = document.createElement("div"); - boost.classList.add("col"); - boost.classList.add("text-end"); - boost.textContent = stats[4][0] + "%"; - defRow.appendChild(defElem); - defRow.appendChild(boost); - statsTable.append(defRow); - - let agiRow = document.createElement("div"); - agiRow.classList.add("row"); - let agiElem = document.createElement("div"); - agiElem.classList.add("col"); - agiElem.classList.add("text-start"); - agiElem.textContent = "Dodge Chance %:"; - boost = document.createElement("div"); - boost.classList.add("col"); - boost.classList.add("text-end"); - boost.textContent = stats[4][1] + "%"; - agiRow.appendChild(agiElem); - agiRow.appendChild(boost); - statsTable.append(agiRow); - } - - if (!insertSummary) { - parent_elem.append(statsTable); - } -} - -function displaysq2PowderSpecials(parent_elem, powderSpecials, build, overall=false) { - parent_elem.textContent = "" - let title = document.createElement("b"); - title.textContent = "Powder Specials"; - parent_elem.appendChild(title); - let specials = powderSpecials.slice(); - let stats = build.statMap; - let expandedStats = new Map(); - //each entry of powderSpecials is [ps, power] - for (special of specials) { - //iterate through the special and display its effects. - let powder_special = document.createElement("p"); - let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); - let specialTitle = document.createElement("p"); - let specialEffects = document.createElement("p"); - specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]); - let effects = special[0]["weaponSpecialEffects"]; - let power = special[1]; - specialTitle.textContent = special[0]["weaponSpecialName"] + " " + Math.floor((power-1)*0.5 + 4) + (power % 2 == 0 ? ".5" : ""); - - if (!overall || powderSpecialStats.indexOf(special[0]) == 2 || powderSpecialStats.indexOf(special[0]) == 3 || powderSpecialStats.indexOf(special[0]) == 4) { - for (const [key,value] of effects) { - let effect = document.createElement("p"); - effect.textContent += key + ": " + value[power-1] + specialSuffixes.get(key); - if(key === "Damage"){ - effect.textContent += elementIcons[powderSpecialStats.indexOf(special[0])]; - } - if(special[0]["weaponSpecialName"] === "Wind Prison" && key === "Damage Boost") { - effect.textContent += " (only 1st hit)"; - } - specialEffects.appendChild(effect); - } - } - powder_special.appendChild(specialTitle); - powder_special.appendChild(specialEffects); - - //if this special is an instant-damage special (Quake, Chain Lightning, Courage Burst), display the damage. - let specialDamage = document.createElement("p"); - // specialDamage.classList.add("item-margin"); - let spells = spell_table["powder"]; - if (powderSpecialStats.indexOf(special[0]) == 0 || powderSpecialStats.indexOf(special[0]) == 1 || powderSpecialStats.indexOf(special[0]) == 3) { //Quake, Chain Lightning, or Courage - let spell = (powderSpecialStats.indexOf(special[0]) == 3 ? spells[2] : spells[powderSpecialStats.indexOf(special[0])]); - let part = spell["parts"][0]; - let _results = calculateSpellDamage(stats, part.conversion, - stats.get("mdRaw"), stats.get("mdPct"), - 0, build.weapon, build.total_skillpoints, build.damageMultiplier * ((part.multiplier[power-1] / 100)));//part.multiplier[power] / 100 - - let critChance = skillPointsToPercentage(build.total_skillpoints[1]); - let save_damages = []; - - let totalDamNormal = _results[0]; - let totalDamCrit = _results[1]; - let results = _results[2]; - for (let i = 0; i < 6; ++i) { - for (let j in results[i]) { - results[i][j] = results[i][j].toFixed(2); - } - } - let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0; - let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; - let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; - - let averageWrap = document.createElement("p"); - let averageLabel = document.createElement("span"); - averageLabel.textContent = "Average: "; - - let averageLabelDmg = document.createElement("span"); - averageLabelDmg.classList.add("Damage"); - averageLabelDmg.textContent = averageDamage.toFixed(2); - - averageWrap.appendChild(averageLabel); - averageWrap.appendChild(averageLabelDmg); - specialDamage.appendChild(averageWrap); - - if (!overall) { - let nonCritLabel = document.createElement("p"); - nonCritLabel.textContent = "Non-Crit Average: "+nonCritAverage.toFixed(2); - nonCritLabel.classList.add("damageSubtitle"); - nonCritLabel.classList.add("item-margin"); - specialDamage.append(nonCritLabel); - - for (let i = 0; i < 6; i++){ - if (results[i][1] > 0){ - let p = document.createElement("p"); - p.classList.add("damagep"); - p.classList.add(damageClasses[i]); - p.textContent = results[i][0]+"-"+results[i][1]; - specialDamage.append(p); - } - } - let normalDamage = document.createElement("p"); - normalDamage.textContent = "Total: " + totalDamNormal[0].toFixed(2) + "-" + totalDamNormal[1].toFixed(2); - normalDamage.classList.add("itemp"); - specialDamage.append(normalDamage); - - let nonCritChanceLabel = document.createElement("p"); - nonCritChanceLabel.textContent = "Non-Crit Chance: " + ((1-critChance)*100).toFixed(2) + "%"; - specialDamage.append(nonCritChanceLabel); - - let critLabel = document.createElement("p"); - critLabel.textContent = "Crit Average: "+critAverage.toFixed(2); - critLabel.classList.add("damageSubtitle"); - critLabel.classList.add("item-margin"); - - specialDamage.append(critLabel); - for (let i = 0; i < 6; i++){ - if (results[i][1] > 0){ - let p = document.createElement("p"); - p.classList.add("damagep"); - p.classList.add(damageClasses[i]); - p.textContent = results[i][2]+"-"+results[i][3]; - specialDamage.append(p); - } - } - let critDamage = document.createElement("p"); - critDamage.textContent = "Total: " + totalDamCrit[0].toFixed(2) + "-" + totalDamCrit[1].toFixed(2); - critDamage.classList.add("itemp"); - specialDamage.append(critDamage); - - let critChanceLabel = document.createElement("p"); - critChanceLabel.textContent = "Crit Chance: " + (critChance*100).toFixed(2) + "%"; - specialDamage.append(critChanceLabel); - - save_damages.push(averageDamage); - } - - powder_special.append(specialDamage); - } - - parent_elem.appendChild(powder_special); - } -} - -function displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx, weapon) { - parent_elem.textContent = ""; - - - let tooltip; let tooltiptext; - const stats = build.statMap; - let title_elem = document.createElement("p"); - - overallparent_elem.textContent = ""; - let title_elemavg = document.createElement("b"); - - if (spellIdx != 0) { - let first = document.createElement("span"); - first.textContent = spell.title + " ("; - title_elem.appendChild(first.cloneNode(true)); //cloneNode is needed here. - title_elemavg.appendChild(first); - - let second = document.createElement("span"); - second.textContent = build.getSpellCost(spellIdx, spell.cost); - second.classList.add("Mana"); - - let int_redux = skillPointsToPercentage(build.total_skillpoints[2]).toFixed(2); - let spPct_redux = (build.statMap.get("spPct" + spellIdx)/100).toFixed(2); - let spRaw_redux = (build.statMap.get("spRaw" + spellIdx)).toFixed(2); - spPct_redux >= 0 ? spPct_redux = "+ " + spPct_redux : spPct_redux = "- " + Math.abs(spPct_redux); - spRaw_redux >= 0 ? spRaw_redux = "+ " + spRaw_redux : spRaw_redux = "- " + Math.abs(spRaw_redux); - - // tooltiptext = `= max(1, floor((ceil(${spell.cost} * (1 - ${int_redux})) ${spRaw_redux}) * (1 ${spPct_redux})))`; - // tooltip = createTooltip(tooltip, "p", tooltiptext, second, ["spellcostcalc"]); - // second.appendChild(tooltip); - title_elem.appendChild(second.cloneNode(true)); - title_elemavg.appendChild(second); - - - let third = document.createElement("span"); - third.textContent = ") [Base: " + build.getBaseSpellCost(spellIdx, spell.cost) + " ]"; - title_elem.appendChild(third); - let third_summary = document.createElement("span"); - third_summary.textContent = ")"; - title_elemavg.appendChild(third_summary); - } - else { - title_elem.textContent = spell.title; - title_elemavg.textContent = spell.title; - } - - parent_elem.append(title_elem); - overallparent_elem.append(title_elemavg); - - overallparent_elem.append(displaysq2NextCosts(spell, build, weapon)); - - - let critChance = skillPointsToPercentage(build.total_skillpoints[1]); - - let save_damages = []; - - let part_divavg = document.createElement("p"); - overallparent_elem.append(part_divavg); - - let spell_parts; - if (spell.parts) { - spell_parts = spell.parts; - } - else { - spell_parts = spell.variants.DEFAULT; - for (const majorID of stats.get("activeMajorIDs")) { - if (majorID in spell.variants) { - spell_parts = spell.variants[majorID]; - break; - } - } - } - //console.log(spell_parts); - - for (const part of spell_parts) { - let part_div = document.createElement("p"); - parent_elem.append(part_div); - - let subtitle_elem = document.createElement("p"); - subtitle_elem.textContent = part.subtitle; - part_div.append(subtitle_elem); - - if (part.type === "damage") { - //console.log(build.expandedStats); - let _results = calculateSpellDamage(stats, part.conversion, - stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct"), - part.multiplier / 100, weapon, build.total_skillpoints, build.damageMultiplier); - let totalDamNormal = _results[0]; - let totalDamCrit = _results[1]; - let results = _results[2]; - let tooltipinfo = _results[3]; - - for (let i = 0; i < 6; ++i) { - for (let j in results[i]) { - results[i][j] = results[i][j].toFixed(2); - } - } - let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0; - let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; - let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; - - let averageLabel = document.createElement("p"); - averageLabel.textContent = "Average: "+averageDamage.toFixed(2); - tooltiptext = ` = ((1 - ${critChance}) * ${nonCritAverage.toFixed(2)}) + (${critChance} * ${critAverage.toFixed(2)})` - // averageLabel.classList.add("damageSubtitle"); - // tooltip = createTooltip(tooltip, "p", tooltiptext, averageLabel, ["spell-tooltip"]); - part_div.append(averageLabel); - - - if (part.summary == true) { - let overallaverageLabel = document.createElement("p"); - let first = document.createElement("span"); - let second = document.createElement("span"); - first.textContent = part.subtitle + " Average: "; - second.textContent = averageDamage.toFixed(2); - overallaverageLabel.appendChild(first); - overallaverageLabel.appendChild(second); - // tooltip = createTooltip(tooltip, "p", tooltiptext, overallaverageLabel, ["spell-tooltip", "summary-tooltip"]); - second.classList.add("Damage"); - part_divavg.append(overallaverageLabel); - } - - function _damage_display(label_text, average, result_idx) { - let label = document.createElement("p"); - label.textContent = label_text+average.toFixed(2); - part_div.append(label); - - let arrmin = []; - let arrmax = []; - for (let i = 0; i < 6; i++){ - if (results[i][1] != 0){ - let p = document.createElement("p"); - p.classList.add(damageClasses[i]); - p.textContent = results[i][result_idx] + " \u2013 " + results[i][result_idx + 1]; - arrmin.push(results[i][result_idx]); - arrmax.push(results[i][result_idx + 1]); - part_div.append(p); - } - } - tooltiptext = ` = ((${arrmin.join(" + ")}) + (${arrmax.join(" + ")})) / 2`; - // tooltip = createTooltip(tooltip, "p", tooltiptext, label, ["spell-tooltip"]); - } - _damage_display("Non-Crit Average: ", nonCritAverage, 0); - _damage_display("Crit Average: ", critAverage, 2); - - save_damages.push(averageDamage); - } else if (part.type === "heal") { - let heal_amount = (part.strength * build.getDefenseStats()[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2); - tooltiptext = ` = ${part.strength} * ${build.getDefenseStats()[0]} * max(0.5, min(1.75, 1 + 0.5 * ${stats.get("wDamPct")/100}))`; - let healLabel = document.createElement("p"); - healLabel.textContent = heal_amount; - // healLabel.classList.add("damagep"); - // tooltip = createTooltip(tooltip, "p", tooltiptext, healLabel, ["spell-tooltip"]); - part_div.append(healLabel); - if (part.summary == true) { - let overallhealLabel = document.createElement("p"); - let first = document.createElement("span"); - let second = document.createElement("span"); - first.textContent = part.subtitle + ": "; - second.textContent = heal_amount; - overallhealLabel.appendChild(first); - second.classList.add("Set"); - overallhealLabel.appendChild(second); - part_divavg.append(overallhealLabel); - } - } else if (part.type === "total") { - let total_damage = 0; - tooltiptext = ""; - for (let i in part.factors) { - total_damage += save_damages[i] * part.factors[i]; - } - - let dmgarr = part.factors.slice(); - dmgarr = dmgarr.map(x => "(" + x + " * " + save_damages[dmgarr.indexOf(x)].toFixed(2) + ")"); - tooltiptext = " = " + dmgarr.join(" + "); - - - let averageLabel = document.createElement("p"); - averageLabel.textContent = "Average: "+total_damage.toFixed(2); - averageLabel.classList.add("damageSubtitle"); - tooltip = createTooltip(tooltip, "p", tooltiptext, averageLabel, ["spell-tooltip"]); - part_div.append(averageLabel); - - let overallaverageLabel = document.createElement("p"); - let overallaverageLabelFirst = document.createElement("span"); - let overallaverageLabelSecond = document.createElement("span"); - overallaverageLabelFirst.textContent = "Average: "; - overallaverageLabelSecond.textContent = total_damage.toFixed(2); - overallaverageLabelSecond.classList.add("Damage"); - - - overallaverageLabel.appendChild(overallaverageLabelFirst); - overallaverageLabel.appendChild(overallaverageLabelSecond); - part_divavg.append(overallaverageLabel); - } - } - - //up and down arrow - done ugly - let arrow = document.createElement("img"); - arrow.id = "arrow_" + overallparent_elem.id; - arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem"; - arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png"; - overallparent_elem.appendChild(arrow); -} - -function displaysq2EquipOrder(parent_elem, buildOrder){ - parent_elem.textContent = ""; - const order = buildOrder.slice(); - let title_elem = document.createElement("b"); - title_elem.textContent = "Equip order "; - title_elem.classList.add("Normal", "text-center"); - parent_elem.append(title_elem); - for (const item of order) { - let p_elem = document.createElement("b"); - p_elem.textContent = item.get("displayName"); - parent_elem.append(p_elem); - } -} - -function displaysq2NextCosts(spell, build, weapon) { - let int = build.total_skillpoints[2]; - let spells = spell_table[weapon.get("type")]; - - let row = document.createElement("div"); - row.classList.add("spellcost-tooltip"); - let init_cost = document.createElement("b"); - init_cost.textContent = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost); - init_cost.classList.add("Mana"); - let arrow = document.createElement("b"); - arrow.textContent = "\u279C"; - let next_cost = document.createElement("b"); - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1); - next_cost.classList.add("Mana"); - let int_needed = document.createElement("b"); - if (init_cost.textContent === "1") { - int_needed.textContent = ": n/a (+0)"; - }else { //do math - let target = build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) - 1; - let needed = int; - let noUpdate = false; - //forgive me... I couldn't inverse ceil, floor, and max. - while (build.getSpellCost(spells.indexOf(spell) + 1, spell.cost) > target) { - if(needed > 150) { - noUpdate = true; - break; - } - needed++; - build.total_skillpoints[2] = needed; - } - let missing = needed - int; - //in rare circumstances, the next spell cost can jump. - if (noUpdate) { - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)-1); - }else { - next_cost.textContent = (init_cost.textContent === "1" ? 1 : build.getSpellCost(spells.indexOf(spell) + 1, spell.cost)); - } - - - build.total_skillpoints[2] = int;//forgive me pt 2 - int_needed.textContent = ": " + (needed > 150 ? ">150" : needed) + " int (+" + (needed > 150 ? "n/a" : missing) + ")"; - } - - // row.appendChild(init_cost); - row.appendChild(arrow); - row.appendChild(next_cost); - row.appendChild(int_needed); - return row; -} - -function apply_sq2_elemental_format(p_elem, id, suffix) { - suffix = (typeof suffix !== 'undefined') ? suffix : ""; - // THIS IS SO JANK BUT IM TOO LAZY TO FIX IT TODO - let parts = idPrefixes[id].split(/ (.*)/); - let element_prefix = parts[0]; - let desc = parts[1]; - let i_elem = document.createElement('span'); - i_elem.classList.add(element_prefix); - i_elem.textContent = element_prefix; - p_elem.appendChild(i_elem); - - let i_elem2 = document.createElement('span'); - i_elem2.textContent = " " + desc + suffix; - p_elem.appendChild(i_elem2); -} - -function displaysq2SetBonuses(parent_id,build) { - setHTML(parent_id, ""); - let parent_div = document.getElementById(parent_id); - - let set_summary_elem = document.createElement('p'); - set_summary_elem.classList.add('text-center'); - set_summary_elem.textContent = "Set Bonuses"; - parent_div.append(set_summary_elem); - - for (const [setName, count] of build.activeSetCounts) { - const active_set = sets[setName]; - if (active_set["hidden"]) { continue; } - - let set_elem = document.createElement('p'); - set_elem.id = "set-"+setName; - set_summary_elem.append(set_elem); - - const bonus = active_set.bonuses[count-1]; - let mock_item = new Map(); - mock_item.set("fixID", true); - mock_item.set("displayName", setName+" Set: "+count+"/"+sets[setName].items.length); - let mock_minRolls = new Map(); - let mock_maxRolls = new Map(); - mock_item.set("minRolls", mock_minRolls); - mock_item.set("maxRolls", mock_maxRolls); - for (const id in bonus) { - if (rolledIDs.includes(id)) { - mock_minRolls.set(id, bonus[id]); - mock_maxRolls.set(id, bonus[id]); - } - else { - mock_item.set(id, bonus[id]); - } - } - mock_item.set("powders", []); - displaysq2ExpandedItem(mock_item, set_elem.id); - console.log(mock_item); - } -} - -function toggle_plus_minus(elem_id) { - let elem = document.getElementById(elem_id); - if (elem.classList.contains("plus_minus")) { - if (elem.textContent == "\u2795") { - elem.textContent = "\u2796"; - } else if (elem.textContent == "\u2796"){ - elem.textContent = "\u2795"; - } else { - // ???? - } - } -} - -// updates the powders within this element. -function updatePowders(elem_id) { - // let elem = document.getElementById(elem_id); - // for (let i = 0; i < elem.value.length - 1; i++) { - // if ('etwfa'.includes(elem.value.charAt(i)) && elem.value.charAt(i + 1) >= '1' && elem.value.charAt(i + 1) <= '6') { - // elem.value = elem.value.substring(0, i) + powder_chars['etwfa'.indexOf(elem.value.charAt(i))] + elem.value.substring(i + 2); - // } - // } -} - -/* -* Displays stats about a recipe that are NOT displayed in the craft stats. -* Includes: mat name and amounts, ingred names in an "array" with ingred effectiveness -*/ -function displaysq2RecipeStats(craft, parent_id) { - let elem = document.getElementById(parent_id); - if (!elem.classList.contains("col")) { - elem.classList.add("col"); - } - - //local vars - elem.textContent = ""; - recipe = craft["recipe"]; - mat_tiers = craft["mat_tiers"]; - ingreds = []; - for (const n of craft["ingreds"]) { - ingreds.push(n.get("name")); - } - let effectiveness = craft["statMap"].get("ingredEffectiveness"); - - let title = document.createElement("div"); - title.classList.add("row", "box-title", "fw-bold", "justify-content-center"); - title.textContent = "Recipe Stats"; - elem.appendChild(title); - - let mats = document.createElement("div"); - mats.classList.add("row"); - mats.textContent = "Crafting Materials: "; - elem.appendChild(mats); - - for (let i = 0; i < 2; i++) { - let tier = mat_tiers[i]; - let row = document.createElement("div"); - row.classList.add("row", "px-0", "mx-0"); - let b = document.createElement("div"); - let mat = recipe.get("materials")[i]; - b.textContent = "- " + mat.get("amount") + "x " + mat.get("item").split(" ").slice(1).join(" "); - b.classList.add("col"); - row.appendChild(b); - - let starsB = document.createElement("div"); - starsB.classList.add("T1-bracket", "col-auto", "px-0"); - starsB.textContent = "["; - row.appendChild(starsB); - for(let j = 0; j < 3; j ++) { - let star = document.createElement("div"); - star.classList.add("col-auto", "px-0"); - star.textContent = "\u272B"; - if(j < tier) { - star.classList.add("T1"); - } else { - star.classList.add("T0"); - } - row.append(star); - } - let starsE = document.createElement("div"); - starsE.classList.add("T1-bracket", "col-auto", "px-0"); - starsE.textContent = "]"; - row.appendChild(starsE); - - elem.appendChild(row); - } - - let ingredTable = document.createElement("div"); - ingredTable.classList.add("row"); - - for (let i = 0; i < 3; i++) { - let row = document.createElement("div"); - row.classList.add("row", "g-1", "justify-content-center"); - - - for (let j = 0; j < 2; j++) { - if (j == 1) { - let spacer = document.createElement("div"); - spacer.classList.add("col-1"); - row.appendChild(spacer); - } - let ingredName = ingreds[2 * i + j]; - let col = document.createElement("div"); - col.classList.add("col-5", "rounded", "dark-6", "border", "border-3", "dark-shadow"); - - let temp_row = document.createElement("div"); - temp_row.classList.add("row"); - col.appendChild(temp_row); - - let ingred_div = document.createElement("div"); - ingred_div.classList.add("col"); - ingred_div.textContent = ingredName; - temp_row.appendChild(ingred_div); - - let eff_div = document.createElement("div"); - eff_div.classList.add("col-auto"); - let e = effectiveness[2 * i + j]; - if (e > 0) { - eff_div.classList.add("positive"); - } else if (e < 0) { - eff_div.classList.add("negative"); - } - eff_div.textContent = "[" + e + "%]"; - - temp_row.appendChild(eff_div); - - row.appendChild(col); - } - ingredTable.appendChild(row); - } - elem.appendChild(ingredTable); -} - -/* -* Displays an ingredient in item format. -* However, an ingredient is too far from a normal item to display as one. -*/ -function displaysq2ExpandedIngredient(ingred, parent_id) { - let parent_elem = document.getElementById(parent_id); - parent_elem.textContent = ""; - - let item_order = [ - "dura", - "strReq", - "dexReq", - "intReq", - "defReq", - "agiReq" - ] - let consumable_order = [ - "dura", - "charges" - ] - let posMods_order = [ - "above", - "under", - "left", - "right", - "touching", - "notTouching" - ]; - let id_display_order = [ - "eDefPct", - "tDefPct", - "wDefPct", - "fDefPct", - "aDefPct", - "eDamPct", - "tDamPct", - "wDamPct", - "fDamPct", - "aDamPct", - "str", - "dex", - "int", - "agi", - "def", - "hpBonus", - "mr", - "ms", - "ls", - "hprRaw", - "hprPct", - "sdRaw", - "sdPct", - "mdRaw", - "mdPct", - "xpb", - "lb", - "lq", - "ref", - "thorns", - "expd", - "spd", - "atkTier", - "poison", - "spRegen", - "eSteal", - "spRaw1", - "spRaw2", - "spRaw3", - "spRaw4", - "spPct1", - "spPct2", - "spPct3", - "spPct4", - "jh", - "sprint", - "sprintReg", - "gXp", - "gSpd", - ]; - let active_elem; - let elemental_format = false; - let style; - for (const command of sq2_ing_display_order) { - if (command.charAt(0) === "!") { - // TODO: This is sooo incredibly janky..... - if (command === "!elemental") { - elemental_format = !elemental_format; - } - else if (command === "!spacer") { - let spacer = document.createElement('div'); - spacer.classList.add("row", "my-2"); - parent_elem.appendChild(spacer); - continue; - } - } else { - let div = document.createElement("div"); - div.classList.add("row"); - if (command === "displayName") { - div.classList.add("box-title"); - let title_elem = document.createElement("div"); - title_elem.classList.add("col-auto", "justify-content-center", "pr-1"); - title_elem.textContent = ingred.get("displayName"); - div.appendChild(title_elem); - - let tier = ingred.get("tier"); //tier in [0,3] - let begin = document.createElement("b"); - begin.classList.add("T"+tier+"-bracket", "col-auto", "px-0"); - begin.textContent = "["; - div.appendChild(begin); - - for (let i = 0; i < 3; i++) { - let tier_elem = document.createElement("b"); - if (i < tier) { - tier_elem.classList.add("T"+tier); - } else { - tier_elem.classList.add("T0"); - } - tier_elem.classList.add("px-0", "col-auto"); - tier_elem.textContent = "\u272B"; - div.appendChild(tier_elem); - } - let end = document.createElement("b"); - end.classList.add("T"+tier+"-bracket", "px-0", "col-auto"); - end.textContent = "]"; - div.appendChild(end); - }else if (command === "lvl") { - div.textContent = "Crafting Lvl Min: " + ingred.get("lvl"); - }else if (command === "posMods") { - for (const [key,value] of ingred.get("posMods")) { - let posModRow = document.createElement("div"); - posModRow.classList.add("row"); - if (value != 0) { - let posMod = document.createElement("div"); - posMod.classList.add("col-auto"); - posMod.textContent = posModPrefixes[key]; - posModRow.appendChild(posMod); - - let val = document.createElement("div"); - val.classList.add("col-auto", "px-0"); - val.textContent = value + posModSuffixes[key]; - if(value > 0) { - val.classList.add("positive"); - } else { - val.classList.add("negative"); - } - posModRow.appendChild(val); - div.appendChild(posModRow); - } - } - } else if (command === "itemIDs") { //dura, reqs - for (const [key,value] of ingred.get("itemIDs")) { - let idRow = document.createElement("div"); - idRow.classList.add("row"); - if (value != 0) { - let title = document.createElement("div"); - title.classList.add("col-auto"); - title.textContent = itemIDPrefixes[key]; - idRow.appendChild(title); - } - let desc = document.createElement("div"); - desc.classList.add("col-auto"); - if(value > 0) { - if(key !== "dura") { - desc.classList.add("negative"); - } else{ - desc.classList.add("positive"); - } - desc.textContent = "+"+value; - } else if (value < 0){ - if(key !== "dura") { - desc.classList.add("positive"); - } else{ - desc.classList.add("negative"); - } - desc.textContent = value; - } - if(value != 0){ - idRow.appendChild(desc); - } - div.appendChild(idRow); - } - } else if (command === "consumableIDs") { //dura, charges - for (const [key,value] of ingred.get("consumableIDs")) { - let idRow = document.createElement("div"); - idRow.classList.add("row"); - if (value != 0) { - let title = document.createElement("div"); - title.classList.add("col-auto"); - title.textContent = consumableIDPrefixes[key]; - idRow.appendChild(title); - } - let desc = document.createElement("div"); - desc.classList.add("col-auto"); - if(value > 0) { - desc.classList.add("positive"); - desc.textContent = "+"+value; - } else if (value < 0){ - desc.classList.add("negative"); - desc.textContent = value; - } - if(value != 0){ - idRow.appendChild(desc); - let suffix = document.createElement("div"); - suffix.classList.add("col-auto"); - suffix.textContent = consumableIDSuffixes[key]; - idRow.appendChild(suffix); - } - div.appendChild(idRow); - } - }else if (command === "skills") { - let row = document.createElement("div"); - row.classList.add("row"); - let title = document.createElement("div"); - title.classList.add("row"); - title.textContent = "Used in:"; - row.appendChild(title); - for(const skill of ingred.get("skills")) { - let skill_div = document.createElement("div"); - skill_div.classList.add("row"); - skill_div.textContent = skill.charAt(0) + skill.substring(1).toLowerCase(); - row.appendChild(skill_div); - } - div.appendChild(row); - } else if (command === "ids") { //warp - for (let [key,value] of ingred.get("ids").get("maxRolls")) { - if (value !== undefined && value != 0) { - let row = displaysq2RolledID(ingred.get("ids"), key, elemental_format); - row.classList.remove("col"); - row.classList.remove("col-12"); - div.appendChild(row); - } - } - } else {//this shouldn't be happening - } - - parent_elem.appendChild(div); - } - } -} - -//TODO: translate the below to BS - -/** Displays Additional Info for - * - * @param {String} elemID - the parent element's id - * @param {Map} item - the statMap of the item - * @returns - */ -function displaysq2AdditionalInfo(elemID, item) { - let parent_elem = document.getElementById(elemID); - - let title = document.createElement("div"); - title.classList.add("big-title", "justify-content-center"); - title.textContent = "Additional Info"; - parent_elem.appendChild(title); - - let droptype_elem = document.createElement("div"); - droptype_elem.classList.add("row"); - droptype_elem.textContent = "Drop type: " + (item.has("drop") ? item.get("drop"): "NEVER"); - parent_elem.appendChild(droptype_elem); - - let warning_elem = document.createElement("div"); - warning_elem.classList.add("row"); - warning_elem.textContent = "This page is incomplete. Will work on it later."; - parent_elem.appendChild(warning_elem); - - return; -} - -/** Displays the ID costs of an item - * - * @param {String} elemID - the id of the parent element. - * @param {Map} item - the statMap of an item. - */ - function displaysq2IDCosts(elemID, item) { - let parent_elem = document.getElementById(elemID); - let tier = item.get("tier"); - if ( (item.has("fixID") && item.get("fixID")) || ["Normal","Crafted","Custom","none", " ",].includes(item.get("tier"))) { - return; - } else { - /** Returns the number of inventory slots minimum an amount of emeralds would take up + the configuration of doing so. - * Returns an array of [invSpace, E, EB, LE, Stx LE] - * - * @param {number} ems - the total numerical value of emeralds to compact. - */ - function emsToInvSpace(ems) { - let stx = Math.floor(ems/262144); - ems -= stx*4096*64; - let LE = Math.floor(ems/4096); - ems -= LE*4096; - let EB = Math.floor(ems/64); - ems -= EB*64; - let e = ems; - return [ stx + Math.ceil(LE/64) + Math.ceil(EB/64) + Math.ceil(e/64) , e, EB, LE, stx]; - } - /** - * - * @param {String} tier - item tier - * @param {Number} lvl - item level - */ - function getIDCost(tier, lvl) { - switch (tier) { - case "Unique": - return Math.round(0.5*lvl + 3); - case "Rare": - return Math.round(1.2*lvl + 8); - case "Legendary": - return Math.round(4.5*lvl + 12); - case "Fabled": - return Math.round(12*lvl + 26); - case "Mythic": - return Math.round(18*lvl + 90); - case "Set": - return Math.round(1.5*lvl + 8) - default: - return -1; - } - } - - parent_elem.style = "display: visible"; - let lvl = item.get("lvl"); - if (typeof(lvl) === "string") { lvl = parseFloat(lvl); } - - let title_elem = document.createElement("div"); - title_elem.classList.add("big-title", "justify-content-center", "Set"); - title_elem.textContent = "Identification Costs"; - parent_elem.appendChild(title_elem); - - let grid_item = document.createElement("div"); - grid_item.classList.add("row", "g-3"); - parent_elem.appendChild(grid_item); - - let IDcost = getIDCost(tier, lvl); - let initIDcost = IDcost; - let invSpace = emsToInvSpace(IDcost); - let rerolls = 0; - - while(invSpace[0] <= 28 && IDcost > 0) { - let container_container = document.createElement("div"); - container_container.classList.add("col-lg-3", "col-sm-12"); - - let container = document.createElement("div"); - container.classList.add("col", "rounded", "border", "border-dark", "border-2"); - - container_container.appendChild(container); - - let container_title = document.createElement("div"); - container_title.classList.add("row", "box-title", "justify-content-center"); - - if (rerolls == 0) { - container_title.textContent = "Initial ID Cost: "; - } else { - container_title.textContent = "Reroll to [" + (rerolls+1) + "] Cost:"; - } - container.appendChild(container_title); - let total_cost_container = document.createElement("div"); - total_cost_container.classList.add("row"); - let total_cost_number = document.createElement("b"); - total_cost_number.classList.add("Set", "fw-bold", "col-6", "text-end"); - total_cost_number.textContent = IDcost + " "; - let total_cost_suffix = document.createElement("div"); - total_cost_suffix.classList.add("col-6", "text-start"); - total_cost_suffix.textContent = "emeralds." - total_cost_container.appendChild(total_cost_number); - total_cost_container.appendChild(total_cost_suffix); - container.appendChild(total_cost_container); - - let OR = document.createElement("div"); - OR.classList.add("row"); - container.appendChild(OR); - let OR_text = document.createElement("div"); - OR_text.classList.add("col", "text-center"); - OR_text.textContent = "OR"; - OR.appendChild(OR_text); - - let esuffixes = ["", "emeralds.", "EB.", "LE.", "stacks of LE."]; - for (let i = 4; i > 0; i--) { - let n_container = document.createElement("div"); - n_container.classList.add("row"); - let n_number = document.createElement("b"); - n_number.classList.add("Set", "fw-bold", "col-6", "text-end"); - n_number.textContent = invSpace[i] + " "; - let n_suffix = document.createElement("div"); - n_suffix.classList.add("col-6", "text-start"); - n_suffix.textContent = esuffixes[i]; - n_container.appendChild(n_number); - n_container.appendChild(n_suffix); - container.appendChild(n_container); - } - grid_item.appendChild(container_container); - - rerolls += 1; - IDcost = Math.round(initIDcost * (5 ** rerolls)); - invSpace = emsToInvSpace(IDcost); - } - } -} - -/** Displays all set bonuses (0/n, 1/n, ... n/n) for a given set - * - * @param {String} parent_id - id of the parent element - * @param {String} setName - the name of the set - */ - function displaysq2AllSetBonuses(parent_id, setName) { - let parent_elem = document.getElementById(parent_id); - parent_elem.style.display = ""; - let set = sets[setName]; - let title_elem = document.createElement("div"); - title_elem.textContent = setName + " Set Bonuses"; - title_elem.classList.add("Set", "big-title", "justify-content-center"); - parent_elem.appendChild(title_elem); - - let grid_elem = document.createElement("div"); - grid_elem.classList.add("row"); - parent_elem.appendChild(grid_elem); - - for (let i = 0; i < set.items.length; i++) { - - let set_elem = document.createElement('div'); - set_elem.classList.add("col-lg-3", "col-sm-12", "py-2", "my-1"); - grid_elem.appendChild(set_elem); - const bonus = set.bonuses[i]; - - let set_elem_display = document.createElement("div"); - set_elem_display.classList.add("rounded", "col", "g-0", "scaled-font", "border", "border-3", "border-dark", "dark-shadow", "dark-7", "p-3"); - set_elem_display.id = "set-"+setName+"-"+i; - set_elem.appendChild(set_elem_display); - - let mock_item = new Map(); - mock_item.set("fixID", true); - mock_item.set("tier", "Set"); - mock_item.set("displayName", setName+" Set: " + (i+1) + "/"+sets[setName].items.length); - // set_elem.textContent = mock_item.get("displayName"); - let mock_minRolls = new Map(); - let mock_maxRolls = new Map(); - mock_item.set("minRolls", mock_minRolls); - mock_item.set("maxRolls", mock_maxRolls); - for (const id in bonus) { - if (rolledIDs.includes(id)) { - mock_minRolls.set(id, bonus[id]); - mock_maxRolls.set(id, bonus[id]); - } - else { - mock_item.set(id, bonus[id]); - } - } - mock_item.set("powders", []); - displaysq2ExpandedItem(mock_item, set_elem_display.id); - } - -} - -/** Displays the individual probabilities of each possible value of each rollable ID for this item. - * - * @param {String} parent_id the document id of the parent element - * @param {String} item expandedItem object - * @param {String} amp the level of corkian amplifier used. 0 means no amp, 1 means Corkian Amplifier I, etc. [0,3] - */ -function displaysq2IDProbabilities(parent_id, item, amp) { - if (item.has("fixID") && item.get("fixID")) {return} - let parent_elem = document.getElementById(parent_id); - parent_elem.style.display = ""; - parent_elem.innerHTML = ""; - let title_elem = document.createElement("div"); - title_elem.textContent = "Identification Probabilities"; - title_elem.classList.add("row", "Legendary", "big-title", "justify-content-center"); - parent_elem.appendChild(title_elem); - - let disclaimer_elem = document.createElement("div"); - disclaimer_elem.classList.add("row", "justify-content-center"); - disclaimer_elem.textContent = "IDs are rolled on a uniform distribution. A chance of 0% means that either the minimum or maximum possible multiplier must be rolled to get this value." - parent_elem.appendChild(disclaimer_elem); - - let amp_row = document.createElement("div"); - amp_row.classList.add("row", "justify-content-center"); - amp_row.id = "amp_row"; - let amp_text = document.createElement("div"); - amp_text.classList.add("col-lg-2", "col-sm-3"); - amp_text.textContent = "Corkian Amplifier Used: " - amp_row.appendChild(amp_text); - - let amp_1 = document.createElement("button"); - amp_1.classList.add("col-lg-1", "col-sm-3", "border-dark", "text-light", "dark-5", "rounded", "scaled-font"); - amp_1.id = "cork_amp_1"; - amp_1.textContent = "I"; - amp_row.appendChild(amp_1); - let amp_2 = document.createElement("button"); - amp_2.classList.add("col-lg-1", "col-sm-3", "border-dark", "text-light", "dark-5", "rounded", "scaled-font"); - amp_2.id = "cork_amp_2"; - amp_2.textContent = "II"; - amp_row.appendChild(amp_2); - let amp_3 = document.createElement("button"); - amp_3.classList.add("col-lg-1", "col-sm-3", "border-dark", "text-light", "dark-5", "rounded", "scaled-font"); - amp_3.id = "cork_amp_3"; - amp_3.textContent = "III"; - amp_row.appendChild(amp_3); - amp_1.addEventListener("click", (event) => {toggleAmps(1)}); - amp_2.addEventListener("click", (event) => {toggleAmps(2)}); - amp_3.addEventListener("click", (event) => {toggleAmps(3)}); - parent_elem.appendChild(amp_row); - - if (amp != 0) {toggleButton("cork_amp_" + amp)} - - item_name = item.get("displayName"); - for (const [id,val] of Object.entries(itemMap.get(item_name))) { - if (rolledIDs.includes(id)) { - let min = item.get("minRolls").get(id); - let max = item.get("maxRolls").get(id); - - if (min != 0 || max != 0) { - //Apply corkian amps - if (val > 0) { - let base = itemMap.get(item_name)[id]; - if (reversedIDs.includes(id)) {max = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)} - else {min = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)} - } - - let row_elem = document.createElement("div"); - row_elem.classList.add("row"); - parent_elem.appendChild(row_elem); - - let base_and_range = document.createElement("div"); - base_and_range.classList.add("col-lg-4", "col-sm-12"); - - - let base_elem = document.createElement("div"); - let base_val = document.createElement("div"); - base_elem.classList.add("row"); - base_prefix = document.createElement("div"); - base_prefix.classList.add("col-auto"); - base_val.classList.add("col-auto"); - - base_prefix.textContent = idPrefixes[id] + "Base "; - base_val.textContent = val + idSuffixes[id]; - if (val > 0 == !reversedIDs.includes(id)) { - base_val.classList.add("positive"); - } else if (val > 0 == reversedIDs.includes(id)) { - base_val.classList.add("negative"); - } - base_elem.appendChild(base_prefix); - base_elem.appendChild(base_val); - - let range_elem = document.createElement("div"); - range_elem.classList.add("row", "justify-content-center"); - - range_elem.textContent = "[ " + min + idSuffixes[id] + ", " + max + idSuffixes[id] + " ]"; - if ( (min > 0 && max > 0 && !reversedIDs.includes(id)) || (min < 0 && max < 0 && reversedIDs.includes(id)) ) { - range_elem.classList.add("positive"); - } else if ( (min < 0 && max < 0 && !reversedIDs.includes(id)) || (min > 0 && max > 0 && reversedIDs.includes(id)) ) { - range_elem.classList.add("negative"); - } - - base_and_range.appendChild(base_elem); - base_and_range.appendChild(range_elem); - row_elem.appendChild(base_and_range); - - - let pdf_and_cdf = document.createElement("div"); - pdf_and_cdf.classList.add("col-lg-4", "col-sm-12"); - - let pdf_elem = document.createElement("div"); - pdf_elem.id = id + "-pdf"; - let cdf_elem = document.createElement("div"); - cdf_elem.id = id + "-cdf"; - pdf_elem.classList.add("row"); - cdf_elem.classList.add("row"); - - pdf_and_cdf.appendChild(pdf_elem); - pdf_and_cdf.appendChild(cdf_elem); - row_elem.appendChild(pdf_and_cdf); - - let input_sec = document.createElement("div"); - input_sec.classList.add("col-lg-4", "col-sm-12"); - - let title_input_slider = document.createElement("input"); - title_input_slider.classList.add("row"); - title_input_slider.type = "range"; - title_input_slider.id = id+"-slider"; - if (!reversedIDs.includes(id)) { - title_input_slider.step = 1; - title_input_slider.min = `${min}`; - title_input_slider.max = `${max}`; - title_input_slider.value = `${max}`; - } else { - title_input_slider.step = 1; - title_input_slider.min = `${-1*min}`; - title_input_slider.max = `${-1*max}`; - title_input_slider.value = `${-1*max}`; - } - let title_input_textbox = document.createElement("input"); - title_input_textbox.classList.add("row"); - title_input_textbox.type = "text"; - title_input_textbox.value = `${max}`; - title_input_textbox.id = id+"-textbox"; - title_input_textbox.classList.add("rounded", "border", "border-dark", "border-2", "dark-5", "text-light"); - input_sec.appendChild(title_input_slider); - input_sec.appendChild(title_input_textbox); - - row_elem.appendChild(input_sec); - - sq2StringPDF(id, max, val, amp); //val is base roll - sq2StringCDF(id, max, val, amp); //val is base roll - title_input_slider.addEventListener("change", (event) => { - let id_name = event.target.id.split("-")[0]; - let textbox_elem = document.getElementById(id_name+"-textbox"); - - if (reversedIDs.includes(id_name)) { - if (event.target.value < -1*min) { event.target.value = -1*min} - if (event.target.value > -1*max) { event.target.value = -1*max} - sq2StringPDF(id_name, -1*event.target.value, val, amp); //val is base roll - sq2StringCDF(id_name, -1*event.target.value, val, amp); //val is base roll - } else { - if (event.target.value < min) { event.target.value = min} - if (event.target.value > max) { event.target.value = max} - sq2StringPDF(id_name, 1*event.target.value, val, amp); //val is base roll - sq2StringCDF(id_name, 1*event.target.value, val, amp); //val is base roll - } - - if (textbox_elem && textbox_elem.value !== event.target.value) { - if (reversedIDs.includes(id_name)) { - textbox_elem.value = -event.target.value; - } else { - textbox_elem.value = event.target.value; - } - } - - - }); - title_input_textbox.addEventListener("change", (event) => { - let id_name = event.target.id.split("-")[0]; - if (reversedIDs.includes(id_name)) { - if (event.target.value > min) { event.target.value = min} - if (event.target.value < max) { event.target.value = max} - } else { - if (event.target.value < min) { event.target.value = min} - if (event.target.value > max) { event.target.value = max} - } - let slider_elem = document.getElementById(id_name+"-slider"); - if (slider_elem.value !== event.target.value) { - slider_elem.value = -event.target.value; - } - - sq2StringPDF(id_name, 1*event.target.value, val, amp); - sq2StringCDF(id_name, 1*event.target.value, val, amp); - }); - } - } - } -} - -//helper functions. id - the string of the id's name, val - the value of the id, base - the base value of the item for this id -function sq2StringPDF(id,val,base,amp) { - /** [0.3b,1.3b] positive normal - * [1.3b,0.3b] positive reversed - * [1.3b,0.7b] negative normal - * [0.7b,1.3b] negative reversed - * - * [0.3, 1.3] minr, maxr [0.3b, 1.3b] min, max - * the minr/maxr decimal roll that corresponds to val -> minround, maxround - */ - let p; let min; let max; let minr; let maxr; let minround; let maxround; - if (base > 0) { - minr = 0.3 + 0.05*amp; maxr = 1.3; - min = Math.max(1, Math.round(minr*base)); max = Math.max(1, Math.round(maxr*base)); - minround = (min == max) ? (minr) : ( Math.max(minr, (val-0.5) / base) ); - maxround = (min == max) ? (maxr) : ( Math.min(maxr, (val+0.5) / base) ); - } else { - minr = 1.3; maxr = 0.7; - min = Math.min(-1, Math.round(minr*base)); max = Math.min(-1, Math.round(maxr*base)); - minround = (min == max) ? (minr) : ( Math.min(minr, (val-0.5) / base) ); - maxround = (min == max) ? (maxr) : ( Math.max(maxr, (val+0.5) / base) ); - } - - p = Math.abs(maxround-minround)/Math.abs(maxr-minr)*100; - p = p.toFixed(3); - - let div1 = document.createElement("div"); - div1.textContent = "Roll exactly "; - div1.classList.add("col-auto", "px-0"); - let div2 = document.createElement("div"); - div2.textContent = val + idSuffixes[id]; - div2.classList.add("col-auto", "px-1"); - if (val > 0 == !reversedIDs.includes(id)) {div2.classList.add("positive")} - if (val > 0 == reversedIDs.includes(id)) {div2.classList.add("negative")} - let div3 = document.createElement("div"); - div3.textContent = ": " + p + "%"; - div3.classList.add("col-auto", "px-0"); - document.getElementById(id + "-pdf").innerHTML = ""; - document.getElementById(id + "-pdf").appendChild(div1); - document.getElementById(id + "-pdf").appendChild(div2); - document.getElementById(id + "-pdf").appendChild(div3); -} -function sq2StringCDF(id,val,base,amp) { - let p; let min; let max; let minr; let maxr; let minround; let maxround; - if (base > 0) { - minr = 0.3 + 0.05*amp; maxr = 1.3; - min = Math.max(1, Math.round(minr*base)); max = Math.max(1, Math.round(maxr*base)); - minround = (min == max) ? (minr) : ( Math.max(minr, (val-0.5) / base) ); - maxround = (min == max) ? (maxr) : ( Math.min(maxr, (val+0.5) / base) ); - } else { - minr = 1.3; maxr = 0.7; - min = Math.min(-1, Math.round(minr*base)); max = Math.min(-1, Math.round(maxr*base)); - minround = (min == max) ? (minr) : ( Math.min(minr, (val-0.5) / base) ); - maxround = (min == max) ? (maxr) : ( Math.max(maxr, (val+0.5) / base) ); - } - - if (reversedIDs.includes(id)) { - p = Math.abs(minr-maxround)/Math.abs(maxr-minr)*100; - } else { - p = Math.abs(maxr-minround)/Math.abs(maxr-minr)*100; - } - p = p.toFixed(3); - - let div1 = document.createElement("div"); - div1.textContent = "Roll "; - div1.classList.add("col-auto", "px-0"); - let div2 = document.createElement("div"); - div2.textContent = val + idSuffixes[id]; - div2.classList.add("col-auto", "px-1"); - if (val > 0 == !reversedIDs.includes(id)) {div2.classList.add("positive")} - if (val > 0 == reversedIDs.includes(id)) {div2.classList.add("negative")} - let div3 = document.createElement("div"); - div3.textContent= " or better: " + p + "%"; - div3.classList.add("col-auto", "px-0"); - document.getElementById(id + "-cdf").innerHTML = ""; - document.getElementById(id + "-cdf").appendChild(div1); - document.getElementById(id + "-cdf").appendChild(div2); - document.getElementById(id + "-cdf").appendChild(div3); -} diff --git a/js/sq2display_constants.js b/js/sq2display_constants.js deleted file mode 100644 index 6f352fd..0000000 --- a/js/sq2display_constants.js +++ /dev/null @@ -1,191 +0,0 @@ -let powder_chars = [ - '\u2724', - '\u2726', - '\u2749', - '\u2739', - '\u274b' -] -let subscript_nums = [ - '\u2081', - '\u2082', - '\u2083', - '\u2084', - '\u2085', - '\u2086', -] - -let skp_names = [ - 'str', - 'dex', - 'int', - 'def', - 'agi' -] - -let elem_chars = [ - 'e', - 't', - 'w', - 'f', - 'a' -] - -let elem_names = [ - 'earth', - 'thunder', - 'water', - 'fire', - 'air' -] - -let elem_colors = [ - "#00AA00", - "#FFFF55", - "#55FFFF", - "#FF5555", - "#FFFFFF" -] - -let item_types = [ - "Helmet", - "Chestplate", - "Leggings", - "Boots", - "Ring", - "Bracelet", - "Necklace", - "Dagger", - "Spear", - "Wand", - "Relik", - "Bow", - "Potion", - "Scroll", - "Food", - "Weapon Tome", - "Armor Tome", - "Guild Tome" -] - -let tome_types = ['weaponTome', 'armorTome', 'guildTome']; -let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1']; - -/* - * Display commands - */ -let build_all_display_commands = [ - "#defense-stats", - "str", "dex", "int", "def", "agi", - "mr", "ms", - "hprRaw", "hprPct", - "sdRaw", "sdPct", - "mdRaw", "mdPct", - "ref", "thorns", - "ls", - "poison", - "expd", - "spd", - "atkTier", - "!elemental", - "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", - "!elemental", - "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", - "rainbowRaw", - "sprint", "sprintReg", - "jh", - "xpb", "lb", "lq", - "spRegen", - "eSteal", - "gXp", "gSpd", -]; - -let build_offensive_display_commands = [ - "str", "dex", "int", "def", "agi", - "mr", "ms", - "sdRaw", "sdPct", - "mdRaw", "mdPct", - "ref", "thorns", - "ls", - "poison", - "expd", - "spd", - "atkTier", - "rainbowRaw", - "!elemental", - "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", - "!elemental", - "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", -]; - -let build_basic_display_commands = [ - '#defense-stats', - // defense stats [hp, ehp, hpr, ] - // "sPot", // base * atkspd + spell raws - // melee potential - // "mPot", // melee% * (base * atkspd) + melee raws - "mr", "ms", - "ls", - "poison", - "spd", - "atkTier", -] - -let sq2_item_display_commands = [ - "displayName", - "atkSpd", - "!elemental", - "hp", - "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", - "!spacer", - "fDef", "wDef", "aDef", "tDef", "eDef", - "!elemental", - "classReq", - "lvl", - "strReq", "dexReq", "intReq", "defReq","agiReq", - "!spacer", - "str", "dex", "int", "def", "agi", - "hpBonus", - "hprRaw", "hprPct", - "sdRaw", "sdPct", - "mdRaw", "mdPct", - "mr", "ms", - "ref", "thorns", - "ls", - "poison", - "expd", - "spd", - "atkTier", - "!elemental", - "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", - "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", - "!elemental", - "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", - "rainbowRaw", - "sprint", "sprintReg", - "jh", - "xpb", "lb", "lq", - "spRegen", - "eSteal", - "gXp", "gSpd", - "majorIds", - "!spacer", - "slots", - "!spacer", - "set", - "lore", - "quest", - "restrict" -]; - -let sq2_ing_display_order = [ - "displayName", //tier will be displayed w/ name - "!spacer", - "ids", - "!spacer", - "posMods", - "itemIDs", - "consumableIDs", - "!spacer", - "lvl", - "skills", -] \ No newline at end of file From 564e4709cd4f7635ce7d88f8ae90dee6f9df54dc Mon Sep 17 00:00:00 2001 From: reschan Date: Wed, 22 Jun 2022 09:34:06 +0700 Subject: [PATCH 21/30] change clickable arrow to a function --- js/sq2display.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/js/sq2display.js b/js/sq2display.js index 7dd1d27..ef8f77e 100644 --- a/js/sq2display.js +++ b/js/sq2display.js @@ -827,12 +827,7 @@ function displaysq2MeleeDamage(parent_elem, overallparent_elem, meleeStats){ parent_elem.append(critStats); - //up and down arrow - done ugly - let arrow = document.createElement("img"); - arrow.id = "arrow_" + overallparent_elem.id; - arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem"; - arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png"; - overallparent_elem.appendChild(arrow); + addClickableArrow(overallparent_elem); } function displaysq2ArmorStats(build) { @@ -1401,12 +1396,7 @@ function displaysq2SpellDamage(parent_elem, overallparent_elem, build, spell, sp } } - //up and down arrow - done ugly - let arrow = document.createElement("img"); - arrow.id = "arrow_" + overallparent_elem.id; - arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem"; - arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png"; - overallparent_elem.appendChild(arrow); + addClickableArrow(overallparent_elem); } function displaysq2EquipOrder(parent_elem, buildOrder){ @@ -2390,3 +2380,12 @@ function sq2StringCDF(id,val,base,amp) { document.getElementById(id + "-cdf").appendChild(div2); document.getElementById(id + "-cdf").appendChild(div3); } + +function addClickableArrow(elem) { + //up and down arrow - done ugly + let arrow = document.createElement("img"); + arrow.id = "arrow_" + elem.id; + arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem"; + arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png"; + elem.appendChild(arrow); +} From 6620d011e5877ca93be44a93fef78a10ab829da0 Mon Sep 17 00:00:00 2001 From: hppeng Date: Tue, 21 Jun 2022 20:41:40 -0700 Subject: [PATCH 22/30] Fix edit id resetting --- builder/index.html | 2 +- js/builder_graph.js | 35 ++++++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/builder/index.html b/builder/index.html index bc5c7d3..7b4f294 100644 --- a/builder/index.html +++ b/builder/index.html @@ -431,7 +431,7 @@
-
diff --git a/js/builder_graph.js b/js/builder_graph.js index ae36651..688615a 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -758,19 +758,40 @@ class AggregateStatsNode extends ComputeNode { } } +let edit_id_output; +function resetEditableIDs() { + edit_id_output.notify(); +} /** * Set the editble id fields. * * Signature: EditableIDSetterNode(build: Build) => null */ class EditableIDSetterNode extends ComputeNode { - constructor() { super("builder-id-setter"); } + constructor(notify_nodes) { + super("builder-id-setter"); + this.notify_nodes = notify_nodes.slice(); + } compute_func(input_map) { if (input_map.size !== 1) { throw "EditableIDSetterNode accepts exactly one input (build)"; } const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element for (const id of editable_item_fields) { - document.getElementById(id).value = build.statMap.get(id); + const val = build.statMap.get(id); + document.getElementById(id).value = val; + document.getElementById(id+'-base').textContent = 'Original Value: ' + val; + } + } + + notify() { + this.mark_dirty(); + this.update(); + // NOTE: DO NOT merge these loops for performance reasons!!! + for (const node of this.notify_nodes) { + node.mark_dirty(); + } + for (const node of this.notify_nodes) { + node.update(); } } } @@ -784,7 +805,7 @@ class EditableIDSetterNode extends ComputeNode { class SkillPointSetterNode extends ComputeNode { constructor(notify_nodes) { super("builder-skillpoint-setter"); - this.notify_nodes = notify_nodes; + this.notify_nodes = notify_nodes.slice(); } compute_func(input_map) { @@ -889,10 +910,6 @@ function builder_graph_init() { item_nodes[3].link_to(powder_nodes[3], 'powdering'); item_nodes[8].link_to(powder_nodes[4], 'powdering'); - // Edit IDs setter declared up here to set ids so they will be populated by default. - let edit_id_output = new EditableIDSetterNode(); - edit_id_output.link_to(build_node); - // Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage let build_disp_node = new BuildDisplayNode() @@ -911,6 +928,10 @@ function builder_graph_init() { stat_agg_node.link_to(node, field); edit_input_nodes.push(node); } + // Edit IDs setter declared up here to set ids so they will be populated by default. + edit_id_output = new EditableIDSetterNode(edit_input_nodes); // Makes shallow copy of list. + edit_id_output.link_to(build_node); + for (const skp of skp_order) { const elem = document.getElementById(skp+'-skp'); const node = new SumNumberInputNode('builder-'+skp+'-input', elem); From 361d4cca542c94b48b5d3c39e557185c09d2e7d9 Mon Sep 17 00:00:00 2001 From: ferricles Date: Tue, 21 Jun 2022 22:09:54 -0700 Subject: [PATCH 23/30] bruh --- atlas/index.html | 2 +- js/atlas.js | 4 ++++ media/audio/bruh_sound_effect.mp3 | Bin 0 -> 23853 bytes 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 media/audio/bruh_sound_effect.mp3 diff --git a/atlas/index.html b/atlas/index.html index 9275ce7..8bbc658 100644 --- a/atlas/index.html +++ b/atlas/index.html @@ -50,7 +50,7 @@
- +
diff --git a/js/atlas.js b/js/atlas.js index 7724c93..f901935 100644 --- a/js/atlas.js +++ b/js/atlas.js @@ -119,6 +119,10 @@ function runAtlas() { let center = [(at1[0]+at2[0])/2, (at1[1]+at2[1])/2 ]; if (Math.sqrt(((at2[1]+atlas2.vy) - (at1[1]+atlas1.vy))**2 + ((at2[0]+atlas2.vx) - (at1[0]+atlas1.vx))**2) < 2*r) { + //Play bruh sound effect + document.getElementById('bruh_sound_effect').play(); + document.getElementById('bruh_sound_effect').currentTime = 0; + if(Math.sqrt( (at2[1]-at1[1])**2 + (at2[0]-at1[0])**2 ) < 2*r ) {//check for collision //Move both away slightly - correct alg this time :) atlas1.style.left = parseFloat(atlas1.style.left.replace("px","")) + (at1[0]-center[0]) * 2 * r / Math.sqrt(dx**2 + dy**2) + "px"; diff --git a/media/audio/bruh_sound_effect.mp3 b/media/audio/bruh_sound_effect.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..2b245a321d004029cdf9ff02f698d4f31151d6d5 GIT binary patch literal 23853 zcmeFZS6h?M7x$Zl5JCtLAV8>wD&2$*QbKP5LQy&<^xj1fh0wdSfT5`LCMsP~loEQA zE-EUDfPkQaiin!c@4rv>!E>-*!1F!Gon)>n*W7b|X3bi&*2K(E2@Lp8V)k}+MyFSH z0Du8ciuO@aS5Q__z+y1}YyE#;=qHQ+pHKhiw`L(ep{FgUKLZd30Nelo1OkCVp+rPP zq)&y#Vl^~0^iO4OZfwGFtPtU->z{pd@#l@whq~x56LZMVtRMeg7-o1NWU0qL4 zH8L_XGc&V%s<&_7Zf$LSIn~k85uHx|uP6WY`M>M`S#AKVUhD7wbKC#@_WzE+|GyD9 z<=^XU#v|8D_`sfpm`HQK3qhVCYr~;x3Wux%BMKzFf!v<(%5ByRAT$GxxZzV$VY<`2 z!}j-Pg`cVkdmmb47UE6uYW9&MGi1`nzt&}%7ee<-ld9o+MOx$`fZ{Z3d;eg)H1MXV zmq{rH$uoii*K-XXe{P@Tq$!Cz{*gf`|j`t=ag68s8W4tO%U=V5Bn-6!p8TC1ZSp?^7p^h59oJaKNQ&K z`TG1v^y-7I6FU9(8?`@gzJdYTHPD8GG`6vHPtlZy!JSZMCSIiX#0Ro9YXHGK)gRli zzioD;nb4;r*!ixPt`HkFcI-?49EHD~m44Ih$+Vij#xb(iVkM1vUC1qka*$7*1KvBSbZz{~$#3r#A@g zN%kTk@Wg@?zgIjS)c_ywXJO0=-sB)}JjWQ1k1d|hJtO+#Ko;OG4vJu6=E)=*?$krX zQ@9sC2t*fFspsG@n6D`W86qMxWvHHslgu~*fdGKetga>8GZx6e`&83Qqp1c9AgSjr zGQOBvLA{~#mG9g2)4hqFCBM2d$1*ac9`L>L1nScj$5i&cq(8NZVIQ)g3MOlw2e({K zi0a@x{*|I3l(^e>w1F7T?)Vj6Jh(^W@rZ0cZ&n*Wb~e!!zBxB1;NS_~;2 zbvfvR>zSr2`XPIJ1ErNBxbMv2{21$RIj!M@7Qu}MkSvoQrXgScv-`9oC$Inj$S|dT z#p!vb+)@#hv(g`bv*G&GEjmFV>P2(8Hgf-?o~ zdy7D%wG;b>hBgY1mB=4Fzigb&4&DA_B^hZYvN|Q$K+md@qd>1_ zIL7roqITV8?~Ad~t+~C@-J@%bN1iry?=1FnZp;UV*0+7XT-tV)U+!B}u{2xZ{}%3rE&9WdMMhN5cqH+F-_zo@p2Xv#OwV>k>xGK|>G+ zNP5o@OH9LnA=qGc>?biyMJ%@JkX1;Lb73KzK}6J+jZZ3{Tt&kGLEn<}938_bRt$Ec zxLJMLK?P%xcSegKs@fk}^>f*%9$W+O0j)`n%)k1sSx#bE#Js zKk^yW6!bb9&|Z?T3pswK@-O6V$Q|gwCwzK3I*Q{_5N14E^Q}n_OsF9%w3?7= z_O?B~-@diJkac7(g`WjsIaH7mg)(e&*R?lAENIOxUTYo=k#Y2Q06QI|H;qSpt`HKv zDJ|=9v7E(NKNgUp@aX^|r-4ffxO-EI9Ek|S0uESaU4tLAxZ=ODeB&6pIxRC_n{HyD z^J%+BV1;5hW4AahCGsVqz0b>nbJsZVq8CX5$?woTB>kl0#?pfzjRfS2Po_tnee{iz zH_?sJY`hFkt-5;-X|~k>fL@rOJfHppRPt$kM?HnL_)3sWCnM-6|) zjSb@m=RQw{eOmHerwFp5?EyF8)nWhfFAPzabh9q+jSF*yjoE`eLp&_6=vtlHe|)kC z4v}GIfWb*kkkCYe9Eb5&j3Ejx$V5e>X?nP}d{cmqC!s1Obq&!7Mj8U`S`#PDeXG$7 zLh)#-oFKCZp#3uK+(kwU(5MKTpdg)wpBLmqgPDdJGQ3f+_ehaueYn3s$`X$$2$jOt z9OV@0dlqBXp7!upUC&7DvqO-07mr;|2ovPR^YCX};%Q4Y`f5 zE}mXNtUx^!$f?NHhA?Na}D zlc+c6P;@3G1qCaZSrS8pczsgEm#wXT3rjz>8I5(>YAeVfYm^5o)i}A-4}u~qqw0}o z*wm;k)%;&Osozymm1j^sLC_>I5tJDF5RMZ~dOt88A)3T+O?U)qY^IoMV$`niiLBtc z)=Wb!UX%h02f_19lRSY7oMb#s1InF~ls#oAFT{vQYex_ayWMHe3TcE1qy;g^qJ1U05Kw9T-Rkh$~b6L+3m zi(b3>q_=xZ;<*5Kc+j;W4GpK+Q2+HSf7=Qf?mVYFKJM&Abr??`B%Dcm=VUmNE|mCy zMku>yHczuOE5*9%w(p*gUP*hVenG}uMoqzy+!ar$Vyy38Y;;Ez=~rHwE9bcXT532N zJpA>tKuPS%jQakD@%=BYeG{)}RIj=9w|YLR@5UeBd#)0DhME0~)el~XWeqc(;_xdl z!D;cl&XLCxwRhuPOSgWtyxV;E+#muZV*zBU}IlVl kNk*qA8yPEP$!9Ujm*~E`4{6cH%>N8qY0Lx8X?-KRw#@q zHFh;9CnH@2{3i=4FAe04h6#L`d<}kA06-j1g z+vtfD9|I@ZOJ0fZ{wT@4(xNG?%xW&OFP_hy#9I5pZ{0Jmu~jd2na7=uCPRE} zP7vhQln4_8Z@nPv>R&`YK;H(O%K4-!;m7#%2TxJ}3TFp=lQ+xG{s1xQezQtz%c+C; zU(ksQC~kkcTLid9k`nivC3|JSL=c9FMVm8`qW((jPUNs|pD;}iGk)KEw9lfX2U*yY z3iH9VU(<7hIl-A2hooL6Ru-gt{j!H*1TA1J^~zvl7+^#xDKRxXw?8(i8cme)AVibC zmXOBK9<6JhVdBJ$Tl`S=DNf)34k6Bpxdz~70FuL@pic^^2Sg0#B_l&(jDic904WM+ zAwq3Y0w_(;Ls0-40c0)UX4}C!LJUoUa13|hPV+7^`fME+u9%PUX3gZQ=H}JC(mx3d zk2+hljtua3T17BT?OXOy=qt6dkk2>mr0-lOD)H*^R-1+qOT^O5hDG%+1(r z)>F<^g!{5i$f5jrR_R%@9PKc2(E==GK{!Q;aZtoI1A$Cq0g_z%(S$)l&IkxINJNiD z=1%~l918+BIr10`_|gCv0uKfc;%3N^1e%e_riut52-FT&$$W__4ocLs0|0PqLYG?) zfjEhzcnd8?FnQV|`I1!_!=&k2PHDBq>|qUx!P29{tKkB~v2uKKn>ka&)(I4(dYG$_ zv>en$URTP~{pPC?if=ahcDF;eeuOXK{nWQfEww*?t(Ky0_rT*~=JBE59=~4L|D5#z z+vWS?E-NqP;Mbqwdkc>VW%1K*@64zvKfU{oPJK#03hhgf2%1azP5wL*Xw8>Vxu>#^ z>*%}^aF(z1y2x(9!>zc~jj2zvc(D&T6aHuW{)u*!-SCF)Rb^@;@Q=C!y{4$~C~4on zBC^?sp529?Yn=;a&no>aI2Y5*bVjtVQg&cKBUhp)rGL9Hi8;q8{61LY_T$nahOhQM z7_fsoNFY`H?R`jU@UaeA7l*0vwS~Alie|D!&o09c1g<6VHN;!N|0P@O^mhRDXYdAuma^f zG$QWGJ$`#b79!n<@| zOSUESp^p9Z&ZAeGAW>tHwRZ90f0_UQSMMMJNBdw1woc3o>k$t$Q}n#B6O5Z70@e_c zr5OUKP9)zoa2CLr3o=r8z8w4?|9otpYA($R^wPq-)6A-M;jWh$um8{g=g9NU@#K>- z4De~AT8_9d7aRL<@CSB*s1_AiF&*$xwK2wxBq!DhKHK%)*C~||q-4Vu*)t{bhVoQi#!jZpVmT77w^WT;FFKdzx`D5}u}uhcoKg1< z<$#>|WP>}hz-~LX#kpb?`#ZAeAws$n`TZhZpuNO8A?usJ{GLksL;e5HGQ=&}VNxn>S|gNC=@%I8t0qtB++x zE(beN&qPn3}ieNbz z;Xc!rO%K*t-Vc0vn76V?L54o%1^}|c1lie?P*hHD4vi#ytpNjf)N}}k;;ieSzLIK5 z4#*tHelh>6*T#8FH@l2o?$?K@I>O zuO-j(^6K>mjq`IaYDPn^R+qNR^LF@NdX2>(0078+A)t%}JedUubVTNwc2h|g41q9s zbFFVQr^^Pp$;Q(txk_>Hw_!7B+W{6HW%PrGXHsHFI&oUFfNvCrd|a3Zo|xoRCufAw z=U&Xi!UiijlBhWlkN&R!5AhYng_e(Nc5G-$K@~_`T<<*!N+1!KQ^_pYTUR+mD!-bS zGnhh5CZmZP^*Zt13?JsmA{hn|!D(qZV8Hgbgdc1qn11W7CFb9KzU&_-yNLR49AyjRU#9{Mk32R|pPV ze-}4+>8HRj`9XmG<;8**HcJm3FFG@%NEK|nRT#Tly(xC5fR_^_JoYn`>5TlP1(&2t zRLyP6$PMAn*+&=l*^_zc>mh7X^F-=!;V^=aC`H{6ZG)&4sV>_R=Q>@p^{4I{9b%6d zgFoufvqJH<^!eJE)mF z=zmQTyWsP!Zod2T@QJotj2u=(y@V>im8Z)3H00M?A6rGG7`spZ@$Z@zo$j=-8ygo! zlJZjrd%C(*g<4qo?+v6EyeCsirE+|b&VT7g5B`3w^R87FGIzD)CDgE-l`#bXfWcxS ze0+6{8Q810B-?T_zaPUazhC|vy?u7$T3>|(6Z7+Z=h#c)vI2Xe*`|A&R3^s4 z11tiMlH+4plKhJWPopYfc*R>^6WeCrlw(p~L#8^E1T8`c14jW-H_lBdc)92QavOSCL?nZ3W^$p_RrM4{L zEEAqOL4)l@WgF*@Yp+@eQhboYMCrF$F{z57^jm!iXX1t5sJsoHW`M`Jl^p3loGS03 z0LVnMh)FTlXVNkYEaRyN7+lOYp;d&D>){hT!Y2v+`yb-Q@$E4Y)H1$Qv4POJrVzs3 zQrU68M|kdm`r-R>19s=p>i3@A%5FJ2Dk@7bq{rg-o9M*PqN%#`4PAa}AOHYnrw}P| z1sMkI0!o*fV$|gU&#(FQ1DVKmMh!Rn;BWt4qpm!&K9F$K^c8Sex3Kdt?RTcF*;^(MVDek09+sQvX;92O$|%|yddK6Ldv>Z- z)Z=iH6@Z~e{QCrK^A+*U0hhY$*73XItN-y|2-*^Nv3(Lp3nSH`4zxex`AOd9d)s>h z4@&BR724R6+^bNiTt+|+I5{%I;a_V7z{DQkh00xXe61s4tei6@4b}Iv zlrf9>Dh38a%w(piThN1Rrguz$dZL9fKOx(q*vlsSWZSCZy?q=tNyt%fc+}gSB zW$`O^w{|BFyVy~#vnn|Fj8LF?V&@l zuy^l?cJ40sl~T|4FX}uEZz2%jYNVXuMuc8Ee}XtVp9=oQnZm=dqWN5sD@nrhu1nE5 zul}|Q{rNz#N(X{ZXVw-<^SW;Nl?b1URc#9Qvf`T`WZb}Pr3|}1O+*SC6rwXHu7-?jd%*AA}!^k0=tvRfH+;>&N=OmVQ)Wr@fGA8c{@*08)p;}!6HG~S^c@bpd*|&p|MrU*DxE=gdu_BWHMjjUQaS$wm*U-LiHF!lKeD1ULr3; zd5E(*#0-cB^MCw5y>S%eWFt@>7v^uwx8}*>O1$8yAM%BDpsd7qAV(X+FUBy%#e|n* z9byD()+0Z2-%*~6Ag+-;`DJWtcnmQG0D?%y<3icAb1j?>RCN@ZJT!pUFdR*uRxwmp zrs*>>NRkaaXb>LI>h0Z9lh=hK365Dy9-P0JEw&xF&`vG#3I?4enX6yhxmW)-RDgbI zbH3Oh`IrA7`^`6O)VY7C>{4DO9tWR~x+;)OYP&oCDE?W(Bi0u^8Z8n(yJv(XnWdvX zSYD{sZZ%LZCtMjR0d)xEPR`z5T(Jn|6c#%dBNSh^epB+rmvi-B==6;XNGhOZ?D0pvYh27#xb`0xEJaPI#=uz2;D*=sOMsqi5Zc^5>C=YR z=Ncu)#LD?WSBliY>tOxYacUa;yf$-eih&xo#uo4KF0&N3oIi_%*jA- z)mZx-RB7WpkUvMHa=JWMVr_~ovpBURC&+Jw0`w3!+#g6z=5N}*QO}QUE7IaqJTA_Y zHA_$A^Sf%u0z-MA?JaS^dNAmIlLy_*@TM?>*%(<0sQe;Js%6o^ZM~Xj;Ac&>&HTc- zF2lOK9pmQL+(qN}hdum%_U=j;vOtvh13t?~oS&_CU@3l<|7Mw6jl0A;#XN>*l*eehuCi;X$dmC~(uf9bk(`n7pf z`?>7a<)@U$uG^|CnCVhJG>4zD+A~Yd&r?2c>UOHLYq^;C-CL3yd~Y}@=R;H2|4xiS zJekQUWXGyO;Tie4FYyJ_?8j6wG;%ae`Pjq@@CLTPKB?A-W4A*J&{KdK|4{8RC|Dwx(OMln`~#KEH2{I|dV<9{;k zlYztY@+Y(~qIc6*Pj!s|%S5;_t*QG6sGWp-g;tR-tXr^@kO@j{RLsim4X@?K3 zs@?a?QA;+0#S=Hr-fQ{%oDJFB!yz7%d6)^)H7j#tC%NGY!fwR0g!x`g_%NqS|D$=% z;VRC0^txT&pCT0dW3wiZ(Ib|813Oo}U4!OjJ23u=D4b(8f+J=g8cg#w8VUQ5XOZ%`#+HX6_hAMwW+7(Os~(?CZ=cCBNR(7=X(@2{TK$=O6LsM_wxyWcZhm^o_H_x zpflOuV@#6ms=A*jj0dBLP(tx4;;=TJ5CtXRauXK_+)iZ9AU~o_=E9{i{Go<8Keh_f zM8f6fd(H0)$)F8Z7F0(VF>X4#x!XK7@1ZZ8X$1i_<%X_*A4JenTn? zA4@Ag=to~Bk`yexbLe>=w8Srs@xy-lMHM_-r!G-R; z-^2lc{k6(>A~%}tI52p8TX%W6tT2xBmb))J70HI4dz&>I#cBaQj~~>t-R)rO4K=8R z7Ym)?x@UZR)74kr@6fZvt@$N>A||tQRIb~{_u}FYuSxy0>8AEq=#I<_03!yB+d3Mk z5r&Mym~tySs;FfDQXIlzaE#c>4K{(PFlI6U15nX5;?fKdzM__5cZNjCO5@WAm_nqW zGGg?lv#U7yn1LfBX7G&s6g&;8XI8HWAc^kY`;Y%OH$K5G5f)DEzpJ;|f-fA8_P>t% z&;DPdZVvZ~L^z{|g9?(8T;ftk&e`gnrDXVN@|$Kk>}vqpctu|Nd}#+2qq^^@4*@JL zyn5#NWN7O_hA5xw4DC5*2m)N9%}4bQ={WEGDELm_m)&dz*Ie0a4_VwkE;(G~KAgWb z`?A4HCenG){N(e$cho4Nhh+!#|+Z}7{F$kQRd@Yc3J(xMIVzWpcv z&XfzdyET6KcZp8_b{E6VcnDJC!ej%*AgzAbsxya(${_4-QLZZ;c1j`0b;y7@hMh}{ z6&H><;A3E^&cd4W*D#uN$i%HPanXuO@;6L}5#nQ5IU0#Fg^lqW!ipOxN=c~@9tg7O znZM5(Ag3^64LucusxgcHN*Dty(S|{IgW*?xJgJhC=-BJonnKJ_*HR)I88DL95@-FK zFpf-!#P@^Di2$1nG7FCoBP6-C)lUR~1AG;i$HXwOF;1OD!pjSwVxB?)#Z4d;79ORu z@EDN+;1IWeIba%BKT2qB?ZJQXg@;yq=D-1ooHhwZvdS2H;tRZjIL-ExMpSt09J+Vw0M=Jvb4rw@z!;2h% zFixT3PXu%gPESnikyxe7P3tAT!!e_Rc>WXj_{Hv$-ZTC$!%HQwZ7FbH znW5A>Dk3SYo4KIib8&RNGuAUtuHwEJx0L<;_h3Z#P?vs8qB4L;*%4tVS%4lHA&4r2 zv?U@!P`)7l=D9ZNfBf&>*rB*uB)?gR@P$Z|1EpQX<>QpkMamB(P`7d`M8YDOGIRVy zK-m=oolfORcaaE$Zb!z2*%J z7^cv?B3!81eKAMsVg!i5s4uAvP7^qdv%qCjI&4@C!>;!Odihy50ZHPx9#NP2#xYcP zAOr?1Ax0^R!K;&*P%t@i|3EEz41IlYTV$Hy&`nmBBPX-J(1s3p=m)d7N$ zvgVuUZ3#Aw51F%NKf)fl`E{-K(f{q0-8`f(8ZB+nM;rP&9M+`W;1N$2&MnqW_9?|j zFQ3ngE4~5^QTS$5W7FIhyIU|F%~$tCy2SX7t91BCVds*Hg23R_4SF*pkisC^P;Zs; z-T1I@4kh`Ynm)Od6tqNjsYc|5lXf7$)u_ zl4S3H4$}+5c!evvUc^qNE7`wMn!SVVm1dAJMy44P(?;YOHw99S`7sld<+Kt>2%FD2x|;7)+lWEJyu=58PFARQu!I|@xu>K!nor)MKAx<% zq|<*L$~E1iuiSfl{AD+O-({5WQTZ=Mic_R3`cb98mz1)9)m?RkyZIK0$W&`ukzle! zck7_m7t^MIfZY>cL4{~}?c#)D^BT%aJ(nS94v6IP94NJ?VFw*2SqfPrAY*Ilr?*tG zOp%lqO1kCi!%CW&uxgQK5zf?pE~f&wyi}E`cmDI@l*U}c-4+o-&B_GZ7CbB2n#X1kB#n)zu5v_@Q=vmzl|JHb zPF9v;W94v70WCIdpxy#a1;S5*x>imix*guDc>B5-xf6~Qz%*VHgq~{*GfGU>M_1#J z#)ixs_HrHEhTK8PV?p@XLy=5rV>z5kj;~{;suRI=FZC<|a0&ENZf#pqhFRaVYcd~# z=%9>AUCwu8nL*Vj`rbLs!~CvFy;K)}WP;9gY-l?vTCF7?Ty=i++&XckglFAAdU5h> z`{1AI%dfldY|m0#wB4jYw`-FsO4{F>qTMI<{K4hrWVfcQ;eF3{l;&-Y$xL@XK;|LRD9;b*L!e_qGTC5K-0Zrz_)Em_s_) zj~rO_p9|9HE5%g`3|RAbGM59G1lCZcAxJ2A4sE*n)iY3yl}!cP*gu4$K{A*VH#Hei z=m}5k>>8!tgQhN*lvcz*fyW~;Uy&3Hv2~7UGKn= zKa1|s7xyjZW66CJvU1V?o+Sw9Jh^wheZoaT)3xWh_sY;MdWdomU+Lg#8dJ#b0D!g^ zUOZW068tjzaULuG(w(r1GE-Hze!zeBznG2LkT_Y?ON$~&Y07Kox{9_XV*c}gVrrzM z(~DyOSpXF9gsFc|2Qn^h$OsneZzQ1ZGTMf5LTJ@L@=Nl{8Yvg9jm^49xU1{*Fq*p? zr}xQ3O0cBJSe6cb{bWT%hX5t{=M5TSBpHw?##7hf~MLR$8MobC$?f&rOSpm`gUPKFV@GiJspaio%90B6wC zTPs-_FQNYdZ?~F0exv96ikscCvzE_ey1;kfF8mt~WVzrJvNI}p8O7z-bL&d<>*eJ) zD@BpMFh2ypNrX5za{|$iG+};ZZmt)Yn>4qEl<)2nkfA&I)6a6ydu7 zgD{uE8`&7BL(X25xf@eHqCnIv!AB(X22+h8nNVOtZFtcGW&naPYsxx|0LK0e9KWxE z5;udH(WWAA$qHA}M+p(ny6w)ZYGQL~{Zff_@B?_mU^38D$(A8#JR`{>QcZuK>ohBv zUcm$pFy@Q0!w{0aX-41tCr7*mgLgQNV>@b3AJX6LwR05`SIEg8_aaIiv|i}O(f_@P z{*V6@wrzR4(d^a9s7uEF?WE09{?7|rN3zVNl+^>%>%!vV!t8P%{F;s(3s`m;Ew!|G zM-7l)eIk9+yV(6&D}YU11bW?x%dc3H;0GUFaBBs zOXxC;jN|H4)Lp#dXypL;^%RNe^B682ub74(>A8hlULz%;AiQ2$o>P43%p#>6KEZ?A zd2H#>4eB7wTN=QJ^eLbciT!#cv=ma25gk%R&HuJL%Z_Y}b- z`H(20v*%aR9l&NDH{W6i2^?Muf?(N8O=lqNIf*y7BAb7s^q_1RX%e=gjJBv#c*>bpJSNMU);GN zDuT@^hG_LL_vURh#Wl8`4n(LGg>Bb#UpmN3M&hr+^V2+G5$hoN+HDGwl0s>Pc`q3t8!hu)>@@gGiP#M2l!A%5SER1S2T~WCgVL36 z3dJQX%)cK;h7o$*9%^i#?tOiIR2odVC*Q>D_)>OBtIDg^pGEbP^-Vq7CCk>t9z}iP zN%qTUdq1?kT>t=zI5;6{Ou7a_Hm#WNhz?EcW$ZOkE&-CBsx$78jpB=a0O5kq4k*g7 z(sU}P{s1&yKkBdL66JzF$yOAJ8P_l3V6ynlD@a{U@y0Ocni)$=qLe+nxI_~5eRQO$ zn4g|x=EQ*!F;2d!XC8?*N>gK13_?xtW)BVo-b)5!s*Ynau2IOy4`=Xp@QiwssXl`X zD+IvgkN}qfKJHc*B2hk$Zl5c1ySFv=mD%kUggL@41Yap(`%M&ShV4Hs$ z7#2xA7{R!egu+l=(C_mOj>@>!|K-1;*!(_P8*{DBfAt*c!TE2yqU)r2 z6b@wv;>$L=N~FX51bGB9CpV}h`pK{FX)4yXx3kLrouSj4UsHbWDTFQ3H*Egarrn?J zQ(U2Bm35w+ELq2=QM?Av7NVGVSeUFCC{6sQ=^a)kD38kfp}`ieqVT7B*P1977aN_q z6pe=2Y3}^)T!$$e8upcp>~uO`ZmBQ*c$uBki}0#-T&(WRpBet%j<@;}I8P6?j0*Ke zaiYF=etW|dDX7HnWuf0%dq~mOSiV&s8<_9mV(=8eS2)898sL#Q@0%gXbEb4Qj8v6L z9ydM;EPMRh`)r0(ncGJ;K}NF4hhd5sCIdN?bciX>S-zpX21c=Chy)o`r8&dFY%Vn> z{JbU;PmYx_X0EayNR!TkgF2Oo#QC!rQ~RM*TXe<8dZUspn~EO2M8K8ID!j+v7>@@?NICa^?uoV}CXl2^N8taAy`o=Bu;7`!g zYP0gvvYkk<$PM8wW^2?jo8N|061l$3TL5kiEZqc>h#8 zzm6;i8eXu@kDD6w5+Ue?;k+Q0VPPM-AV8a{i$pwvmCqcc<6>tForVSa{4$kH>}QL7 z10th54fI;xbfK~)%s|zxIWXfc4iVyE%K=2KH~t_qCi_0j18%eB zD~C;KPGV}-FnDQs8+GYnp8wZ(Te;KmC&ed@_Dz98j+4jVYX`|=V*m&(5$6H6l8m}2 zcjLBj>*QO2mce=#Uyg!v&8LWz+0!Q z{nF)H?v_3~ch!>;8THvf=cEIkZn}K=(2GVQ8uKZoPg3>LdE`J=1gK%43`pKi{CWy8 z|K%;^HNwMtj~W@wOlz-rk3CDzxpQ9V)FNNG7ph?Qvv5mk!ntCzrLB@53;;`AqH$^s zLlImWTRAY!+O}b)QRYlRQPEKeWuZTJ|6RHK^X}x2S0`I>??#V)Ilj5J_xF8{pwaI> zIsuTsxmT;=JlopYq<`THZ0hNdApX%Za_I!#(uL^+k8XNCuD(&~sQL2RnpC7>eGWub zi7+-;N7K)M+ZHK%*bf%k0omZNi=2`L^7sf|QD>?lycX_=!89VrHa)eN48;bBey`j7uBkjx?% zT5`o?n4OQ*{Hgzc)-o7=KU98zpISrx$sHyTCWc7Mf?%8^A{<3z9ov&KfU1lL;R_;I>U#Cs){*+1Lh5KK&_a$GkyPR^8U0eAv2Zz zM0a#6v@MnH-V{7|ccSiXroc}+N3Tr+S{P@u@fGs;fqU*7ZpJ#5t$d1mG`GCDdDWt< z73K>dw|aSTdd!wKtf81@VbCMy>2J6h=AzzG1kzjv0O7%MiJ984>7sd5y4UPqqJj5n zGB~FigN6*J#sEXWDf^gNz5LrUa-QI(YJa_Y5C{e|2JJX;B#Am=(1IYm^6D_aT{buP z3>aj}WQ2-}BigP77KRI!;glT@pzseb^v|cGpv9u(L~`jlu)PdadbY_UF;5P`&qhrW zNKFeUsXD{J&O-6c5|L@UHlIRhh$SeQ|C#)@?}0T)zb_>tAnvDec?ck069I58Q%p+Z zLO%{A&Te0$T6EoST{&d)uu)v=ZAm?I={4V0Z-VQxZu;Q~{myRLZ%Tn82c84TVG13T z3@J~CLoQv`;{*aSfT?PXE6DcFegNoou}Smg>0F%PkbJ6lVfMZ@b5<5icEM?k=WS{z zb>~aZa$Ix^OGtTwTEap5!-F4Hjx`f8M|3uukCn}Dj^oQlGgw8yX{9kv`Rsf;1N}Gn z6~j}Xpel}MJA_fk-I5iq%n$PcX44q_LL(up zN-7IQ{JM(C23U{`N(nfS^aE9xp<#~#A=%`QL&uXA(1XmZjN%!o6n6d$+AEDD4ptz8 z0f;OD+Q}bGeqG+(?z{B+la-IEqq$9bYCKJz(cv1>Gaabig#anUmRYx8Bof%n!KdShkiP%_U-r9m3HSim9B$dx$mZa+X3JJ z(96naz_%hCpQ0){b?+(Z^c0C+7M0j%GD1AvotFJ4*n9ozOij%tUI#jTF5}HT9PZ6k zjpFRk1EqvxA_{~3^S<4T%)Xf7wB<*f!j$r zHCfhHr7%*FPx<4*_Dii-9XCKqBAksx+^^1;*=ZSg1I`>RsY!DX3!F?vZWhR4VZvl$ zjC#sq>Ig7}WL|n6EddV;c+9S(5n5ji^HJa3&Km-`UAeL$>n9HD)=VJ&EmAY<>r@N zXZkz(gUz&wH_VPoP-#q*46vIe8BCmgJEOb*t$hcO26$d3x4#Z6svpajm-O23?Vbo2 z^(m|Ohxz>O4?7xx8$$K5dO6hkAc*vnN*_ixs_8XFpeO;%MAjp7JfcUHjh^SYQ<(&3iBkzMqJ?S*MR%*l62GYV+1Pn!V1if|@iF22OyEi0q1$ueou@rU+g?lp zFZLaD%~)wa5B^;_H>#{}#-(ALpA?HX@>p=c+*PyKL~zZM)vajLov4>QdXqvX0GI&? zBztAkO2x$y<%q4~bxZDR*_;$aT9{}3f~4aw>#}>&f(|}+Ux|6`>VA3FNB?BfoW&CH zTCw(y&Cl`gf3@^lG*XP)frbwB-_Q#)Xz3wn*jklNI@MIH&Oj;Oj?+eycz6zr6}1!X z(7Y_h8^Ok>fsTaes^aq3I%I<83$S&Tbye-V>Qa~|&DZ$D{GIfZCcuDOY?Ys}oHd3^ z{E{w}`U_B^5%faXA$p_($Z>-aQ6nZS_&9h-BO_ljBo!(%)IV4v&Ll1gc2BE3Wm>2p zf294SsVwd>6mFfWqxjue7-rAwdbtLXZcnK?hrEY3;180bBvzwvI1|bjh!>%M9cvb# zRF;%N$#P9L_9CNg_G!tv2I=CC`s#ZMomx0k3Ah8x>9J1#PjV4YA6HDe z{^@!!E`nD3Ct#=9E)(~C?@j*&GyaRuzSLd&F%@bgIhn)=O=&#qvp0~~r13aeI?TTn z!2R&I5bW^lcb{_M&HxAl00F~k%JQ^i9gK&HBBopXLzQ}5kB_a+UDRE5vc*n^)Nk^Y zAVd9#whU)Tz_ea5mEDs@=Nqd8dLA2P_;E&4+Z%5=QTqWFYmHl1=pP39RllLi?2cMpkW|7SSlp^>U%3iNh{iZNzvLi6In=pXxw}T)s#Z3gI(s{zf0 z7UsX>Z%LdE5i<*gv;MdLDeD>ZBlD5+&75HVlyu`|N&E4umpCW@uQ)#tB2}N+z&$db zk|+Y@Tryr$D*#rTd$ZU-3fuO1U07-|nncz_*e%YH6q;*QyZMpfzNzGZPULs zxBhl5sj#u&{AYPf<~UyV_@QOpbDs0id*Uzj_lAdgx`z9KrU=-F9X}2;>grWz`|iIx zh3sV|OI^NqPJYghyEEp>9hz1|oyj{CzIS3Z6aV>Nar_heTTw-icMaF=yO$?d-Z0W% z21VEQHNNS)>vGSF+Kuz&Oq>w_Ir579Kkb}#TNKRKfR~0Pq*+0PMZjg3?h@$+$)#%% zB%~Vz>F(|ZsTGheX^_rEO1dQlK@`{@&zE@rdgmL=T=#Y6oHKK7PHJ6pxuSC@i3?X* zV&()vdKzehTi@>mKDj`hB@IAQDl8H+YzzZ<*F40{44~y(?st7h>#OceszdUOduh2R zk!d0O8KzY%Sh6pWST$5&)BbRX8iN+VUC5Itjmq@4krHJnuY4b4S6scs=-5a#noN}D z))-d4DVrY=EpGbhk4Zwe*^|rr~ab^`HeGL07HOUeWDdcSSg=u63 zOh(vSuN+ZCscHQ5V)F1yF)A2UiA%F!k5{cRk+;G$<(C9zVkKITS(7_gm2C6s<(-PnVYPdpydE``M%A_l8$c0p3ax*X}-^FK>=#Q=YcZOXwCe>YOl4rncT|w?W@pu_YM5|;?3e7g<#@`++1*C(H z=pDOvU{#L{0E3&Ib7qvYNVoe*0hxpzfx9LoR=b*yvy8X$h0yml#oTvtM|n~%#}A0= zsywHH`Db3*Ume5u|9y1QxIs_Bze{Q0Gry9?`k_+TZiYU>b7Oye5$lhPlw2%T6^2Yn zgSZPt0>-&}CZhZLWEjmif6s;vU_@u)R_#0B%<`7&6H>Da+|T1F;|KcAOS?i_Ga7KdwBaVPN9}=$XA8Xy*`B znhDopME@;g_z!Do^p}Vr4^%ic@JS?_;dXlWfBbV3;K#W4^&9j%8xn#lnado(E=2?! zn^QQp`4`Yf=k{+#RfQA2XZ+97E63F_GXXkAE(S|yBA|J~CCso|_L3KH^> zv{ephs;*Jl)j8qaH$#?2IcwsjHUCwrwc}>NISuGP7sgOpo$0IEyt*sC$P@BA_o8yv zQ%%;Y>KA=PxjJhvg`UKOUyoR-N%&PU1ly?_&-?uZQpz~<6aW3lscY? zm6Oj<@bKtLdA(rP_1HwYtwo=q9}A72zt%7F*17-n#NhP=;$ResTKiTp1%C=^V6~KQ zD>Bb;6Ul))N=fR8JsZ$&pDUe;Ou&FY_X0BvjEczsr%jAap8JxZ57EEHBJN3}2c*YC zWVRakF46sGukUQGg7I`w_vVxUNp3u665GTc$uc(>6|Y5L;5(16xY0PhvSHGJYN%`8 z%(j}mRIqPn`L0iPrAAMlkm2)kcO#t40IeOd1Wf<1@TzGev!Vbq_}*C?ei>byVcg`3 zr?XbpMg#<3G6x^!VGEyupcO&ASOQteBHsYEeK zk$3^-M5BsHIVA^4EF)=S8jd^ZYCNUM ze>pyx*e8<9=eSL+0zAb60PykR`CAtUvMppudp5aMj6Hb_tmX&2{Kr2(2Hv(|-h;e;FtZq+HPS{NUs+xBLhhmxrxWrppu3+uA7WFU?GQHd!?wE(7bpDFNQW0_w`WcQZ8x@zZH^2=S7+ z99P;Jt_Ys!NFglj);taq%Jh({jG|#1dmlW_6GT&%Y7n!X zh~u?y#1CfRR1A0$zVjt?$nwxgc#7VhOGn654rVh_{^^NRW$fb&6x8GON$2Oe#tTWc zAA}h{&9s>X%?|C>x1Dag+|qsWMK&Wg!&n(^_di<|>=$fLIF=#%igsuvoTcnfYybca z^SG20hlOtsmqJC>+RI2}?}Pz_QJoorrIw2us{FYWzg~IA@#dy|wkBI+Sx-tz5L%f< z<2qGl&}4GDrM57)LpvWOt7=WVG;hVd{_|wc9Dp%{)e<6*+!wgb{)7-v{^e)z)eCZ& zx^tZ89yPVd7zEYtgaT$lpX`m9YrA3G&Y9sEA z|7&89pt}@>Q@^Wev`sX-oFgN00$c5of3M17n+Af`IuK@uzMJuAS;6rFPa_sb>XDJX z6b?nNXFdOinvK^kO`NJN*kI-H&2r~Zg81D}(Cl{i@g{w`QV}Y0UrVI?YwW49m@Oc? zX1Z#tc1eg^*7QZLh`U;0>~o3r$mE;4`CqB!DHFa!4QFm^-RJ9K=&{TPzuh-yez%tB zw1PVyru5 z{~j^+;5ou6{kIqf>y<|mUGCh;Y*>lvL8DM6YW{fS87dDT8F#HWAX}N$>+$yW?@fBO zjbrM%kM1nMR`CbHj^sR6Mpzj>`ip=ZE=FMslnKEW7>n^8GWN^+$`o#z z!gh`yHA;HhC%IBMis0M&W`aYc=^oo2>MzC4Fe(98(k1e{*4nB2CFsxWEJ5e8o=ynmfr-t=)rh~c$+n@fe4 zsB*2dr6?PYNW)W`DbDT_xhY@%dssU%nmBXV%yjI4XOdBoy+29f({FT@pVmUtBkUll z+|x{Ra42P%95#SM)!cyZ>c)cjW~1AN~ z_C80(T@)tqWBwmOwYhqp#Umrz>H?9jnSv@jEqBDuoZvFI3Z@H5e3u05Nyarm0JqgT zbD&BtKBIA|0|<7Wc)=Oa8~TAi{++K#8$zdF29NRhi}$yQN`t~;6Jk{Fx5;YN`fnDj zkw1Z|D`(#|FHhbh=0hs}pnkuXk0WBnmb-BV02s~GqF$z(2A=+PTG`HO{ntT_`W^q3 z2KVW^lwMEVup+d>FQq$W18L41j>{d)$vz1&y2eIr0V5mhREQl($C8U;ANt2L-^=;e5b;ha4@9%! z_hkOkYJMrO_|{IpgGq&|ni$8HZ=Wra#>cml{L@muNxTfNcd`qxC_?J`<99n$<8N4K zh_^~NU{0Bgq(A&`XI5|gtQA`Wmz%!yZ3}rIy)e(B=dsp}AL?I|2RgrD!N7}tKOYXe z^q=}TroSEcaM`;1=V#s-r)eTao8+!`oPg1H>e%LMi)aGPM=+Z;7x4DW8PbJ+;W47; zJ`7KZd`6$eIZW-JYMCmao76JLwkPxtQj4|xjg!{oyd+l@Mm`>R zEu(6O(d9R8zo)X~m;so8e&+&mD{kJto~S5bGmW0`R&-^%-Pk^NDORcwz1HGnfZI>@ zH4ghUX2rcfL764~qBIapK|l81nh%x3F{CaryGqGmX-iDG~E%t$FlYYV0$~tvp;FQd5^vEaG_Kqb*`P7S^>idm(uh zo2wGdJZVq$hV=m&}G#i`shva1R9$-mwN>#WwHO};NZOBkwz*Am~ z*=No7jD>wxJq^3_X29j2J*@3lx5q)8M*CIavEi)^)Jm5|1 zsZCaYCzFEAs>7{VrTEyKm--+uQtpGT2!0VrMC|uT<+GYMKf-SGnDE zVb|C6!`b@*gZAxfAM+CKPA`dgYHwE;jJqS8*M;J0!*dGdg@Yt{DS1ntB4ojAtHD7ij-uqd z%~&n4M1+S9(yZ#bi=mi`3sm51qy$RuPjHf{EaMvaqh$+75aY*DtWEde%hH(kqHjm? zF!RP}dpl)bcA!>kj&?a;k9U|_6t$6TBRR1+<|S1ERu>zSx;)|H5?z{IALikML5>3< z>nuD4Dr%O&{?<|gcoO99 zp3dt`Ztl=W0;t?Ms9(WuO|vp6EImajvz<42o*wm+TO6hQxS}QEibk zF{d^>AN&IZGl8V4r5Tyvv1L9w3Xs%vcI(WjPFp`h=L?me!eQ;EwP$f!nNbOJUgb_* zBY9dg4a(>TB<;qdaZ2)$a`H$_w{<~7lSxw}xlIdfJ!ZPy$%$n{dwU`x}?rgphN@Rjreoyk-)WnDb&w|=V+8T6Gn z-KXRWw}cHc!&pT1UXEzJ6*C^m z^5Lau>$jMX6dJlrx?5#n>2-K?;`!W@7W2ez^3zS>d~+G*9b3A0b(MbO+UG&>3z(8@vfz8*?HT~ zB;hpdv9;ggao?8T)C?1g-e>2Bmm>}LX=h)}MzHJFkd?Q9IDfmXwWc2)X5E`w{GhMh0_Z z5-@FcxP8h1DM(rt(&n6 Date: Tue, 21 Jun 2022 22:39:10 -0700 Subject: [PATCH 24/30] Small refactor to prepare for atree; fix reset health/lvl --- js/build.js | 4 - js/build2.js | 550 -------------------------------------- js/build_encode_decode.js | 18 +- js/builder.js | 6 +- js/builder_graph.js | 57 ++-- js/craft.js | 3 - js/display.js | 8 +- 7 files changed, 51 insertions(+), 595 deletions(-) delete mode 100644 js/build2.js diff --git a/js/build.js b/js/build.js index 78cf2ac..1760004 100644 --- a/js/build.js +++ b/js/build.js @@ -211,9 +211,5 @@ class Build{ let statMap = this.statMap; let weapon_stats = this.weapon.statMap; statMap.set("damageRaw", [weapon_stats.get("nDam"), weapon_stats.get("eDam"), weapon_stats.get("tDam"), weapon_stats.get("wDam"), weapon_stats.get("fDam"), weapon_stats.get("aDam")]); - statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]); - statMap.set("defRaw", [statMap.get("eDef"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]); - statMap.set("defBonus", [statMap.get("eDefPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]); - statMap.set("defMult", classDefenseMultipliers.get(weapon_stats.get("type"))); } } diff --git a/js/build2.js b/js/build2.js deleted file mode 100644 index b74e403..0000000 --- a/js/build2.js +++ /dev/null @@ -1,550 +0,0 @@ - - -const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20] ]); - -/** - * @description Error to catch items that don't exist. - * @module ItemNotFound - */ -class ItemNotFound { - /** - * @class - * @param {String} item the item name entered - * @param {String} type the type of item - * @param {Boolean} genElement whether to generate an element from inputs - * @param {String} override override for item type - */ - constructor(item, type, genElement, override) { - /** - * @public - * @type {String} - */ - this.message = `Cannot find ${override||type} named ${item}`; - if (genElement) - /** - * @public - * @type {Element} - */ - this.element = document.getElementById(`${type}-choice`).parentElement.querySelectorAll("p.error")[0]; - else - this.element = document.createElement("div"); - } -} - -/** - * @description Error to catch incorrect input. - * @module IncorrectInput - */ -class IncorrectInput { - /** - * @class - * @param {String} input the inputted text - * @param {String} format the correct format - * @param {String} sibling the id of the error node's sibling - */ - constructor(input, format, sibling) { - /** - * @public - * @type {String} - */ - this.message = `${input} is incorrect. Example: ${format}`; - /** - * @public - * @type {String} - */ - this.id = sibling; - } -} - -/** - * @description Error that inputs an array of items to generate errors of. - * @module ListError - * @extends Error - */ -class ListError extends Error { - /** - * @class - * @param {Array} errors array of errors - */ - constructor(errors) { - let ret = []; - if (typeof errors[0] == "string") { - super(errors[0]); - } else { - super(errors[0].message); - } - for (let i of errors) { - if (typeof i == "string") { - ret.push(new Error(i)); - } else { - ret.push(i); - } - } - /** - * @public - * @type {Object[]} - */ - this.errors = ret; - } -} - -/*Class that represents a wynn player's build. -*/ -class Build{ - - /** - * @description Construct a build. - * @param {Number} level : Level of the player. - * @param {String[]} equipment : List of equipment names that make up the build. - * In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon. - * @param {Number[]} powders : Powder application. List of lists of integers (powder IDs). - * In order: boots, Chestplate, Leggings, Boots, Weapon. - * @param {Object[]} inputerrors : List of instances of error-like classes. - */ - constructor(level,equipment, powders, externalStats, inputerrors=[]){ - - let errors = inputerrors; - //this contains the Craft objects, if there are any crafted items. this.boots, etc. will contain the statMap of the Craft (which is built to be an expandedItem). - this.craftedItems = []; - this.customItems = []; - // NOTE: powders is just an array of arrays of powder IDs. Not powder objects. - this.powders = powders; - if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") { - const helmet = itemMap.get(equipment[0]); - this.powders[0] = this.powders[0].slice(0,helmet.slots); - this.helmet = expandItem(helmet, this.powders[0]); - } else { - try { - //let boots = getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : (getCustomFromHash(equipment[0])? getCustomFromHash(equipment[0]) : undefined); - let helmet = getCustomFromHash(equipment[0]) ? getCustomFromHash(equipment[0]) : (getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : undefined); - if (helmet.statMap.get("type") !== "helmet") { - throw new Error("Not a helmet"); - } - this.powders[0] = this.powders[0].slice(0,helmet.statMap.get("slots")); - helmet.statMap.set("powders",this.powders[0].slice()); - helmet.applyPowders(); - this.helmet = helmet.statMap; - if (this.helmet.get("custom")) { - this.customItems.push(helmet); - } else if (this.helmet.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(helmet); - } - - } catch (Error) { - //console.log(Error); //fix - const helmet = itemMap.get("No Helmet"); - this.powders[0] = this.powders[0].slice(0,helmet.slots); - this.helmet = expandItem(helmet, this.powders[0]); - errors.push(new ItemNotFound(equipment[0], "helmet", true)); - } - } - if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") { - const chestplate = itemMap.get(equipment[1]); - this.powders[1] = this.powders[1].slice(0,chestplate.slots); - this.chestplate = expandItem(chestplate, this.powders[1]); - } else { - try { - let chestplate = getCustomFromHash(equipment[1]) ? getCustomFromHash(equipment[1]) : (getCraftFromHash(equipment[1]) ? getCraftFromHash(equipment[1]) : undefined); - if (chestplate.statMap.get("type") !== "chestplate") { - throw new Error("Not a chestplate"); - } - this.powders[1] = this.powders[1].slice(0,chestplate.statMap.get("slots")); - chestplate.statMap.set("powders",this.powders[1].slice()); - chestplate.applyPowders(); - this.chestplate = chestplate.statMap; - if (this.chestplate.get("custom")) { - this.customItems.push(chestplate); - } else if (this.chestplate.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(chestplate); - } - } catch (Error) { - const chestplate = itemMap.get("No Chestplate"); - this.powders[1] = this.powders[1].slice(0,chestplate.slots); - this.chestplate = expandItem(chestplate, this.powders[1]); - errors.push(new ItemNotFound(equipment[1], "chestplate", true)); - } - } - if (itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") { - const leggings = itemMap.get(equipment[2]); - this.powders[2] = this.powders[2].slice(0,leggings.slots); - this.leggings = expandItem(leggings, this.powders[2]); - } else { - try { - let leggings = getCustomFromHash(equipment[2]) ? getCustomFromHash(equipment[2]) : (getCraftFromHash(equipment[2]) ? getCraftFromHash(equipment[2]) : undefined); - if (leggings.statMap.get("type") !== "leggings") { - throw new Error("Not a leggings"); - } - this.powders[2] = this.powders[2].slice(0,leggings.statMap.get("slots")); - leggings.statMap.set("powders",this.powders[2].slice()); - leggings.applyPowders(); - this.leggings = leggings.statMap; - if (this.leggings.get("custom")) { - this.customItems.push(leggings); - } else if (this.leggings.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(leggings); - } - } catch (Error) { - const leggings = itemMap.get("No Leggings"); - this.powders[2] = this.powders[2].slice(0,leggings.slots); - this.leggings = expandItem(leggings, this.powders[2]); - errors.push(new ItemNotFound(equipment[2], "leggings", true)); - } - } - if (itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") { - const boots = itemMap.get(equipment[3]); - this.powders[3] = this.powders[3].slice(0,boots.slots); - this.boots = expandItem(boots, this.powders[3]); - } else { - try { - let boots = getCustomFromHash(equipment[3]) ? getCustomFromHash(equipment[3]) : (getCraftFromHash(equipment[3]) ? getCraftFromHash(equipment[3]) : undefined); - if (boots.statMap.get("type") !== "boots") { - throw new Error("Not a boots"); - } - this.powders[3] = this.powders[3].slice(0,boots.statMap.get("slots")); - boots.statMap.set("powders",this.powders[3].slice()); - boots.applyPowders(); - this.boots = boots.statMap; - console.log(boots); - if (this.boots.get("custom")) { - this.customItems.push(boots); - } else if (this.boots.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(boots); - } - } catch (Error) { - const boots = itemMap.get("No Boots"); - this.powders[3] = this.powders[3].slice(0,boots.slots); - this.boots = expandItem(boots, this.powders[3]); - errors.push(new ItemNotFound(equipment[3], "boots", true)); - } - } - if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") { - const ring = itemMap.get(equipment[4]); - this.ring1 = expandItem(ring, []); - }else{ - try { - let ring = getCustomFromHash(equipment[4]) ? getCustomFromHash(equipment[4]) : (getCraftFromHash(equipment[4]) ? getCraftFromHash(equipment[4]) : undefined); - if (ring.statMap.get("type") !== "ring") { - throw new Error("Not a ring"); - } - this.ring1 = ring.statMap; - if (this.ring1.get("custom")) { - this.customItems.push(ring); - } else if (this.ring1.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(ring); - } - } catch (Error) { - const ring = itemMap.get("No Ring 1"); - this.ring1 = expandItem(ring, []); - errors.push(new ItemNotFound(equipment[4], "ring1", true, "ring")); - } - } - if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") { - const ring = itemMap.get(equipment[5]); - this.ring2 = expandItem(ring, []); - }else{ - try { - let ring = getCustomFromHash(equipment[5]) ? getCustomFromHash(equipment[5]) : (getCraftFromHash(equipment[5]) ? getCraftFromHash(equipment[5]) : undefined); - if (ring.statMap.get("type") !== "ring") { - throw new Error("Not a ring"); - } - this.ring2 = ring.statMap; - if (this.ring2.get("custom")) { - this.customItems.push(ring); - } else if (this.ring2.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(ring); - } - } catch (Error) { - const ring = itemMap.get("No Ring 2"); - this.ring2 = expandItem(ring, []); - errors.push(new ItemNotFound(equipment[5], "ring2", true, "ring")); - } - } - if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") { - const bracelet = itemMap.get(equipment[6]); - this.bracelet = expandItem(bracelet, []); - }else{ - try { - let bracelet = getCustomFromHash(equipment[6]) ? getCustomFromHash(equipment[6]) : (getCraftFromHash(equipment[6]) ? getCraftFromHash(equipment[6]) : undefined); - if (bracelet.statMap.get("type") !== "bracelet") { - throw new Error("Not a bracelet"); - } - this.bracelet = bracelet.statMap; - if (this.bracelet.get("custom")) { - this.customItems.push(bracelet); - } else if (this.bracelet.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(bracelet); - } - } catch (Error) { - const bracelet = itemMap.get("No Bracelet"); - this.bracelet = expandItem(bracelet, []); - errors.push(new ItemNotFound(equipment[6], "bracelet", true)); - } - } - if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") { - const necklace = itemMap.get(equipment[7]); - this.necklace = expandItem(necklace, []); - }else{ - try { - let necklace = getCustomFromHash(equipment[7]) ? getCustomFromHash(equipment[7]) : (getCraftFromHash(equipment[7]) ? getCraftFromHash(equipment[7]) : undefined); - if (necklace.statMap.get("type") !== "necklace") { - throw new Error("Not a necklace"); - } - this.necklace = necklace.statMap; - if (this.necklace.get("custom")) { - this.customItems.push(necklace); - } else if (this.necklace.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(necklace); - } - } catch (Error) { - const necklace = itemMap.get("No Necklace"); - this.necklace = expandItem(necklace, []); - errors.push(new ItemNotFound(equipment[7], "necklace", true)); - } - } - if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") { - const weapon = itemMap.get(equipment[8]); - this.powders[4] = this.powders[4].slice(0,weapon.slots); - this.weapon = expandItem(weapon, this.powders[4]); - if (equipment[8] !== "No Weapon") { - document.getElementsByClassName("powder-specials")[0].style.display = "grid"; - } else { - document.getElementsByClassName("powder-specials")[0].style.display = "none"; - } - }else{ - try { - let weapon = getCustomFromHash(equipment[8]) ? getCustomFromHash(equipment[8]) : (getCraftFromHash(equipment[8]) ? getCraftFromHash(equipment[8]) : undefined); - if (weapon.statMap.get("category") !== "weapon") { - throw new Error("Not a weapon"); - } - this.weapon = weapon.statMap; - if (this.weapon.get("custom")) { - this.customItems.push(weapon); - } else if (this.weapon.get("crafted")) { //customs can also be crafted, but custom takes priority. - this.craftedItems.push(weapon); - } - this.powders[4] = this.powders[4].slice(0,this.weapon.get("slots")); - this.weapon.set("powders",this.powders[4].slice()); - document.getElementsByClassName("powder-specials")[0].style.display = "grid"; - } catch (Error) { - const weapon = itemMap.get("No Weapon"); - this.powders[4] = this.powders[4].slice(0,weapon.slots); - this.weapon = expandItem(weapon, this.powders[4]); - document.getElementsByClassName("powder-specials")[0].style.display = "none"; - errors.push(new ItemNotFound(equipment[8], "weapon", true)); - } - } - //console.log(this.craftedItems) - - if (level < 1) { //Should these be constants? - this.level = 1; - } else if (level > 106) { - this.level = 106; - } else if (level <= 106 && level >= 1) { - this.level = level; - } else if (typeof level === "string") { - this.level = level; - errors.push(new IncorrectInput(level, "a number", "level-choice")); - } else { - errors.push("Level is not a string or number."); - } - document.getElementById("level-choice").value = this.level; - - this.availableSkillpoints = levelToSkillPoints(this.level); - this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ]; - this.items = this.equipment.concat([this.weapon]); - // return [equip_order, best_skillpoints, final_skillpoints, best_total]; - let result = calculate_skillpoints(this.equipment, this.weapon); - console.log(result); - this.equip_order = result[0]; - this.base_skillpoints = result[1]; - this.total_skillpoints = result[2]; - this.assigned_skillpoints = result[3]; - this.activeSetCounts = result[4]; - - // For strength boosts like warscream, vanish, etc. - this.damageMultiplier = 1.0; - this.defenseMultiplier = 1.0; - - // For other external boosts ;-; - this.externalStats = externalStats; - - this.initBuildStats(); - - // Remove every error before adding specific ones - for (let i of document.getElementsByClassName("error")) { - i.textContent = ""; - } - this.errors = errors; - if (errors.length > 0) this.errored = true; - } - - /*Returns build in string format - */ - toString(){ - return [this.equipment,this.weapon].flat(); - } - - /* Getters */ - - /* Get total health for build. - */ - - getSpellCost(spellIdx, cost) { - cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2]))); - cost = Math.max(0, Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100))); - return Math.max(1, cost + this.statMap.get("spRaw"+spellIdx)); - } - - - /* Get melee stats for build. - Returns an array in the order: - */ - getMeleeStats(){ - const stats = this.statMap; - if (this.weapon.get("tier") === "Crafted") { - stats.set("damageBases", [this.weapon.get("nDamBaseHigh"),this.weapon.get("eDamBaseHigh"),this.weapon.get("tDamBaseHigh"),this.weapon.get("wDamBaseHigh"),this.weapon.get("fDamBaseHigh"),this.weapon.get("aDamBaseHigh")]); - } - let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier"); - if(adjAtkSpd > 6){ - adjAtkSpd = 6; - }else if(adjAtkSpd < 0){ - adjAtkSpd = 0; - } - - let damage_mult = 1; - if (this.weapon.get("type") === "relik") { - damage_mult = 0.99; // CURSE YOU WYNNCRAFT - //One day we will create WynnWynn and no longer have shaman 99% melee injustice. - //In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams. - } - // 0spellmult for melee damage. - let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct") + this.externalStats.get("mdPct"), 0, this.weapon, this.total_skillpoints, damage_mult * this.damageMultiplier, this.externalStats); - - let dex = this.total_skillpoints[1]; - - let totalDamNorm = results[0]; - let totalDamCrit = results[1]; - totalDamNorm.push(1-skillPointsToPercentage(dex)); - totalDamCrit.push(skillPointsToPercentage(dex)); - let damages_results = results[2]; - - let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2]) - +(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2; - - //Now do math - let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd]; - let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd]; - let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex))); - //[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit] - return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]); - } - - /* - Get all defensive stats for this build. - */ - getDefenseStats(){ - const stats = this.statMap; - let defenseStats = []; - let def_pct = skillPointsToPercentage(this.total_skillpoints[3]); - let agi_pct = skillPointsToPercentage(this.total_skillpoints[4]); - //total hp - let totalHp = stats.get("hp") + stats.get("hpBonus"); - if (totalHp < 5) totalHp = 5; - defenseStats.push(totalHp); - //EHP - let ehp = [totalHp, totalHp]; - let defMult = classDefenseMultipliers.get(this.weapon.get("type")); - ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); - ehp[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); - defenseStats.push(ehp); - //HPR - let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.); - defenseStats.push(totalHpr); - //EHPR - let ehpr = [totalHpr, totalHpr]; - ehpr[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier)); - ehpr[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier)); - defenseStats.push(ehpr); - //skp stats - defenseStats.push([ (1 - ((1-def_pct) * (2 - this.defenseMultiplier)))*100, agi_pct*100]); - //eledefs - TODO POWDERS - let eledefs = [0, 0, 0, 0, 0]; - for(const i in skp_elements){ //kinda jank but ok - eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.); - } - defenseStats.push(eledefs); - - //[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]] - return defenseStats; - } - - /* Get all stats for this build. Stores in this.statMap. - @pre The build itself should be valid. No checking of validity of pieces is done here. - */ - initBuildStats(){ - - let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef"]; - - //Create a map of this build's stats - let statMap = new Map(); - - for (const staticID of staticIDs) { - statMap.set(staticID, 0); - } - statMap.set("hp", levelToHPBase(this.level)); - - let major_ids = new Set(); - for (const item of this.items){ - for (let [id, value] of item.get("maxRolls")) { - statMap.set(id,(statMap.get(id) || 0)+value); - } - for (const staticID of staticIDs) { - if (item.get(staticID)) { - statMap.set(staticID, statMap.get(staticID) + item.get(staticID)); - } - } - if (item.get("majorIds")) { - for (const majorID of item.get("majorIds")) { - major_ids.add(majorID); - } - } - } - statMap.set("activeMajorIDs", major_ids); - for (const [setName, count] of this.activeSetCounts) { - const bonus = sets[setName].bonuses[count-1]; - for (const id in bonus) { - if (skp_order.includes(id)) { - // pass. Don't include skillpoints in ids - } - else { - statMap.set(id,(statMap.get(id) || 0)+bonus[id]); - } - } - } - statMap.set("poisonPct", 100); - - // The stuff relevant for damage calculation!!! @ferricles - statMap.set("atkSpd", this.weapon.get("atkSpd")); - - for (const x of skp_elements) { - this.externalStats.set(x + "DamPct", 0); - } - this.externalStats.set("mdPct", 0); - this.externalStats.set("sdPct", 0); - this.externalStats.set("damageBonus", [0, 0, 0, 0, 0]); - this.externalStats.set("defBonus",[0, 0, 0, 0, 0]); - this.externalStats.set("poisonPct", 0); - this.statMap = statMap; - - this.aggregateStats(); - } - - aggregateStats() { - let statMap = this.statMap; - statMap.set("damageRaw", [this.weapon.get("nDam"), this.weapon.get("eDam"), this.weapon.get("tDam"), this.weapon.get("wDam"), this.weapon.get("fDam"), this.weapon.get("aDam")]); - statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]); - statMap.set("defRaw", [statMap.get("eDef"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]); - statMap.set("defBonus", [statMap.get("eDefPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]); - statMap.set("defMult", classDefenseMultipliers.get(this.weapon.get("type"))); - } -} diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index 2601830..86aabd5 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -202,15 +202,15 @@ function shareBuild(build) { if (build) { let text = url_base+location.hash+"\n"+ "WynnBuilder build:\n"+ - "> "+build.helmet.statMap.get("displayName")+"\n"+ - "> "+build.chestplate.statMap.get("displayName")+"\n"+ - "> "+build.leggings.statMap.get("displayName")+"\n"+ - "> "+build.boots.statMap.get("displayName")+"\n"+ - "> "+build.ring1.statMap.get("displayName")+"\n"+ - "> "+build.ring2.statMap.get("displayName")+"\n"+ - "> "+build.bracelet.statMap.get("displayName")+"\n"+ - "> "+build.necklace.statMap.get("displayName")+"\n"+ - "> "+build.weapon.statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]"; + "> "+build.items[0].statMap.get("displayName")+"\n"+ + "> "+build.items[1].statMap.get("displayName")+"\n"+ + "> "+build.items[2].statMap.get("displayName")+"\n"+ + "> "+build.items[3].statMap.get("displayName")+"\n"+ + "> "+build.items[4].statMap.get("displayName")+"\n"+ + "> "+build.items[5].statMap.get("displayName")+"\n"+ + "> "+build.items[6].statMap.get("displayName")+"\n"+ + "> "+build.items[7].statMap.get("displayName")+"\n"+ + "> "+build.items[8].statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]"; copyTextToClipboard(text); document.getElementById("share-button").textContent = "Copied!"; } diff --git a/js/builder.js b/js/builder.js index ec4f93f..34f7eab 100644 --- a/js/builder.js +++ b/js/builder.js @@ -96,18 +96,14 @@ function resetFields(){ } } - const nodes_to_reset = item_nodes.concat(powder_nodes.concat(edit_input_nodes)); + const nodes_to_reset = item_nodes.concat(powder_nodes).concat(edit_input_nodes).concat([powder_special_input, boosts_node]); for (const node of nodes_to_reset) { node.mark_dirty(); } - powder_special_input.mark_dirty(); - boosts_node.mark_dirty(); for (const node of nodes_to_reset) { node.update(); } - powder_special_input.update(); - boosts_node.update(); setValue("level-choice", "106"); location.hash = ""; diff --git a/js/builder_graph.js b/js/builder_graph.js index 688615a..dd2a66e 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -206,6 +206,11 @@ class ItemInputDisplayNode extends ComputeNode { this.input_field.classList.add("text-light"); this.image.classList.remove('Normal-shadow', 'Unique-shadow', 'Rare-shadow', 'Legendary-shadow', 'Fabled-shadow', 'Mythic-shadow', 'Set-shadow', 'Crafted-shadow', 'Custom-shadow'); + if (this.health_field) { + // Doesn't exist for weapons. + this.health_field.textContent = "0"; + } + this.level_field.textContent = "0"; if (!item) { this.input_field.classList.add("is-invalid"); return null; @@ -443,7 +448,7 @@ function getDefenseStats(stats) { defenseStats.push(totalHp); //EHP let ehp = [totalHp, totalHp]; - let defMult = (2 - stats.get("classDef")) * (1 - stats.get("defBonus")); + let defMult = (2 - stats.get("classDef")) * (2 - stats.get("defMultiplier")); ehp[0] /= (1-def_pct)*(1-agi_pct)*defMult; ehp[1] /= (1-def_pct)*defMult; defenseStats.push(ehp); @@ -725,33 +730,47 @@ class DisplayBuildWarningsNode extends ComputeNode { } /** - * Aggregate stats from the build and from inputs. + * Aggregate stats from all inputs (merges statmaps). * - * Signature: AggregateStatsNode(build: Build, *args) => StatMap + * Signature: AggregateStatsNode(*args) => StatMap */ class AggregateStatsNode extends ComputeNode { constructor() { super("builder-aggregate-stats"); } + compute_func(input_map) { + const output_stats = new Map(); + for (const [k, v] of input_map.entries()) { + for (const [k2, v2] of v.entries()) { + if (output_stats.has(k2)) { + output_stats.set(k2, v2 + output_stats.get(k2)); + } + else { + output_stats.set(k2, v2); + } + } + } + return output_stats; + } +} + +/** + * Aggregate editable ID stats with build and weapon type. + * + * Signature: AggregateEditableIDNode(build: Build, weapon: Item, *args) => StatMap + */ +class AggregateEditableIDNode extends ComputeNode { + constructor() { super("builder-aggregate-inputs"); } + compute_func(input_map) { const build = input_map.get('build'); input_map.delete('build'); - const powder_boost = input_map.get('powder-boost'); input_map.delete('powder-boost'); - const potion_boost = input_map.get('potion-boost'); input_map.delete('potion-boost'); const weapon = input_map.get('weapon'); input_map.delete('weapon'); const output_stats = new Map(build.statMap); - output_stats.set("damageMultiplier", 1 + potion_boost[0]); - output_stats.set("defBonus", potion_boost[1]); + output_stats.set("damageMultiplier", 1); + output_stats.set("defMultiplier", 1); for (const [k, v] of input_map.entries()) { output_stats.set(k, v); } - for (const [k, v] of powder_boost.entries()) { - if (output_stats.has(k)) { - output_stats.set(k, v + output_stats.get(k)); - } - else { - output_stats.set(k, v); - } - } output_stats.set('classDef', classDefenseMultipliers.get(weapon.statMap.get("type"))); return output_stats; @@ -919,13 +938,14 @@ function builder_graph_init() { // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) let stat_agg_node = new AggregateStatsNode(); - stat_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon'); + let edit_agg_node = new AggregateEditableIDNode(); + edit_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon'); for (const field of editable_item_fields) { // Create nodes that listens to each editable id input, the node name should match the "id" const elem = document.getElementById(field); const node = new SumNumberInputNode('builder-'+field+'-input', elem); - stat_agg_node.link_to(node, field); + edit_agg_node.link_to(node, field); edit_input_nodes.push(node); } // Edit IDs setter declared up here to set ids so they will be populated by default. @@ -936,11 +956,12 @@ function builder_graph_init() { const elem = document.getElementById(skp+'-skp'); const node = new SumNumberInputNode('builder-'+skp+'-input', elem); - stat_agg_node.link_to(node, skp); + edit_agg_node.link_to(node, skp); build_encode_node.link_to(node, skp); build_warnings_node.link_to(node, skp); edit_input_nodes.push(node); } + stat_agg_node.link_to(edit_agg_node); build_disp_node.link_to(stat_agg_node, 'stats'); for (const input_node of item_nodes.concat(powder_nodes)) { diff --git a/js/craft.js b/js/craft.js index 1d3ae41..db30aef 100644 --- a/js/craft.js +++ b/js/craft.js @@ -170,7 +170,6 @@ class Craft{ statMap.set(e + "Dam", "0-0"); statMap.set(e + "DamLow", "0-0"); } - //statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]); statMap.set("category","weapon"); statMap.set("atkSpd",this.atkSpd); } @@ -381,12 +380,10 @@ class Craft{ statMap.set("reqs",[0,0,0,0,0]); statMap.set("skillpoints", [0,0,0,0,0]); - statMap.set("damageBonus",[0,0,0,0,0]); for (const e in skp_order) { statMap.set(skp_order[e], statMap.get("maxRolls").has(skp_order[e]) ? statMap.get("maxRolls").get(skp_order[e]) : 0); statMap.get("skillpoints")[e] = statMap.get("maxRolls").has(skp_order[e]) ? statMap.get("maxRolls").get(skp_order[e]) : 0; statMap.get("reqs")[e] = statMap.has(skp_order[e]+"Req") && !consumableTypes.includes(statMap.get("type"))? statMap.get(skp_order[e]+"Req") : 0; - statMap.get("damageBonus")[e] = statMap.has(skp_order[e]+"DamPct") ? statMap.get(skp_order[e]+"DamPct") : 0; } for (const id of rolledIDs) { if (statMap.get("minRolls").has(id)) { diff --git a/js/display.js b/js/display.js index 61c57f0..9ecb378 100644 --- a/js/display.js +++ b/js/display.js @@ -182,7 +182,6 @@ function displayExpandedItem(item, parent_id){ stats.set("wDamPct", 0); stats.set("fDamPct", 0); stats.set("aDamPct", 0); - stats.set("damageBonus", [0, 0, 0, 0, 0]); //SUPER JANK @HPP PLS FIX let damage_keys = [ "nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_" ]; @@ -1293,9 +1292,6 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){ statsTable.appendChild(hpRow); } - let defMult = statMap.get("defMult"); - if (!defMult) {defMult = 1} - //EHP let ehpRow = document.createElement("div"); ehpRow.classList.add("row"); @@ -1405,8 +1401,8 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){ boost.classList.add("col"); boost.classList.add("text-end"); - let defRaw = statMap.get("defRaw")[i]; - let defPct = statMap.get("defBonus")[i]/100; + let defRaw = statMap.get(skp_elements[i]+"Def"); + let defPct = statMap.get(skp_elements[i]+"DefPct")/100; if (defRaw < 0) { defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct; } else { From e4466d80952bd7d1b037d95bfe1a9dc9f0b1a216 Mon Sep 17 00:00:00 2001 From: hppeng Date: Tue, 21 Jun 2022 22:53:04 -0700 Subject: [PATCH 25/30] Add back morph easter egg --- js/builder_graph.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/js/builder_graph.js b/js/builder_graph.js index dd2a66e..9528be9 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -178,6 +178,24 @@ class ItemInputNode extends InputNode { return item; } } + else if (this.none_item.statMap.get('category') === 'weapon' && item_text.startsWith("Morph-")) { + let replace_items = [ "Morph-Stardust", + "Morph-Steel", + "Morph-Iron", + "Morph-Gold", + "Morph-Topaz", + "Morph-Emerald", + "Morph-Amethyst", + "Morph-Ruby", + item_text.substring(6) + ] + + for (const [i, x] of zip2(equipment_inputs, replace_items)) { setValue(i, x); } + + // NOTE: DO NOT REORDER FOR PERFORMANCE REASONS + for (const node of item_nodes) { node.mark_dirty(); } + for (const node of item_nodes) { node.update(); } + } return null; } } From 9227dd0f56ab1672daa198b54fba8a66c6d0d4b1 Mon Sep 17 00:00:00 2001 From: hppeng Date: Thu, 23 Jun 2022 01:25:19 -0700 Subject: [PATCH 26/30] Working armor special sliders (don't reset properly i think) --- builder/index.html | 20 ++++---- js/builder_graph.js | 105 +++++++++++++++++++++++++--------------- js/computation_graph.js | 2 + js/display_constants.js | 21 +++++--- 4 files changed, 94 insertions(+), 54 deletions(-) diff --git a/builder/index.html b/builder/index.html index 7b4f294..c30e554 100644 --- a/builder/index.html +++ b/builder/index.html @@ -944,27 +944,27 @@
-
-
-
-
-
@@ -1031,7 +1031,7 @@ Rage (Passive)
- +
@@ -1075,7 +1075,7 @@ Kill Streak (Passive)
- +
@@ -1119,7 +1119,7 @@ Concentration (Passive)
- +
@@ -1163,7 +1163,7 @@ Endurance (Passive)
- +
@@ -1207,7 +1207,7 @@ Dodge (Passive)
- +
diff --git a/js/builder_graph.js b/js/builder_graph.js index 9528be9..ed0ac00 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -1,18 +1,42 @@ -let boosts_node; -/* Updates all spell boosts -*/ -function updateBoosts(buttonId) { - let elem = document.getElementById(buttonId); - if (elem.classList.contains("toggleOn")) { - elem.classList.remove("toggleOn"); - } else { - elem.classList.add("toggleOn"); +let armor_powder_node = new (class extends ComputeNode { + constructor() { super('builder-armor-powder-input'); } + + compute_func(input_map) { + let damage_boost = 0; + let def_boost = 0; + let statMap = new Map(); + for (const [e, elem] of zip2(skp_elements, skp_order)) { + let val = parseInt(document.getElementById(elem+"_boost_armor").value); + statMap.set(e+'DamPct', val); + } + return statMap; } - boosts_node.mark_dirty(); - boosts_node.update(); +})().update(); + +/* Updates PASSIVE powder special boosts (armors) +*/ +function update_armor_powder_specials(elem_id) { + //we only update the powder special + external stats if the player has a build + let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi + + //update the label associated w/ the slider + let elem = document.getElementById(elem_id); + let label = document.getElementById(elem_id + "_label"); + + let value = elem.value; + + label.textContent = label.textContent.split(":")[0] + ": " + value + + //update the slider's graphics + let bg_color = elem_colors[skp_order.indexOf(wynn_elem)]; + let pct = Math.round(100 * value / powderSpecialStats[skp_order.indexOf(wynn_elem)].cap); + elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`; + + armor_powder_node.mark_dirty().update(); } -class BoostsInputNode extends ComputeNode { + +let boosts_node = new (class extends ComputeNode { constructor() { super('builder-boost-input'); } compute_func(input_map) { @@ -28,14 +52,40 @@ class BoostsInputNode extends ComputeNode { } return [damage_boost, def_boost]; } +})().update(); + +/* Updates all spell boosts +*/ +function update_boosts(buttonId) { + let elem = document.getElementById(buttonId); + if (elem.classList.contains("toggleOn")) { + elem.classList.remove("toggleOn"); + } else { + elem.classList.add("toggleOn"); + } + boosts_node.mark_dirty().update(); } -let powder_special_input; let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"]; +let powder_special_input = new (class extends ComputeNode { + constructor() { super('builder-powder-special-input'); } + + compute_func(input_map) { + let powder_specials = []; // [ [special, power], [special, power]] + for (const sName of specialNames) { + for (let i = 1;i < 6; i++) { + if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) { + let powder_special = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))]; + powder_specials.push([powder_special, i]); + break; + } + } + } + return powder_specials; + } +})(); function updatePowderSpecials(buttonId) { - //console.log(player_build.statMap); - let name = (buttonId).split("-")[0]; let power = (buttonId).split("-")[1]; // [1, 5] @@ -53,26 +103,7 @@ function updatePowderSpecials(buttonId) { elem.classList.add("toggleOn"); } - powder_special_input.mark_dirty(); - powder_special_input.update(); -} - -class PowderSpecialInputNode extends ComputeNode { - constructor() { super('builder-powder-special-input'); } - - compute_func(input_map) { - let powder_specials = []; // [ [special, power], [special, power]] - for (const sName of specialNames) { - for (let i = 1;i < 6; i++) { - if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) { - let powder_special = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))]; - powder_specials.push([powder_special, i]); - break; - } - } - } - return powder_specials; - } + powder_special_input.mark_dirty().update(); } class PowderSpecialCalcNode extends ComputeNode { @@ -988,17 +1019,15 @@ function builder_graph_init() { level_input.update(); // Powder specials. - powder_special_input = new PowderSpecialInputNode(); let powder_special_calc = new PowderSpecialCalcNode().link_to(powder_special_input, 'powder-specials'); new PowderSpecialDisplayNode().link_to(powder_special_input, 'powder-specials') .link_to(stat_agg_node, 'stats').link_to(item_nodes[8], 'weapon'); stat_agg_node.link_to(powder_special_calc, 'powder-boost'); + stat_agg_node.link_to(armor_powder_node, 'armor-powder'); powder_special_input.update(); // Potion boost. - boosts_node = new BoostsInputNode(); stat_agg_node.link_to(boosts_node, 'potion-boost'); - boosts_node.update(); // Also do something similar for skill points diff --git a/js/computation_graph.js b/js/computation_graph.js index f99a966..160c802 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -37,6 +37,7 @@ class ComputeNode { for (const child of this.children) { child.mark_input_clean(this.name, this.value); } + return this; } /** @@ -69,6 +70,7 @@ class ComputeNode { child.mark_dirty(); } } + return this; } /** diff --git a/js/display_constants.js b/js/display_constants.js index 4901e88..3ad22fb 100644 --- a/js/display_constants.js +++ b/js/display_constants.js @@ -212,20 +212,21 @@ let posModSuffixes = { let build_all_display_commands = [ "#defense-stats", "str", "dex", "int", "def", "agi", + "!spacer", "mr", "ms", "hprRaw", "hprPct", + "ls", "sdRaw", "sdPct", "mdRaw", "mdPct", - "ref", "thorns", - "ls", - "poison", - "expd", - "spd", - "atkTier", "!elemental", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "!elemental", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", + "atkTier", + "poison", + "ref", "thorns", + "expd", + "spd", "rainbowRaw", "sprint", "sprintReg", "jh", @@ -325,3 +326,11 @@ let sq2_ing_display_order = [ "lvl", "skills", ] + +let elem_colors = [ + "#00AA00", + "#FFFF55", + "#55FFFF", + "#FF5555", + "#FFFFFF" +] From 6ea34708a6de2ed906efe382aa4dda57105d3de9 Mon Sep 17 00:00:00 2001 From: hppeng Date: Thu, 23 Jun 2022 02:23:56 -0700 Subject: [PATCH 27/30] Fully working tomes !!! --- js/build.js | 14 ++++++++++++-- js/build_encode_decode.js | 2 +- js/builder.js | 10 +++++++++- js/builder_graph.js | 40 ++++++++++++++++++++++++++++++++------- js/load_tome.js | 24 +++++++++++------------ tomes.json | 20 ++++++++++++++++++++ 6 files changed, 87 insertions(+), 23 deletions(-) diff --git a/js/build.js b/js/build.js index 1760004..987aac3 100644 --- a/js/build.js +++ b/js/build.js @@ -155,10 +155,12 @@ class Build{ */ initBuildStats(){ - let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "def", "agi"]; + let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "def", "agi", "dmgMobs", "defMobs"]; //Create a map of this build's stats let statMap = new Map(); + statMap.set("damageMultiplier", 1); + statMap.set("defMultiplier", 1); for (const staticID of staticIDs) { statMap.set(staticID, 0); @@ -176,7 +178,15 @@ class Build{ } for (const staticID of staticIDs) { if (item_stats.get(staticID)) { - statMap.set(staticID, statMap.get(staticID) + item_stats.get(staticID)); + if (staticID === "dmgMobs") { + statMap.set('damageMultiplier', statMap.get('damageMultiplier') * item_stats.get(staticID)); + } + else if (staticID === "defMobs") { + statMap.set('defMultiplier', statMap.get('defMultiplier') * item_stats.get(staticID)); + } + else { + statMap.set(staticID, statMap.get(staticID) + item_stats.get(staticID)); + } } } if (item_stats.get("majorIds")) { diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index 86aabd5..c12dae0 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -157,7 +157,7 @@ function encodeBuild(build, powders, skillpoints) { } else if (item.statMap.get("crafted")) { build_string += "CR-"+encodeCraft(item); } else if (item.statMap.get("category") === "tome") { - let tome_id = item.get("id"); + let tome_id = item.statMap.get("id"); if (tome_id <= 60) { // valid normal tome. ID 61-63 is for NONE tomes. build_version = Math.max(build_version, 6); diff --git a/js/builder.js b/js/builder.js index 34f7eab..cf997f3 100644 --- a/js/builder.js +++ b/js/builder.js @@ -95,8 +95,15 @@ function resetFields(){ elem.classList.remove("toggleOn"); } } + for (const elem of skp_order) { + console.log(document.getElementById(elem + "_boost_armor").value); + document.getElementById(elem + "_boost_armor").value = 0; + document.getElementById(elem + "_boost_armor_prev").value = 0; + document.getElementById(elem + "_boost_armor").style.background = `linear-gradient(to right, #AAAAAA, #AAAAAA 0%, #AAAAAA 100%)`; + document.getElementById(elem + "_boost_armor_label").textContent = `% ${damageClasses[skp_order.indexOf(elem)+1]} Damage Boost: 0`; + } - const nodes_to_reset = item_nodes.concat(powder_nodes).concat(edit_input_nodes).concat([powder_special_input, boosts_node]); + const nodes_to_reset = item_nodes.concat(powder_nodes).concat(edit_input_nodes).concat([powder_special_input, boosts_node, armor_powder_node]); for (const node of nodes_to_reset) { node.mark_dirty(); } @@ -317,6 +324,7 @@ function init_autocomplete() { if (event.detail.selection.value) { event.target.value = event.detail.selection.value; } + event.target.dispatchEvent(new Event('input')); }, }, } diff --git a/js/builder_graph.js b/js/builder_graph.js index ed0ac00..cfb8491 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -259,7 +259,10 @@ class ItemInputDisplayNode extends ComputeNode { // Doesn't exist for weapons. this.health_field.textContent = "0"; } - this.level_field.textContent = "0"; + if (this.level_field) { + // Doesn't exist for tomes. + this.level_field.textContent = "0"; + } if (!item) { this.input_field.classList.add("is-invalid"); return null; @@ -274,7 +277,10 @@ class ItemInputDisplayNode extends ComputeNode { // Doesn't exist for weapons. this.health_field.textContent = item.statMap.get('hp'); } - this.level_field.textContent = item.statMap.get('lvl'); + if (this.level_field) { + // Doesn't exist for tomes. + this.level_field.textContent = item.statMap.get('lvl'); + } this.image.classList.add(tier + "-shadow"); return null; } @@ -401,7 +407,14 @@ class BuildAssembleNode extends ComputeNode { input_map.get('ring1-input'), input_map.get('ring2-input'), input_map.get('bracelet-input'), - input_map.get('necklace-input') + input_map.get('necklace-input'), + input_map.get('weaponTome1-input'), + input_map.get('weaponTome2-input'), + input_map.get('armorTome1-input'), + input_map.get('armorTome2-input'), + input_map.get('armorTome3-input'), + input_map.get('armorTome4-input'), + input_map.get('guildTome1-input') ]; let weapon = input_map.get('weapon-input'); let level = input_map.get('level-input'); @@ -759,7 +772,7 @@ class DisplayBuildWarningsNode extends ComputeNode { } let baditem = document.createElement("p"); baditem.classList.add("nocolor"); baditem.classList.add("itemp"); - baditem.textContent = item.get("displayName") + " requires level " + item_lvl + " to use."; + baditem.textContent = item.statMap.get("displayName") + " requires level " + item_lvl + " to use."; lvlWarning.appendChild(baditem); } } @@ -791,7 +804,13 @@ class AggregateStatsNode extends ComputeNode { for (const [k, v] of input_map.entries()) { for (const [k2, v2] of v.entries()) { if (output_stats.has(k2)) { - output_stats.set(k2, v2 + output_stats.get(k2)); + // TODO: ugly AF + if (k2 === 'damageMultiplier' || k2 === 'defMultiplier') { + output_stats.set(k2, v2 * output_stats.get(k2)); + } + else { + output_stats.set(k2, v2 + output_stats.get(k2)); + } } else { output_stats.set(k2, v2); @@ -815,8 +834,6 @@ class AggregateEditableIDNode extends ComputeNode { const weapon = input_map.get('weapon'); input_map.delete('weapon'); const output_stats = new Map(build.statMap); - output_stats.set("damageMultiplier", 1); - output_stats.set("defMultiplier", 1); for (const [k, v] of input_map.entries()) { output_stats.set(k, v); } @@ -944,6 +961,15 @@ function builder_graph_init() { //new PrintNode(eq+'-debug').link_to(item_input); //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); } + console.log(none_tomes); + for (const [eq, none_item] of zip2(tome_fields, [none_tomes[0], none_tomes[0], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[2]])) { + let input_field = document.getElementById(eq+"-choice"); + let item_image = document.getElementById(eq+"-img"); + + let item_input = new ItemInputNode(eq+'-input', input_field, none_item); + item_nodes.push(item_input); + new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); + } // weapon image changer node. let weapon_image = document.getElementById("weapon-img"); diff --git a/js/load_tome.js b/js/load_tome.js index e6fded5..3b44842 100644 --- a/js/load_tome.js +++ b/js/load_tome.js @@ -1,4 +1,4 @@ -const TOME_DB_VERSION = 1; +const TOME_DB_VERSION = 2; // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA let tdb; @@ -120,6 +120,11 @@ async function load_tome_init() { }); } +let none_tomes = [ + ["tome", "weaponTome", "No Weapon Tome"], + ["tome", "armorTome", "No Armor Tome"], + ["tome", "guildTome", "No Guild Tome"] +]; function init_tome_maps() { //warp tomeMap = new Map(); @@ -131,17 +136,12 @@ function init_tome_maps() { tomeLists.set(it, []); } - let noneTomes = [ - ["tome", "weaponTome", "No Weapon Tome"], - ["tome", "armorTome", "No Armor Tome"], - ["tome", "guildTome", "No Guild Tome"] - ]; for (let i = 0; i < 3; i++) { let tome = Object(); tome.slots = 0; - tome.category = noneTomes[i][0]; - tome.type = noneTomes[i][1]; - tome.name = noneTomes[i][2]; + tome.category = none_tomes[i][0]; + tome.type = none_tomes[i][1]; + tome.name = none_tomes[i][2]; tome.displayName = tome.name; tome.set = null; tome.quest = null; @@ -160,14 +160,14 @@ function init_tome_maps() { //dependency - load.js clean_item(tome); - noneTomes[i] = tome; + none_tomes[i] = tome; } - tomes = tomes.concat(noneTomes); + tomes = tomes.concat(none_tomes); for (const tome of tomes) { if (tome.remapID === undefined) { tomeLists.get(tome.type).push(tome.displayName); tomeMap.set(tome.displayName, tome); - if (noneTomes.includes(tome)) { + if (none_tomes.includes(tome)) { tomeIDMap.set(tome.id, ""); } else { diff --git a/tomes.json b/tomes.json index 913efa4..0b4c213 100644 --- a/tomes.json +++ b/tomes.json @@ -9,6 +9,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defmobs": 3, "thorns": 6, "ref": 6, "hpBonus": 120, @@ -23,6 +24,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "thorns": 8, "ref": 8, "fixID": false, @@ -36,6 +38,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defMobs": 3, "exploding": 5, "mdPct": 5, "hpBonus": 120, @@ -50,6 +53,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "thorns": 6, "reflection": 6, "fixID": false, @@ -63,6 +67,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defMobs": 3, "sdPct": 5, "hpBonus": 120, "fixID": false, @@ -76,6 +81,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "sdPct": 6, "fixID": false, "id": 5 @@ -88,6 +94,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defMobs": 3, "hprRaw": 15, "hpBonus": 120, "fixID": false, @@ -101,6 +108,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "hprRaw": 60, "fixID": false, "id": 7 @@ -113,6 +121,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defMobs": 3, "ls": 25, "hpBonus": 120, "fixID": false, @@ -126,6 +135,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "ls": 85, "fixID": false, "id": 9 @@ -138,6 +148,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defMobs": 3, "lb": 5, "hpBonus": 120, "fixID": false, @@ -151,6 +162,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "lb": 6, "fixID": false, "id": 11 @@ -163,6 +175,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 60, + "defMobs": 3, "spd": 5, "hpBonus": 120, "fixID": false, @@ -176,6 +189,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 5, "spd": 6, "fixID": false, "id": 13 @@ -188,6 +202,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 8, "eDefPct": 10, "hpBonus": 150, "fixID": false, @@ -201,6 +216,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 8, "tDefPct": 10, "hpBonus": 150, "fixID": false, @@ -214,6 +230,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 8, "wDefPct": 10, "hpBonus": 150, "fixID": false, @@ -227,6 +244,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 8, "fDefPct": 10, "hpBonus": 150, "fixID": false, @@ -240,6 +258,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 8, "aDefPct": 10, "hpBonus": 150, "fixID": false, @@ -253,6 +272,7 @@ "drop": "never", "restrict": "Soulbound Item", "lvl": 100, + "defMobs": 8, "eDefPct": 6, "tDefPct": 6, "wDefPct": 6, From 12e4c1e88c976670d1c72ca0a13bac58a74a07be Mon Sep 17 00:00:00 2001 From: hppeng Date: Thu, 23 Jun 2022 03:16:36 -0700 Subject: [PATCH 28/30] Revert to current dev branch behaviour somewhat --- js/builder.js | 2 +- js/builder_graph.js | 1 - js/computation_graph.js | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/js/builder.js b/js/builder.js index f497224..96513d8 100644 --- a/js/builder.js +++ b/js/builder.js @@ -293,7 +293,7 @@ function init_autocomplete() { resultsList: { maxResults: 1000, tabSelect: true, - noResults: true, + noResults: false, class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm", element: (list, data) => { // dynamic result loc diff --git a/js/builder_graph.js b/js/builder_graph.js index cfb8491..51a4d6c 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -894,7 +894,6 @@ class SkillPointSetterNode extends ComputeNode { } compute_func(input_map) { - console.log("a"); if (input_map.size !== 1) { throw "SkillPointSetterNode accepts exactly one input (build)"; } const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element for (const [idx, elem] of skp_order.entries()) { diff --git a/js/computation_graph.js b/js/computation_graph.js index c0337ec..90fe08c 100644 --- a/js/computation_graph.js +++ b/js/computation_graph.js @@ -105,7 +105,7 @@ class ComputeNode { * * @param node : ComputeNode to schedule an update for. */ -function calcSchedule(node) { +function calcSchedule(node, timeout) { if (node.update_task !== null) { clearTimeout(node.update_task); } @@ -113,7 +113,7 @@ function calcSchedule(node) { node.update_task = setTimeout(function() { node.update(); node.update_task = null; - }, 500); + }, timeout); } class PrintNode extends ComputeNode { @@ -139,8 +139,8 @@ class InputNode extends ComputeNode { constructor(name, input_field) { super(name); this.input_field = input_field; - //this.input_field.addEventListener("input", () => calcSchedule(this)); - this.input_field.addEventListener("change", () => calcSchedule(this)); + this.input_field.addEventListener("input", () => calcSchedule(this, 5000)); + this.input_field.addEventListener("change", () => calcSchedule(this, 500)); //calcSchedule(this); Manually fire first update for better control } From ef5dd8f6c6065dc38b085f5af075a4d301c93345 Mon Sep 17 00:00:00 2001 From: hppeng Date: Thu, 23 Jun 2022 03:42:18 -0700 Subject: [PATCH 29/30] HOTFIX: tome patch --- js/build.js | 9 ++++----- js/build_encode_decode.js | 8 ++++---- js/builder_graph.js | 16 ++++++++++------ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/js/build.js b/js/build.js index b95a569..ccd25a6 100644 --- a/js/build.js +++ b/js/build.js @@ -105,7 +105,7 @@ class Build{ * In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome. * 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building) */ - constructor(level, items, tomes, weapon){ + constructor(level, items, weapon){ if (level < 1) { //Should these be constants? this.level = 1; @@ -123,13 +123,12 @@ class Build{ this.availableSkillpoints = levelToSkillPoints(this.level); this.equipment = items; - this.tomes = tomes; this.weapon = weapon; - this.items = this.equipment.concat([this.weapon]).concat(this.tomes); + this.items = this.equipment.concat([this.weapon]); // return [equip_order, best_skillpoints, final_skillpoints, best_total]; // calc skillpoints requires statmaps only - let result = calculate_skillpoints(this.equipment.concat(this.tomes).map((x) => x.statMap), this.weapon.statMap); + let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap); this.equip_order = result[0]; // How many skillpoints the player had to assign (5 number) this.base_skillpoints = result[1]; @@ -145,7 +144,7 @@ class Build{ /*Returns build in string format */ toString(){ - return [this.equipment,this.weapon,this.tomes].flat(); + return [this.equipment,this.weapon].flat(); } diff --git a/js/build_encode_decode.js b/js/build_encode_decode.js index c12dae0..618bb71 100644 --- a/js/build_encode_decode.js +++ b/js/build_encode_decode.js @@ -119,11 +119,11 @@ function decodeBuild(url_tag) { // Tomes. if (version == 6) { //tome values do not appear in anything before v6. - for (let i = 0; i < 7; ++i) { + for (let i in tomes) { let tome_str = info[1].charAt(i); - for (let i in tomes) { - setValue(tomeInputs[i], getTomeNameFromID(Base64.toInt(tome_str))); - } + let tome_name = getTomeNameFromID(Base64.toInt(tome_str)); + console.log(tome_name); + setValue(tomeInputs[i], tome_name); } info[1] = info[1].slice(7); } diff --git a/js/builder_graph.js b/js/builder_graph.js index 51a4d6c..f4d41ea 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -382,8 +382,6 @@ class URLUpdateNode extends ComputeNode { * Create a "build" object from a set of equipments. * Returns a new Build object, or null if all items are NONE items. * - * TODO: add tomes - * * Signature: BuildAssembleNode(helmet-input: Item, * chestplate-input: Item, * leggings-input: Item, @@ -426,7 +424,7 @@ class BuildAssembleNode extends ComputeNode { if (all_none && !location.hash) { return null; } - return new Build(level, equipments, [], weapon); + return new Build(level, equipments, weapon); } } @@ -1007,8 +1005,6 @@ function builder_graph_init() { let build_disp_node = new BuildDisplayNode() build_disp_node.link_to(build_node, 'build'); - let build_warnings_node = new DisplayBuildWarningsNode(); - build_warnings_node.link_to(build_node, 'build'); // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) let stat_agg_node = new AggregateStatsNode(); @@ -1026,14 +1022,15 @@ function builder_graph_init() { edit_id_output = new EditableIDSetterNode(edit_input_nodes); // Makes shallow copy of list. edit_id_output.link_to(build_node); + let skp_inputs = []; for (const skp of skp_order) { const elem = document.getElementById(skp+'-skp'); const node = new SumNumberInputNode('builder-'+skp+'-input', elem); edit_agg_node.link_to(node, skp); build_encode_node.link_to(node, skp); - build_warnings_node.link_to(node, skp); edit_input_nodes.push(node); + skp_inputs.push(node); } stat_agg_node.link_to(edit_agg_node); build_disp_node.link_to(stat_agg_node, 'stats'); @@ -1079,6 +1076,13 @@ function builder_graph_init() { let skp_output = new SkillPointSetterNode(edit_input_nodes); skp_output.link_to(build_node); + let build_warnings_node = new DisplayBuildWarningsNode(); + build_warnings_node.link_to(build_node, 'build'); + for (const [skp_input, skp] of zip2(skp_inputs, skp_order)) { + build_warnings_node.link_to(skp_input, skp); + } + build_warnings_node.update(); + // call node.update() for each skillpoint node and stat edit listener node manually // NOTE: the text boxes for skill points are already filled out by decodeBuild() so this will fix them // this will propagate the update to the `stat_agg_node`, and then to damage calc From 5f9c0d5185ea08b8dfa43b8513df5715f37b2073 Mon Sep 17 00:00:00 2001 From: hppeng Date: Thu, 23 Jun 2022 03:55:13 -0700 Subject: [PATCH 30/30] HOTFIX: str/dex optimizer working now --- js/builder_graph.js | 7 +++---- js/display.js | 1 - js/optimize.js | 30 +++++++++--------------------- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/js/builder_graph.js b/js/builder_graph.js index f4d41ea..5cddf8d 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -223,9 +223,8 @@ class ItemInputNode extends InputNode { for (const [i, x] of zip2(equipment_inputs, replace_items)) { setValue(i, x); } - // NOTE: DO NOT REORDER FOR PERFORMANCE REASONS - for (const node of item_nodes) { node.mark_dirty(); } - for (const node of item_nodes) { node.update(); } + for (const node of item_nodes) { calcSchedule(node, 10); } + return this.compute_func(input_map); } return null; } @@ -942,6 +941,7 @@ let item_nodes = []; let powder_nodes = []; let spelldmg_nodes = []; let edit_input_nodes = []; +let skp_inputs = []; function builder_graph_init() { // Phase 1/2: Set up item input, propagate updates, etc. @@ -1022,7 +1022,6 @@ function builder_graph_init() { edit_id_output = new EditableIDSetterNode(edit_input_nodes); // Makes shallow copy of list. edit_id_output.link_to(build_node); - let skp_inputs = []; for (const skp of skp_order) { const elem = document.getElementById(skp+'-skp'); const node = new SumNumberInputNode('builder-'+skp+'-input', elem); diff --git a/js/display.js b/js/display.js index 11afa6a..8f7017d 100644 --- a/js/display.js +++ b/js/display.js @@ -51,7 +51,6 @@ function displaySetBonuses(parent_id,build) { } mock_item.set("powders", []); displayExpandedItem(mock_item, set_elem.id); - console.log(mock_item); } } diff --git a/js/optimize.js b/js/optimize.js index fdce392..2343d47 100644 --- a/js/optimize.js +++ b/js/optimize.js @@ -19,7 +19,7 @@ function optimizeStrDex() { total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus); // Calculate total 3rd spell damage - let spell = spell_table[player_build.weapon.get("type")][2]; + let spell = spell_table[player_build.weapon.statMap.get("type")][2]; const stats = player_build.statMap; let critChance = skillPointsToPercentage(total_skillpoints[1]); let save_damages = []; @@ -41,8 +41,7 @@ function optimizeStrDex() { if (part.type === "damage") { let _results = calculateSpellDamage(stats, part.conversion, stats.get("sdRaw"), stats.get("sdPct"), - part.multiplier / 100, player_build.weapon, total_skillpoints, - player_build.damageMultiplier, player_build.externalStats); + part.multiplier / 100, player_build.weapon.statMap, total_skillpoints, 1); let totalDamNormal = _results[0]; let totalDamCrit = _results[1]; let results = _results[2]; @@ -75,27 +74,16 @@ function optimizeStrDex() { str_bonus -= 1; dex_bonus += 1; - } - // TODO: reduce duplicated code, @calculateBuild - let skillpoints = player_build.total_skillpoints; - let delta_total = 0; + console.log(best_skillpoints); + + // TODO do not merge for performance reasons for (let i in skp_order) { - let manual_assigned = best_skillpoints[i]; - let delta = manual_assigned - skillpoints[i]; - skillpoints[i] = manual_assigned; - player_build.base_skillpoints[i] += delta; - delta_total += delta; + skp_inputs[i].input_field.value = best_skillpoints[i]; + skp_inputs[i].mark_dirty(); } - player_build.assigned_skillpoints += delta_total; - - try { - calculateBuildStats(); - if (player_build.errored) - throw new ListError(player_build.errors); - } - catch (error) { - handleBuilderError(error); + for (let i in skp_order) { + skp_inputs[i].update(); } }