2021-01-19 17:32:27 +00:00
|
|
|
let armorTypes = [ "helmet", "chestplate", "leggings", "boots" ];
|
|
|
|
let accessoryTypes = [ "ring", "bracelet", "necklace" ];
|
|
|
|
let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
|
|
|
|
let consumableTypes = [ "potion", "scroll", "food"]
|
2021-01-29 22:11:57 +00:00
|
|
|
|
|
|
|
//constructs a craft from a hash 'CR-qwoefsabaoe' or 'qwoefsaboe'
|
|
|
|
function createCraft(hash) {
|
|
|
|
let name = hash;
|
|
|
|
if (name.slice(0,3) === "CR-") {
|
|
|
|
name = name.substring(3);
|
|
|
|
}
|
|
|
|
this.hash = name;
|
|
|
|
ingreds = [];
|
|
|
|
/*for (let i = 0; i < 6; i ++ ) {
|
|
|
|
ingreds.push( ingIDMap.get(Base64.toInt(name.substring(2*i,2*i+2))) );
|
|
|
|
}
|
|
|
|
let recipe = recipeIDMap.get(Base64.toInt(tag.substring(12,14)));
|
|
|
|
recipesName = recipe.split("-");
|
|
|
|
setValue("recipe-choice",recipesName[0]);
|
|
|
|
setValue("level-choice",recipesName[1]+"-"+recipesName[2]);
|
|
|
|
tierNum = Base64.toInt(tag.substring(14,15));
|
|
|
|
let mat_tiers = [];
|
|
|
|
mat_tiers.push(tierNum % 3 == 0 ? 3 : tierNum % 3);
|
|
|
|
mat_tiers.push(Math.floor((tierNum-0.5) / 3)+1); //Trying to prevent round-off error, don't yell at me
|
|
|
|
let atkSpd = Base64.toInt(tag.substring(15));
|
|
|
|
let atkSpds = ["SLOW,NORMAL,FAST"];
|
|
|
|
let attackSpeed atkSpds[atkSpd];
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-19 17:32:27 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
2021-01-28 00:52:34 +00:00
|
|
|
constructor(recipe, mat_tiers, ingreds, attackSpeed, hash) {
|
2021-01-19 17:32:27 +00:00
|
|
|
this.recipe = recipe;
|
|
|
|
this.mat_tiers = mat_tiers;
|
|
|
|
this.ingreds = ingreds;
|
|
|
|
this.statMap = new Map(); //can use the statMap as an expanded Item
|
2021-01-21 18:30:22 +00:00
|
|
|
this.atkSpd = attackSpeed;
|
2021-01-28 00:52:34 +00:00
|
|
|
this.hash = hash;
|
2021-01-19 17:32:27 +00:00
|
|
|
this.initCraftStats();
|
|
|
|
}
|
2021-01-29 22:11:57 +00:00
|
|
|
|
|
|
|
|
2021-01-19 17:32:27 +00:00
|
|
|
|
2021-01-28 00:52:34 +00:00
|
|
|
setHash(hash) {
|
|
|
|
this.hash = hash;
|
|
|
|
this.statMap.set("displayName", "CR-" + this.hash);
|
|
|
|
}
|
2021-01-19 17:32:27 +00:00
|
|
|
/* 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());
|
2021-01-28 00:52:34 +00:00
|
|
|
statMap.set("displayName", "CR-" + this.hash); //TODO: DISPLAY THE HASH
|
2021-01-19 17:32:27 +00:00
|
|
|
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);
|
2021-01-28 00:52:34 +00:00
|
|
|
for (const e of skp_elements) {
|
|
|
|
statMap.set(e + "Dam", "0-0");
|
|
|
|
statMap.set(e + "Def", 0);
|
|
|
|
}
|
|
|
|
for (const e of skp_order) {
|
|
|
|
statMap.set(e + "Req", 0)
|
|
|
|
}
|
2021-01-19 17:32:27 +00:00
|
|
|
|
|
|
|
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("-"));
|
2021-01-19 18:30:47 +00:00
|
|
|
statMap.set("duration", this.recipe.get("basicDuration"));
|
2021-01-19 17:32:27 +00:00
|
|
|
}
|
|
|
|
statMap.set("category","consumable");
|
|
|
|
} else {
|
2021-01-28 00:52:34 +00:00
|
|
|
statMap.set("charges", 0);
|
2021-01-19 17:32:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2021-01-28 00:52:34 +00:00
|
|
|
statMap.set(e + "DamLow", "0-0");
|
2021-01-19 17:32:27 +00:00
|
|
|
}
|
|
|
|
//statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]);
|
|
|
|
statMap.set("category","weapon");
|
2021-01-21 18:30:22 +00:00
|
|
|
statMap.set("atkSpd",this.atkSpd);
|
2021-01-19 17:32:27 +00:00
|
|
|
}
|
2021-01-28 00:52:34 +00:00
|
|
|
statMap.set("powders",[]);
|
2021-01-21 21:15:05 +00:00
|
|
|
|
2021-01-19 17:32:27 +00:00
|
|
|
/* Change certain IDs based on material tier.
|
|
|
|
healthOrDamage changes.
|
|
|
|
duration and durability change. (but not basicDuration)
|
|
|
|
|
|
|
|
*/
|
2021-01-21 21:15:05 +00:00
|
|
|
let matmult = 1;
|
2021-01-29 22:11:57 +00:00
|
|
|
let sorted = this.mat_tiers.slice().sort(function(a, b){return a - b});
|
2021-01-28 00:52:34 +00:00
|
|
|
//TODO - MAT MULTIPLIERS ARE SUS FOR NON-MIXING TIERS.
|
2021-01-21 21:15:05 +00:00
|
|
|
if( sorted[0] == 1 && sorted[1] == 1) {
|
|
|
|
matmult = 1;
|
|
|
|
} else if( sorted[0] == 1 && sorted[1] == 2) {
|
2021-01-29 22:11:57 +00:00
|
|
|
matmult = 1.09;
|
2021-01-21 21:15:05 +00:00
|
|
|
}else if( sorted[0] == 1 && sorted[1] == 3) {
|
|
|
|
matmult = 1.15;
|
|
|
|
}else if( sorted[0] == 2 && sorted[1] == 2) {
|
2021-01-27 04:32:00 +00:00
|
|
|
matmult = 1.25;
|
2021-01-21 21:15:05 +00:00
|
|
|
}else if( sorted[0] == 2 && sorted[1] == 3) {
|
|
|
|
matmult = 1.3;
|
|
|
|
}else if( sorted[0] == 3 && sorted[1] == 3) {
|
|
|
|
matmult = 1.4;
|
|
|
|
}
|
|
|
|
let low = this.recipe.get("healthOrDamage")[0];
|
|
|
|
let high = this.recipe.get("healthOrDamage")[1];
|
|
|
|
if (statMap.get("category") === "consumable") {
|
|
|
|
//duration modifier
|
|
|
|
if(statMap.has("hp")) { //hack
|
|
|
|
statMap.set("hp", Math.floor( low * matmult )+ "-" + Math.floor( high * matmult ));
|
|
|
|
} else {
|
|
|
|
statMap.set("duration", [Math.floor( statMap.get("duration")[0] * matmult ), Math.floor( statMap.get("duration")[1] * matmult )]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//durability modifier
|
|
|
|
statMap.set("durability", [Math.floor( statMap.get("durability")[0] * matmult ), Math.floor( statMap.get("durability")[1] * matmult )]);
|
|
|
|
}
|
|
|
|
if (statMap.get("category") === "weapon") {
|
|
|
|
//attack damages oh boy
|
2021-01-28 00:52:34 +00:00
|
|
|
let ratio = 2.05; //UNSURE IF THIS IS HOW IT'S DONE.
|
|
|
|
if (this['atkSpd'] === "SLOW") {
|
2021-01-21 21:15:05 +00:00
|
|
|
ratio /= 1.5;
|
2021-01-28 00:52:34 +00:00
|
|
|
} else if (this['atkSpd'] === "NORMAL") {
|
2021-01-21 21:15:05 +00:00
|
|
|
ratio = 1;
|
2021-01-28 00:52:34 +00:00
|
|
|
} else if (this['atkSpd'] === "FAST") {
|
2021-01-21 21:15:05 +00:00
|
|
|
ratio /= 2.5;
|
|
|
|
}
|
2021-01-29 22:11:57 +00:00
|
|
|
let nDamBaseLow = Math.floor(low * matmult);
|
|
|
|
let nDamBaseHigh = Math.floor(high * matmult);
|
|
|
|
nDamBaseLow = Math.floor(nDamBaseLow * ratio);
|
|
|
|
nDamBaseHigh = Math.floor(nDamBaseHigh * ratio);
|
|
|
|
let elemDamBaseLow = [0,0,0,0,0];
|
|
|
|
let elemDamBaseHigh = [0,0,0,0,0];
|
2021-01-28 00:52:34 +00:00
|
|
|
/*
|
|
|
|
* APPLY POWDERS - MAY NOT BE CORRECT
|
|
|
|
*/
|
2021-01-29 22:11:57 +00:00
|
|
|
let powders = [];
|
2021-01-28 00:52:34 +00:00
|
|
|
for (let n in this.ingreds) {
|
|
|
|
let ingred = this.ingreds[n];
|
|
|
|
if (ingred.get("isPowder")) {
|
2021-01-29 22:11:57 +00:00
|
|
|
powders.push(ingred.get("pid"));
|
2021-01-28 00:52:34 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-29 22:11:57 +00:00
|
|
|
for (const p of powders) {
|
|
|
|
/* Powders as ingredients in crafted weapons are different than powders applied to non-crafted weapons. Thanks to nbcss for showing me the math.
|
|
|
|
*/
|
|
|
|
let powder = powderStats[p]; //use min, max, and convert
|
|
|
|
let element = Math.floor((p+0.01)/6); //[0,4], the +0.01 attempts to prevent division error
|
|
|
|
let diffLow = Math.floor(nDamBaseLow * powder.convert/100);
|
|
|
|
nDamBaseLow -= diffLow;
|
|
|
|
elemDamBaseLow[element] += diffLow + Math.floor( (powder.min + powder.max) / 2 );
|
|
|
|
let diffHigh = Math.floor(nDamBaseHigh * powder.convert/100);
|
|
|
|
nDamBaseHigh -= diffHigh;
|
|
|
|
elemDamBaseHigh[element] += diffHigh + Math.floor( (powder.min + powder.max) / 2 );
|
2021-01-28 00:52:34 +00:00
|
|
|
}
|
|
|
|
|
2021-01-29 22:11:57 +00:00
|
|
|
/* I create a separate variable for each low damage range because we need one damage range to calculate damage with, and it's custom to use the maximum range of the range range.
|
|
|
|
*/
|
|
|
|
let low1 = Math.floor(nDamBaseLow * 0.9);
|
|
|
|
let low2 = Math.floor(nDamBaseLow * 1.1);
|
|
|
|
let high1 = Math.floor(nDamBaseHigh * 0.9);
|
|
|
|
let high2 = Math.floor(nDamBaseHigh * 1.1);
|
|
|
|
statMap.set("nDamLow", low1+"-"+low2);
|
|
|
|
statMap.set("nDam", high1+"-"+high2);
|
|
|
|
for (const e in skp_elements) {
|
|
|
|
low1 = Math.floor(elemDamBaseLow[e] * 0.9);
|
|
|
|
low2 = Math.floor(elemDamBaseLow[e] * 1.1);
|
|
|
|
high1 = Math.floor(elemDamBaseHigh[e] * 0.9);
|
|
|
|
high2 = Math.floor(elemDamBaseHigh[e] * 1.1);
|
|
|
|
statMap.set(skp_elements[e]+"DamLow", low1+"-"+low2);
|
|
|
|
statMap.set(skp_elements[e]+"Dam",high1+"-"+high2);
|
|
|
|
}
|
2021-01-28 00:52:34 +00:00
|
|
|
} else if (statMap.get("category") === "armor") {
|
|
|
|
for (let n in this.ingreds) {
|
|
|
|
let ingred = this.ingreds[n];
|
|
|
|
if (ingred.get("isPowder")) {
|
|
|
|
let powder = powderStats[ingred.get("pid")];
|
|
|
|
let name = powderNames.get(ingred.get("pid"));
|
|
|
|
statMap.set(name.charAt(0) + "Def", (statMap.get(name.charAt(0)+"Def") || 0) + powder["defPlus"]);
|
|
|
|
statMap.set(skp_elements[(skp_elements.indexOf(name.charAt(0)) + 4 )% 5] + "Def", (statMap.get(skp_elements[(skp_elements.indexOf(name.charAt(0)) + 4 )% 5]+"Def") || 0) - powder["defMinus"]);
|
|
|
|
}
|
|
|
|
}
|
2021-01-28 08:01:34 +00:00
|
|
|
low = Math.floor(low * matmult);
|
|
|
|
high = Math.floor(high * matmult);
|
|
|
|
statMap.set("hp",low+"-"+high);
|
2021-01-21 21:15:05 +00:00
|
|
|
}
|
|
|
|
/* END SECTION */
|
2021-01-19 17:32:27 +00:00
|
|
|
|
|
|
|
//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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-19 19:53:41 +00:00
|
|
|
|
2021-01-19 17:32:27 +00:00
|
|
|
//apply ingredient effectivness - on ids, and reqs (itemIDs). NOT on durability, duration, or charges.
|
|
|
|
let eff_flat = eff.flat();
|
2021-01-20 05:59:17 +00:00
|
|
|
statMap.set("ingredEffectiveness", eff_flat);
|
2021-01-19 17:32:27 +00:00
|
|
|
//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") {
|
2021-01-28 00:52:34 +00:00
|
|
|
if (!ingred.get("isPowder")) {
|
|
|
|
statMap.set(key, Math.floor(statMap.get(key) + value*eff_mult)); //CHECK IF THIS IS CORRECT
|
|
|
|
} else {
|
|
|
|
statMap.set(key, Math.floor(statMap.get(key) + value));
|
|
|
|
}
|
2021-01-19 17:32:27 +00:00
|
|
|
} 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)];
|
2021-01-29 22:11:57 +00:00
|
|
|
rolls = rolls.map(x => Math.floor(x * eff_mult)).sort(function(a, b){return a - b});
|
2021-01-19 17:32:27 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-20 05:59:17 +00:00
|
|
|
for (const e of skp_order) {
|
2021-01-19 17:32:27 +00:00
|
|
|
statMap.set(e,statMap.get("maxRolls").get(e));
|
|
|
|
}
|
2021-01-20 05:59:17 +00:00
|
|
|
for (const d in statMap.get("durability")) {
|
|
|
|
if(statMap.get("durability")[d] < 1) { statMap.get("durability")[d] = 1;}
|
|
|
|
}
|
|
|
|
for (const d in statMap.get("duration")) {
|
|
|
|
if(statMap.get("duration")[d] < 1) { statMap.get("duration")[d] = 1;}
|
|
|
|
}
|
|
|
|
if(statMap.has("charges") && statMap.get("charges") < 1 ) { statMap.set("charges",1)}
|
2021-01-19 17:32:27 +00:00
|
|
|
this.statMap = statMap;
|
|
|
|
}
|
|
|
|
}
|