diff --git a/build.js b/build.js index af00975..67e240c 100644 --- a/build.js +++ b/build.js @@ -3,44 +3,89 @@ const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ]; const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"]; const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20] ]); -/*Turns the input amount of skill points into a float precision percentage. -* @param skp - the integer skillpoint count to be converted -*/ -function skillPointsToPercentage(skp){ - if (skp<=0){ - return 0.0; - }else if(skp>=150){ - return 0.808; - }else{ - return (-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771); - //return(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771).toFixed(3); - } -} - -/*Turns the input amount of levels into skillpoints available. -* -* @param level - the integer level count te be converted -*/ -function levelToSkillPoints(level){ - if(level < 1){ - return 0; - }else if(level >= 101){ - return 200; - }else{ - return (level - 1) * 2; +/** + * @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"); } } -/*Turns the input amount of levels in to base HP. -* @param level - the integer level count to be converted -*/ -function levelToHPBase(level){ - if(level < 1){ //bad level - return this.levelToHPBase(1); - }else if (level > 106){ //also bad level - return this.levelToHPBase(106); - }else{ //good level - return 5*level + 5; +/** + * @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; } } @@ -48,15 +93,19 @@ function levelToHPBase(level){ */ class Build{ - /* - * Construct a build. - * @param level : Level of the player. - * @param equipment : List of equipment names that make up the 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: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon. - * @param powders : Powder application. List of lists of integers (powder IDs). + * @param {Number[]} powders : Powder application. List of lists of integers (powder IDs). * In order: Helmet, Chestplate, Leggings, Boots, Weapon. + * @param {Object[]} inputerrors : List of instances of error-like classes. */ - constructor(level,equipment, powders, externalStats){ + constructor(level,equipment, powders, externalStats, inputerrors=[]){ + + let errors = inputerrors; + // 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") { @@ -64,67 +113,99 @@ class Build{ this.powders[0] = this.powders[0].slice(0,helmet.slots); this.helmet = expandItem(helmet, this.powders[0]); }else{ - throw new TypeError("No such helmet named "+ equipment[0]); + 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{ - throw new TypeError("No such chestplate named "+ equipment[1]); + 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{ - throw new TypeError("No such leggings named "+ equipment[2]); + const chestplate = itemMap.get("No Leggings"); + this.powders[1] = this.powders[1].slice(0,chestplate.slots); + this.chestplate = expandItem(chestplate, this.powders[1]); + 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{ - throw new TypeError("No such boots named "+ equipment[3]); + 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{ - throw new TypeError("No such ring named "+ equipment[4]); + 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{ - throw new TypeError("No such ring named "+ equipment[5]); + 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{ - throw new TypeError("No such bracelet named "+ equipment[6]); + 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{ - throw new TypeError("No such necklace named "+ equipment[7]); + 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{ - throw new TypeError("No such weapon named "+ equipment[8]); + const weapon = itemMap.get("No Weapon"); + this.powders[4] = this.powders[4].slice(0,weapon.slots); + this.weapon = expandItem(weapon, this.powders[4]); + errors.push(new ItemNotFound(equipment[8], "weapon", true)); } - if(level < 1){ //Should these be constants? + + if (level < 1) { //Should these be constants? this.level = 1; - }else if (level > 106){ + } else if (level > 106) { this.level = 106; - }else{ + } 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.level = 106; + 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]); @@ -144,12 +225,19 @@ class Build{ 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.helmet.get("name") + ", " + this.chestplate.get("name") + ", " + this.leggings.get("name") + ", " + this.boots.get("name") + ", " + this.ring1.get("name") + ", " + this.ring2.get("name") + ", " + this.bracelet.get("name") + ", " + this.necklace.get("name") + ", " + this.weapon.get("name"); + return [this.equipment,this.weapon].flat(); } /* Getters */ @@ -274,14 +362,10 @@ class Build{ } } } + statMap.set("poisonPct", 100); // The stuff relevant for damage calculation!!! @ferricles statMap.set("atkSpd", this.weapon.get("atkSpd")); - 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("eDam"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]); - statMap.set("defBonus", [statMap.get("eDamPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]); - statMap.set("poisonPct", 100); for (const x of skp_elements) { this.externalStats.set(x + "DamPct", 0); @@ -292,6 +376,15 @@ class Build{ 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")]); + } } diff --git a/build_utils.js b/build_utils.js new file mode 100644 index 0000000..f9d071a --- /dev/null +++ b/build_utils.js @@ -0,0 +1,40 @@ +/*Turns the input amount of skill points into a float precision percentage. +* @param skp - the integer skillpoint count to be converted +*/ +function skillPointsToPercentage(skp){ + if (skp<=0){ + return 0.0; + }else if(skp>=150){ + return 0.808; + }else{ + return (-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771); + //return(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771).toFixed(3); + } +} + +/*Turns the input amount of levels into skillpoints available. +* +* @param level - the integer level count te be converted +*/ +function levelToSkillPoints(level){ + if(level < 1){ + return 0; + }else if(level >= 101){ + return 200; + }else{ + return (level - 1) * 2; + } +} + +/*Turns the input amount of levels in to base HP. +* @param level - the integer level count to be converted +*/ +function levelToHPBase(level){ + if(level < 1){ //bad level + return this.levelToHPBase(1); + }else if (level > 106){ //also bad level + return this.levelToHPBase(106); + }else{ //good level + return 5*level + 5; + } +} \ No newline at end of file diff --git a/builder.js b/builder.js index f374c0e..19d293a 100644 --- a/builder.js +++ b/builder.js @@ -1,16 +1,9 @@ -/* - * TESTING SECTION - */ - const url_base = location.href.split("#")[0]; const url_tag = location.hash.slice(1); console.log(url_base); console.log(url_tag); -/* - * END testing section - */ - +const BUILD_VERSION = "6.9.4"; function setTitle() { document.getElementById("header").textContent = "WynnBuilder version "+BUILD_VERSION+" (db version "+DB_VERSION+")"; @@ -26,6 +19,7 @@ let accessoryTypes = [ "ring", "bracelet", "necklace" ]; let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ]; // THIS IS SUPER DANGEROUS, WE SHOULD NOT BE KEEPING THIS IN SO MANY PLACES let item_fields = [ "name", "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", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id" ]; +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" ]; @@ -424,29 +418,36 @@ function calculateBuild(save_skp, skp){ 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]); let powdering = []; + let errorederrors = []; while (input) { let first = input.slice(0, 2); let powder = powderIDs.get(first); console.log(powder); if (powder === undefined) { - throw new TypeError("Invalid powder " + powder + " in slot " + i); + errorederrors.push(first); + } else { + powdering.push(powder); } - 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); } - //level setting - let level = document.getElementById("level-choice").value; - if(level === ""){ - level = 106; - } - document.getElementById("level-choice").value = level; + + console.log(equipment); + player_build = new Build(document.getElementById("level-choice").value, equipment, powderings, new Map(), errors); for (let i of document.getElementsByClassName("hide-container-block")) { i.style.display = "block"; @@ -454,9 +455,9 @@ function calculateBuild(save_skp, skp){ for (let i of document.getElementsByClassName("hide-container-grid")) { i.style.display = "grid"; } + document.getElementById("int-info-div").style.display = "none"; - player_build = new Build(level, equipment, powderings, new Map()); - //console.log(player_build.toString()); + console.log(player_build.toString()); displayEquipOrder(document.getElementById("build-order"),player_build.equip_order); @@ -466,6 +467,11 @@ function calculateBuild(save_skp, skp){ for (let i in skp_order){ //big bren setText(skp_order[i] + "-skp-base", "Original Value: " + 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 @@ -483,21 +489,50 @@ function calculateBuild(save_skp, skp){ calculateBuildStats(); setTitle(); + if (player_build.errored) + throw new ListError(player_build.errors); + } catch (error) { - 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); + 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); } - let p2 = document.createElement("p"); - p2.textContent = "If you believe this is an error, contact hppeng on forums or discord."; - header.appendChild(p2); } } @@ -550,28 +585,33 @@ function updateStats() { delta_total += delta; } player_build.assigned_skillpoints += delta_total; - calculateBuildStats(); if(player_build){ - updatePowderSpecials("skip"); - updateBoosts("skip"); + updatePowderSpecials("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 spell boosts */ -function updateBoosts(buttonId) { +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 -= .10; + player_build.defenseMultiplier -= .20; } elem.classList.remove("toggleOn"); }else{ player_build.damageMultiplier += damageMultipliers.get(name); if (name === "warscream") { - player_build.defenseMultiplier += .10; + player_build.defenseMultiplier += .20; } elem.classList.add("toggleOn"); } @@ -582,16 +622,18 @@ function updateBoosts(buttonId) { if (elem.classList.contains("toggleOn")) { elem.classList.remove("toggleOn"); player_build.damageMultiplier -= value; - if (key === "warscream") { player_build.defenseMultiplier -= .10 } + if (key === "warscream") { player_build.defenseMultiplier -= .20 } } } } - calculateBuildStats(); + if (recalcStats) { + calculateBuildStats(); + } } /* Updates all powder special boosts */ -function updatePowderSpecials(buttonId){ +function updatePowderSpecials(buttonId, recalcStats) { //console.log(player_build.statMap); let name = (buttonId).split("-")[0]; @@ -671,8 +713,10 @@ function updatePowderSpecials(buttonId){ } } + if (recalcStats) { + calculateBuildStats(); + } displayPowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build); - calculateBuildStats(); //also make damage boosts apply ;-; } /* Calculates all build statistics and updates the entire display. */ @@ -836,5 +880,18 @@ function resetFields(){ 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"); + } +} + load_init(init); diff --git a/clean.json b/clean.json index 68bd2e4..0bc0b5a 100644 --- a/clean.json +++ b/clean.json @@ -270652,7 +270652,7 @@ "restrict": "1.20 item", "fixID": false, "strReq": 0, - "dexReq": 105, + "dexReq": 120, "intReq": 0, "defReq": 0, "agiReq": 0, diff --git a/craft.js b/craft.js new file mode 100644 index 0000000..3fbfff6 --- /dev/null +++ b/craft.js @@ -0,0 +1,181 @@ +let armorTypes = [ "helmet", "chestplate", "leggings", "boots" ]; +let accessoryTypes = [ "ring", "bracelet", "necklace" ]; +let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ]; +let consumableTypes = [ "potion", "scroll", "food"] +/* Creates a crafted item object. +*/ +class Craft{ + /* Constructs a craft. + @param recipe: Helmet-1-3 (id), etc. A recipe object. + @param mat_tiers: [1->3, 1->3]. An array with 2 numbers. + @param ingreds: []. An array with 6 entries, each with an ingredient Map. + */ + constructor(recipe, mat_tiers, ingreds) { + this.recipe = recipe; + this.mat_tiers = mat_tiers; + this.ingreds = ingreds; + this.statMap = new Map(); //can use the statMap as an expanded Item + + this.initCraftStats(); + } + + + /* Get all stats for this build. Stores in this.statMap. + @pre The craft itself should be valid. No checking of validity of pieces is done here. + */ + initCraftStats(){ + let consumables = ["POTION", "SCROLL", "FOOD"]; + let statMap = new Map(); + statMap.set("minRolls", new Map()); + statMap.set("maxRolls", new Map()); + statMap.set("displayName", "Crafted Item"); //TODO: DISPLAY THE HASH + statMap.set("tier", "Crafted"); + statMap.set("type", this.recipe.get("type").toLowerCase()); + statMap.set("duration", [this.recipe.get("duration")[0], this.recipe.get("duration")[1]]); //[low, high] + statMap.set("durability", [this.recipe.get("durability")[0], this.recipe.get("durability")[1]]); + statMap.set("lvl", (this.recipe.get("lvl")[0] + "-" + this.recipe.get("lvl")[1]) ); + statMap.set("nDam", 0); + + if (armorTypes.includes(statMap.get("type")) || weaponTypes.includes(statMap.get("type"))) { + if(this.recipe.get("lvl")[0] < 30) { + statMap.set("slots", 1); + } else if (this.recipe.get("lvl") < 70) { + statMap.set("slots", 2); + } else{ + statMap.set("slots", 3); + } + } else { + statMap.set("slots", 0); + } + if (consumableTypes.includes(statMap.get("type"))) { + if(this.recipe.get("lvl")[0] < 30) { + statMap.set("charges", 1); + } else if (this.recipe.get("lvl") < 70) { + statMap.set("charges", 2); + } else{ + statMap.set("charges", 3); + } + //no ingredient consumables ALWAYS have 3 charges. + let allNone = true; + for(const ingred of this.ingreds) { + if(ingred.get("name") !== "No Ingredient") { + allNone = false; + break; + } + } + if (allNone) { + statMap.set("charges", 3); + statMap.set("hp", this.recipe.get("healthOrDamage").join("-")); + } + statMap.set("category","consumable"); + } else { + statMap.set("slots", 0); + } + + if (armorTypes.includes(statMap.get("type"))) { + statMap.set("hp", this.recipe.get("healthOrDamage").join("-")); + statMap.set("category","armor"); + } else if (weaponTypes.includes(statMap.get("type"))) { + statMap.set("nDam", this.recipe.get("healthOrDamage").join("-")); + for (const e of skp_elements) { + statMap.set(e + "Dam", "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("powders",""); + /* Change certain IDs based on material tier. + healthOrDamage changes. + duration and durability change. (but not basicDuration) + + */ + + //calc ingredient effectivenesses -> see https://wynndata.tk/cr/585765168 + let eff = [[100,100],[100,100],[100,100]]; + for (let n in this.ingreds) { + let ingred = this.ingreds[n]; + //i and j will refer to the eff matrix. + let i = Math.floor(n / 2); + let j = n % 2; + for (const [key,value] of ingred.get("posMods")) { + if(value == 0) { + continue; + } else { + if (key === "above") { + for (let k = i-1; k > -1; k--) { + eff[k][j] += value; + } + } else if (key === "under") { + for (let k = i+1; k < 3; k++) { + eff[k][j] += value; + } + } else if (key === "left") { + if (j == 1) { + eff[i][j-1] += value; + } + } else if (key === "right") { + if (j == 0) { + eff[i][j+1] += value; + } + } else if (key === "touching") { + for (let k in eff) { + for (let l in eff[k]) { + if ( (Math.abs(k-i) == 1 && Math.abs (l-j) == 0) || (Math.abs(k-i) == 0 && Math.abs (l-j) == 1) ) { + eff[k][l] += value; + } + } + } + } else if (key === "notTouching") { + for (let k in eff) { + for (let l in eff[k]) { + if ( (Math.abs(k-i) > 1) || (Math.abs(k-i) == 1 && Math.abs(l-j) == 1) ) { + eff[k][l] += value; + } + } + } + } else { + console.log("Something went wrong. Please contact hppeng."); + //wtf happened + } + } + } + } + //apply ingredient effectivness - on ids, and reqs (itemIDs). NOT on durability, duration, or charges. + let eff_flat = eff.flat(); + //console.log(eff_flat); + //apply ingredient ids + for (const n in this.ingreds) { + let ingred = this.ingreds[n]; + let eff_mult = (eff_flat[n] / 100).toFixed(2); + for (const [key, value] of ingred.get("itemIDs")) { + if(key !== "dura") { + statMap.set(key, statMap.get(key) + Math.floor(value*eff_mult)); //CHECK IF THIS IS CORRECT + } else { //durability, NOT affected by effectiveness + statMap.set("durability", statMap.get("durability").map(x => x + value)); + } + } + for (const [key,value] of ingred.get("consumableIDs")) { + //neither duration nor charges are affected by effectiveness + if(key === "dura") { + statMap.set("duration", statMap.get("duration").map(x => x + value)); + } else{ + statMap.set(key, statMap.get("charges") + value); + } + } + for (const [key,value] of ingred.get("ids").get("minRolls")) { + if (value && value != 0) { + let rolls = [value,ingred.get("ids").get("maxRolls").get(key)]; + rolls = rolls.map(x => Math.floor(x * eff_mult)).sort(); + console.log(rolls); + statMap.get("minRolls").set(key, (statMap.get("minRolls").get(key)) ? statMap.get("minRolls").get(key) + rolls[0] : rolls[0]); + statMap.get("maxRolls").set(key, (statMap.get("maxRolls").get(key)) ? statMap.get("maxRolls").get(key) + rolls[1] : rolls[1]); + } + } + } + for(const e of skp_order) { + statMap.set(e,statMap.get("maxRolls").get(e)); + } + + this.statMap = statMap; + } +} \ No newline at end of file diff --git a/crafter.html b/crafter.html new file mode 100644 index 0000000..7544d4e --- /dev/null +++ b/crafter.html @@ -0,0 +1,183 @@ + + +
+ + + + + + + + + +
+
+ + + + |
+
+
+ + + + |
+
+
+ + + + + |
+
+
+ + + + + |
+
+
+ + + + |
+
+
+ + + + |
+
+
+ + + + |
+
+
+ + + + |
+
+
+ + + + |
+
+
+ + + + |
+
+ + | ++ + | ++ + | ++ + | +
+
++
+ +Made by hppeng and ferricles with Atlas Inc (JavaScript required to function, nothing works without js)
+Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.
+