diff --git a/TODO.txt b/TODO.txt
index 86770f5..df9e4f7 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,5 +1,6 @@
Item identifications
- Base max/min, rounding correctly, and handling pre-ID
+ - Appears to be working correctly. Need to continue checking.
Damage calculation
- General calculation framework
@@ -17,3 +18,1393 @@ Build encoding
LATER STUFF:
- Custom items integration
- wynndata parse? And/or google sheets
+- Crafted Items
+
+"use strict";
+$(function () {
+ // some globals
+ let globalItemDb = {};
+ let globalHashItemDb = {};
+ let dropdowns = [
+ ["helmet_select", "helmet"],
+ ["chestplate_select", "chestplate"],
+ ["leggings_select", "leggings"],
+ ["boots_select", "boots"],
+ ["ring0_select", "ring"],
+ ["ring1_select", "ring"],
+ ["bracelet_select", "bracelet"],
+ ["necklace_select", "necklace"],
+ ["weapon_select", "weapon"]
+ ];
+ let correctOrder = ["helmet", "chestplate", "leggings", "boots", "ring", "ring", "bracelet", "necklace", "weapon"];
+ // [suffix, id]
+ let idMap = {
+ damage: {
+ spellPercent: ["%", "Spell Damage"],
+ spellRaw: ["", "Neutral Spell Damage"],
+ meleePercent: ["%", "Main Attack Damage"],
+ meleeRaw: ["", "Main Attack Neutral Damage"],
+ earth: ["%", "Earth Damage"],
+ thunder: ["%", "Thunder Damage"],
+ water: ["%", "Water Damage"],
+ fire: ["%", "Fire Damage"],
+ air: ["%", "Air Damage"],
+ },
+ regen: {
+ healthPercent: ["%", "Health Regen"],
+ healthRaw: ["", "Health Regen"],
+ mana: ["/4s", "Mana Regen"],
+ soulPoint: ["%", "Soul Pint Regen"]
+ },
+ steal: {
+ mana: ["/4s", "Mana Steal"],
+ health: ["/4s", "Life Steal"]
+ },
+ others: {
+ attackSpeedBonus: [" Tier", "Attack Speed"],
+ exploding: ["%", "Exploding"],
+ healthBonus: ["", "Health"],
+ jumpHeight: ["", "Jump Height"],
+ lootBonus: ["%", "Loot Bonus"],
+ lootQuality: ["%", "Loot Quality"],
+ poison: ["/3s", "Poison"],
+ reflection: ["%", "Reflection"],
+ sprint: ["%", "Sprint"],
+ sprintRegen: ["%", "Sprint Regen"],
+ stealing: ["%", "Stealing"],
+ thorns: ["%", "Thorns"],
+ walkSpeed: ["%", "Walk Speed"],
+ xpBonus: ["%", "XP Bonus"]
+ }
+ };
+ // min dmg, max dmg, conversion, +def, -def, powder code
+ let powderStats = {
+ E: [["earth","air"],[3,6,17,2,1,'0'],[6,9,21,4,2,'1'],[8,14,25,8,3,'2'],[11,16,31,14,5,'3'],[15,18,38,22,9,'4'],[18,22,46,30,13,'5']],
+ T: [["thunder","earth"],[1,8,9,3,1,'6'],[1,13,11,5,1,'7'],[2,18,14,9,2,'8'],[3,24,17,14,4,'9'],[3,32,22,20,7,'A'],[5,40,28,28,10,'B']],
+ W: [["water","thunder"],[3,4,13,3,1,'C'],[4,7,15,6,1,'D'],[6,10,17,11,2,'E'],[8,12,21,18,4,'F'],[11,14,26,28,7,'G'],[13,17,32,40,10,'H']],
+ F: [["fire","water"],[2,5,14,3,1,'J'],[4,8,16,5,2,'K'],[6,10,19,9,3,'L'],[9,13,24,16,5,'M'],[12,16,30,25,9,'N'],[15,19,37,36,13,'P']],
+ A: [["air","fire"],[2,6,11,3,1,'Q'],[4,9,14,6,2,'R'],[7,10,17,10,3,'S'],[9,13,22,16,5,'T'],[13,18,28,24,9,'U'],[16,18,35,34,13,'W']]
+ };
+ let skillBounsPct = [
+ 0.0, 1.0, 2.0, 2.9, 3.9, 4.9, 5.8, 6.7, 7.7, 8.6,
+ 9.5,10.4,11.3,12.2,13.1,13.9,14.8,15.7,16.5,17.3,
+ 18.2,19.0,19.8,20.6,21.4,22.2,23.0,23.8,24.6,25.3,
+ 26.1,26.8,27.6,28.3,29.0,29.8,30.5,31.2,31.9,32.6,
+ 33.3,34.0,34.6,35.3,36.0,36.6,37.3,37.9,38.6,39.2,
+ 39.9,40.5,41.1,41.7,42.3,42.9,43.5,44.1,44.7,45.3,
+ 45.8,46.4,47.0,47.5,48.1,48.6,49.2,49.7,50.3,50.8,
+ 51.3,51.8,52.3,52.8,53.4,53.9,54.3,54.8,55.3,55.8,
+ 56.3,56.8,57.2,57.7,58.1,58.6,59.1,59.5,59.9,60.4,
+ 60.8,61.3,61.7,62.1,62.5,62.9,63.3,63.8,64.2,64.6,
+ 65.0,65.4,65.7,66.1,66.5,66.9,67.3,67.6,68.0,68.4,
+ 68.7,69.1,69.4,69.8,70.1,70.5,70.8,71.2,71.5,71.8,
+ 72.2,72.5,72.8,73.1,73.5,73.8,74.1,74.4,74.7,75.0,
+ 75.3,75.6,75.9,76.2,76.5,76.8,77.1,77.3,77.6,77.9,
+ 78.2,78.4,78.7,79.0,79.2,79.5,79.8,80.0,80.3,80.5,80.8];
+ let elemIcons = {
+ earth: "✤",
+ thunder: "✦",
+ water: "❉",
+ fire: "✹",
+ air: "❋"
+ };
+ let elemColours = {
+ earth: "dark_green",
+ thunder: "yellow",
+ water: "aqua",
+ fire: "red",
+ air: "white"
+ };
+ let elementList = ["earth", "thunder", "water", "fire", "air"];
+ let skillList = ["strength", "dexterity", "intelligence", "defense", "agility"];
+ let itemListBox = $("#item_list_box");
+ let currentReq = {
+ req: {
+ strength: 0,
+ dexterity: 0,
+ intelligence: 0,
+ defense: 0,
+ agility: 0
+ },
+ bonus: {
+ strength: 0,
+ dexterity: 0,
+ intelligence: 0,
+ defense: 0,
+ agility: 0
+ },
+ order: []
+ }; // for the wearables
+ let realReq = {
+ req: {
+ strength: 0,
+ dexterity: 0,
+ intelligence: 0,
+ defense: 0,
+ agility: 0
+ },
+ bonus: {
+ strength: 0,
+ dexterity: 0,
+ intelligence: 0,
+ defense: 0,
+ agility: 0
+ },
+ order: []
+ }; // for the wearables+wep
+ let powderList = {helmet: [], chestplate: [], leggings: [], boots: [], weapon: []};
+
+ console.log("window ready");
+ // load item db
+ loadItemDb().then(itemDb => {
+ itemDb.forEach(item => globalItemDb[item.displayName || item.info.name] = item);
+ itemDb.forEach(item => globalHashItemDb[item.info.hash] = item);
+
+ // add powder button handlers
+ $("span.powder").click(e => {
+ let type = e.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.id.replace("_powders", "");
+ let powder = e.target.classList[1].substr(7).toUpperCase();
+ let item = globalItemDb[$(`#${type}_select > select`).val()];
+ let sockets = item.info.sockets;
+ let powderArray = powderList[type];
+ for (let i = 0; i < sockets; i++) {
+ if (powderArray.length <= i) {
+ powderArray.push(powder);
+ break;
+ }
+ if (!powderArray[i]) {
+ powderArray[i] = powder;
+ break;
+ }
+ }
+ let powderBox = $(`#${type}_powders`);
+ let powderListBox = powderBox.find("div > div.powder_list");
+ renderSockets(powderListBox, type, realReq);
+ });
+ $('.sp_input').change(() => {
+ let build = dropdowns.map(dropdown => {
+ let select = $("#" + dropdown[0] + " > select");
+ let name = select.val();
+ return {name, powder: powderList[dropdown[1]]};
+ });
+ let total = $('.sp_input').map((i, v) => 1*v.value).toArray().reduce((a, b) => a + b);
+ $('#sp_remaining').html(`Assign Skill Points (${200 - total} remaining):`);
+ renderBuild(calculateBuild(build));
+ });
+ $('.reset_button').click(() => {
+ $('.sp_input').map((i, v) => v.value = v.getAttribute("min"));
+ $('.sp_input[data-slot=0]').change();
+ });
+ $('#copy_btn').click(function (e) {
+ let copyText = $('#link_box');
+ copyText.select();
+ document.execCommand("copy");
+ $(e.target).html('Copied!');
+ });
+
+ // load into dropdown menus
+ let readySelects = 0;
+ dropdowns.forEach((dropdown) => {
+ let select = $("#" + dropdown[0] + " > select");
+ itemDb.filter((x) => (x.info.type || x.accessoryType).toLowerCase() === dropdown[1] || x.category.toLowerCase() === dropdown[1])
+ .map((x) => x.displayName || x.info.name).sort().forEach(name => {
+ select.append(`${name} `);
+ });
+ select.change((e, o) => {
+ let build = dropdowns.map(dropdown => {
+ let select = $("#" + dropdown[0] + " > select");
+ let name = select.val();
+ return {name, powder: powderList[dropdown[1]]};
+ });
+ let calculatedBuild = calculateBuild(build);
+ let parentId = e.target.parentElement.id;
+ let type = parentId.replace("_select", "");
+ let index = correctOrder.indexOf(type.replace(/\d/, ""));
+ if (o && !o.deferCalc) {
+ if (type !== "weapon") {
+ currentReq = findStatReq(calculatedBuild.items);
+ realReq = JSON.parse(JSON.stringify(currentReq));
+ }
+ if (calculatedBuild.items[8]) {
+ // take the skills we have and see what else do we need
+ let weaponItem = calculatedBuild.items[8];
+ skillList.forEach(skill => {
+ let ownedPoints = currentReq.req[skill] + currentReq.bonus[skill];
+ let diff;
+ if (!weaponItem.req[skill]) {
+ diff = 0;
+ } else if (weaponItem.req[skill] > ownedPoints) {
+ diff = weaponItem.req[skill] - ownedPoints;
+ } else {
+ diff = 0;
+ }
+ realReq.req[skill] = currentReq.req[skill] + diff;
+ });
+ let bonus = {};
+ skillList.forEach(skill => {
+ bonus[skill] = currentReq.bonus[skill] + weaponItem.base.skill[skill];
+ });
+ realReq.bonus = bonus;
+ realReq.order = currentReq.order;
+ }
+ skillList.forEach((skill, i) => {
+ let spInput = $(`.sp_input[data-slot=${i}]`);
+ spInput.attr("min", realReq.req[skill]);
+ spInput.val(realReq.req[skill]);
+ });
+ }
+ let item = calculatedBuild.items[index];
+ let box = $(`#${type}_div`).empty();
+ let powderBox = $(`#${type}_powders`);
+ if (item && item.info.sockets) {
+ box.append(generateItemBox(item, false));
+ powderBox.show();
+ powderBox.find("div > p.large").text(item.displayName || item.info.name).attr("class", "large item_name " + item.info.tier.toLowerCase());
+ let powderListBox = powderBox.find("div > div.powder_list");
+ renderSockets(powderListBox, type);
+ } else {
+ powderBox.hide();
+ renderBuild(calculatedBuild);
+ }
+ // $(`#${type}_div`).empty();
+ });
+ select.on("chosen:ready", () => {
+ ++readySelects;
+ if (readySelects == 9) {
+ let query = document.location.search.substr(1);
+ if (query.split('-').length === 14) {
+ let _s = query.split('-');
+ let itemNames = _s.slice(0, 9).map(h => (globalHashItemDb[h] || {info: {name: ""}}).info.name);
+ let powders = [];
+ let selects = $('.item_select > select');
+ for (let i = 0; i < 5; ++i) {
+ let _pows = _s[9 + i];
+ let pows = [];
+ for (let j = 0; j < _pows.length; ++j) {
+ let idx = '0123456789ABCDEFGHJKLMNPQRSTUW'.indexOf(_pows[j]);
+ pows.push('ETWFA'[Math.floor(idx/6)]+(1+(idx%6)));
+ }
+ powders.push(pows);
+ }
+ for (let i = 0; i < 9; ++i) {
+ $(selects[i]).val(itemNames[i]).trigger('chosen:updated');
+ }
+ powderList.helmet = powders[0];
+ powderList.chestplate = powders[1];
+ powderList.leggings = powders[2];
+ powderList.boots = powders[3];
+ powderList.weapon = powders[4];
+ for (let i = 0; i < 5; ++i) {
+ for (let j = 0; j < powders[i].length; ++j) {
+ $(`.powder_choices:eq(${i})`).find(`.powder_${powders[i][j].toLowerCase()}`).click();
+ }
+ }
+ $(selects[0]).trigger("change", {deferCalc: true});
+ $(selects[1]).trigger("change", {deferCalc: true});
+ $(selects[2]).trigger("change", {deferCalc: true});
+ $(selects[3]).trigger("change", {deferCalc: true});
+ $(selects[7]).trigger("change", {deferCalc: false});
+ $(selects[8]).trigger("change", {deferCalc: false});
+
+ window.selects = selects;
+ $('.reset_button').click();
+ }
+ }
+ });
+ select.chosen({width: "100%"}).addClass("col-md-3 col-sm-4");
+ });
+ $(window).resize(resetPos);
+ });
+
+ function spellDamage(build, totalSkills, spellMultiplier, elemMultiplier) {
+ let weaponDamage = JSON.parse(JSON.stringify(build.base.damage));
+ for (let i in weaponDamage) {
+ weaponDamage[i] = weaponDamage[i].split("-").map(x => 1*x);
+ }
+ // consider wep powders, move elemMultiplier.neutral to the respective elem
+ powderList.weapon.forEach(powder => {
+ if (!powder || !elemMultiplier.neutral) {
+ return;
+ }
+ let stat = powderStats[powder[0]][powder[1]];
+ let conversion = stat[2];
+ if (elemMultiplier.neutral > conversion) {
+ elemMultiplier.neutral -= conversion;
+ elemMultiplier[powderStats[powder[0]][0][0]] += conversion;
+ } else {
+ elemMultiplier[powderStats[powder[0]][0][0]] += elemMultiplier.neutral;
+ elemMultiplier.neutral = 0;
+ }
+ });
+ // base damage is `neutral * multiplier + elemDamageOnWep` then multiply by attack speed multiplier, then spell multiplier
+ let base = {};
+ let attackSpeedMultiplier = {SUPER_SLOW: 0.51, VERY_SLOW: 0.83, SLOW: 1.5, NORMAL: 2.05, FAST: 2.5, VERY_FAST: 3.1, SUPER_FAST: 4.3}[build.base.attackSpeed];
+ base.neutral = weaponDamage.neutral.map(x => x * elemMultiplier.neutral / 100 * attackSpeedMultiplier * spellMultiplier / 100);
+ elementList.forEach(elem => {
+ base[elem] = [0, 1].map(i => (weaponDamage.neutral[i] * elemMultiplier[elem]) / 100 + weaponDamage[elem][i])
+ .map(x => x * attackSpeedMultiplier * spellMultiplier / 100);
+ });
+ // console.log(base);
+ // multiply by (1 + spellDamage + strengthPercentage + elemSkillPercentage + elemDamagePercentage)
+ let normal = {};
+ normal.neutral = base.neutral.map(x => (x * (100 + build.identification.damage.spellPercent + skillBounsPct[totalSkills[0]]) + build.identification.damage.spellRaw * spellMultiplier) / 100).map(Math.floor);
+ normal.total = [normal.neutral[0], normal.neutral[1]];
+ elementList.forEach((elem, i) => {
+ normal[elem] = base[elem].map(x => x * (100 + build.identification.damage.spellPercent + skillBounsPct[totalSkills[0]] + skillBounsPct[totalSkills[i]] + build.identification.damage[elem]) / 100).map(Math.floor);
+ normal.total = sum(normal.total, normal[elem]);
+ });
+ // for crit hits change the 1 to 2
+ let critical = {};
+ critical.neutral = base.neutral.map(x => (x * (200 + build.identification.damage.spellPercent + skillBounsPct[totalSkills[0]]) + build.identification.damage.spellRaw * spellMultiplier) / 100).map(Math.floor);
+ critical.total = [critical.neutral[0], critical.neutral[1]];
+ elementList.forEach((elem, i) => {
+ critical[elem] = base[elem].map(x => x * (200 + build.identification.damage.spellPercent + skillBounsPct[totalSkills[0]] + skillBounsPct[totalSkills[i]] + build.identification.damage[elem]) / 100).map(Math.floor);
+ critical.total = sum(critical.total, critical[elem]);
+ });
+ let args = [build.identification.damage.spellPercent, elementList.map(elem => build.identification.damage[elem]), skillBounsPct[totalSkills[0]], totalSkills.map(x => skillBounsPct[x])];
+ return {normal, critical, args};
+ }
+
+ function renderSockets(powderListBox, type) {
+ let box = $(`#${type}_div`).empty();
+ let powderBox = $(`#${type}_powders`);
+ let index = correctOrder.indexOf(type);
+
+ let build = dropdowns.map(dropdown => {
+ let select = $("#" + dropdown[0] + " > select");
+ let name = select.val();
+ return {name, powder: powderList[dropdown[1]]};
+ });
+ let calculatedBuild = calculateBuild(build);
+ let item = calculatedBuild.items[index];
+ let sockets = item.info.sockets;
+ if (sockets) {
+ box.append(generateItemBox(item, false));
+ powderBox.show();
+ powderBox.find("div > p.large").text(item.displayName || item.info.name).attr("class", "large item_name " + item.info.tier.toLowerCase());
+ let powderListBox = powderBox.find("div > div.powder_list");
+ powderListBox.empty();
+ for (let i = 0; i < sockets; i++) {
+ let powderBox;
+ if (powderList[type].length <= i || !powderList[type][i]) {
+ powderBox = $(`
`);
+ } else {
+ powderBox = $(`
`);
+ powderBox.click(() => {
+ powderList[type][i] = undefined;
+ renderSockets(powderListBox, type);
+ });
+ }
+ powderListBox.append(powderBox);
+ }
+ } else {
+ powderBox.hide();
+ }
+ renderBuild(calculatedBuild);
+ }
+
+ async function loadItemDb() {
+ // load from server lol, maybe cached tho
+ return await fetch("/build/itemdb.json").then(data => data.json()).then(json => json.itemDB)
+ }
+
+ /**
+ * Calculates the build"s stats according to the items and skill points allocated.
+ * Normally we use the item"s base stats as outlined in itemDB, but stats can be overridden,
+ * even if the override is outside of the normal range we won"t check
+ * @param items The items equipped as an array
+ */
+ function calculateBuild(items) {
+ // check for the correct types [helm, chest, leggings, boots, ring, ring, bracelet, necklace, wep]
+ let totalIdentifications = {};
+ let totalBase = {};
+ let totalReq = {quest: []};
+ let realItems = [];
+ let conversion = {neutral: 100, earth: 0, thunder: 0, water: 0, fire: 0, air: 0};
+ for (let i = 0; i < 9; i++) {
+ if (!items[i]) {
+ realItems.push(null);
+ continue;
+ }
+ let name = items[i].name;
+ let item = globalItemDb[name];
+ if (!item) {
+ realItems.push(null);
+ if (name.length) {
+ console.warn("Item " + name + " not found");
+ }
+ continue;
+ }
+ if ((item.info.type || item.accessoryType).toLowerCase() !== correctOrder[i] && item.category.toLowerCase() !== correctOrder[i]) {
+ return null;
+ }
+ // must clone since we don"t want to override the base item
+ let realItem = JSON.parse(JSON.stringify(item));
+ // this item passed the check, populate it with the real one
+ override(realItem.identification, items.override);
+ // powders
+ if (Array.isArray(items[i].powder)) {
+ let powders = items[i].powder.slice(0, realItem.info.sockets);
+ realItem.powders = powders;
+
+ for (let j = 0; j < powders.length; j++) {
+ if (!powders[j]) {
+ continue;
+ }
+ let elem = powders[j][0];
+ let tier = powders[j][1];
+ let elemName = powderStats[elem][0][0];
+ let weakElemName = powderStats[elem][0][1];
+ let powder = powderStats[elem][tier];
+ let conversionPct = Math.min(conversion.neutral, powder[2]);
+ if (i === 8) {
+ // weapon, modify conversion and damage
+ conversion.neutral -= conversionPct;
+ conversion[elemName] += conversionPct;
+ let originalDamage = realItem.base.damage[elemName] || "0-0";
+ let [lower, upper] = originalDamage.split("-").map(x => parseInt(x));
+ // console.log(lower, upper, originalDamage);
+ lower += powder[0];
+ upper += powder[1];
+ realItem.base.damage[elemName] = lower + "-" + upper;
+ switch (realItem.info.type) {
+ case "Relik":
+ realItem.req.class = "Shaman";
+ break;
+ case "Wand":
+ realItem.req.class = "Mage";
+ break;
+ case "Spear":
+ realItem.req.class = "Warrior";
+ break;
+ case "Bow":
+ realItem.req.class = "Archer";
+ break;
+ case "Dagger":
+ realItem.req.class = "Assassin";
+ break;
+ }
+ } else {
+ // non weapon, modify defense
+ realItem.base.defense[elemName] += powder[3];
+ realItem.base.defense[weakElemName] -= powder[4];
+ }
+ }
+ }
+ // this goes after all the modifications
+ totalIdentifications = sum(totalIdentifications, realItem.identification);
+ totalBase = sum(totalBase, realItem.base);
+ totalReq = max(totalReq, realItem.req);
+
+ realItems.push(realItem);
+ }
+ // adjust health according to lvl req
+ totalBase.health = (totalBase.health || 0) + Math.max((totalReq.level || 1), 101) * 5 + 5;
+
+ if (realItems[8]) {
+ let totalConverted = [0, 0];
+ let displayDamage = JSON.parse(JSON.stringify(totalBase.damage));
+ for (let i in displayDamage) {
+ displayDamage[i] = displayDamage[i].split("-").map(x => 1*x);
+ }
+ elementList.forEach(elem => {
+ if (conversion[elem]) {
+ let converted = totalBase.damage.neutral.split("-").map(x => Math.round(x * conversion[elem] / 100));
+ [0, 1].forEach(i => {
+ if (converted[i] + totalConverted[i] > displayDamage.neutral[i]) {
+ converted[i] = displayDamage.neutral[i] - totalConverted[i];
+ }
+ });
+ displayDamage[elem] = sum(displayDamage[elem], converted);
+ totalConverted[0] += converted[0];
+ totalConverted[1] += converted[1];
+ }
+ });
+ displayDamage.neutral[0] -= totalConverted[0];
+ displayDamage.neutral[1] -= totalConverted[1];
+ realItems[8].displayDamage = displayDamage;
+ return {
+ identification: totalIdentifications,
+ base: totalBase,
+ req: totalReq,
+ items: realItems,
+ conversion,
+ displayDamage
+ };
+ }
+ return {
+ identification: totalIdentifications,
+ base: totalBase,
+ req: totalReq,
+ items: realItems,
+ conversion
+ };
+ }
+
+ function renderBuild(build) {
+ if (!build.items.filter(x => x).length) {
+ // empty build
+ return;
+ }
+ let buildCode = build.items.map(x => x ? x.info.hash : "");
+ [["helmet", 0], ["chestplate", 1], ["leggings", 2], ["boots", 3], ["weapon", 8]].forEach(v => {
+ let type = v[0];
+ let index = v[1];
+ let item = build.items[index];
+ if (item) {
+ let powderCode = "";
+ let sockets = Math.min(powderList[type].length, item.info.sockets);
+ for (let i = 0; i < sockets; i++) {
+ let powder = powderList[type][i];
+ powderCode += powderStats[powder[0]][powder[1]][5];
+ }
+ buildCode.push(powderCode);
+ } else {
+ buildCode.push("");
+ }
+ });
+ $("#link_box").val(`${location.protocol}//${location.host}${location.pathname}?${buildCode.join("-")}`);
+ history.replaceState({}, "", "?" + buildCode.join("-"));
+ itemListBox.empty();
+ build.items.forEach(item => {
+ if (item !== null) {
+ itemListBox.append(generateItemBox(item, true));
+ }
+ });
+ resetPos();
+ // requirements
+ let req = build.req;
+ let reqBox = $("#build_req > .build_content");
+ reqBox.empty();
+ if (req.quest.length) {
+ for (let i = 0; i < req.quest.length; i++) {
+ reqBox.append(`✓ Quest Req: ${req.quest[i]} `);
+ }
+ }
+ if (req.class) {
+ reqBox.append(`✓ Class Req: ${req.class} `);
+ }
+ if (req.level) {
+ reqBox.append(`✓ Combat Lv. Min: ${req.level} `);
+ }
+ skillList.forEach(skill => {
+ let value = req[skill];
+ if (value) {
+ reqBox.append(`✓ ${capitalize(skill)} Min: ${value} `);
+ }
+ });
+ // how to wear
+ let howToWearBox = $("#build_howto > .build_content");
+ howToWearBox.empty();
+ howToWearBox.append("Assign the follow Skill Points:
");
+ skillList.forEach(skill => {
+ howToWearBox.append(`${capitalize(skill)}: ${realReq.req[skill]} `);
+ });
+ howToWearBox.append("Then wear your items in the following order: ");
+ realReq.order.forEach((index, i) => {
+ howToWearBox.append(`${i + 1}. ${capitalize(correctOrder[index])} `);
+ });
+ if (build.items[8]) {
+ howToWearBox.append(`${realReq.order.length + 1}. Weapon `);
+ }
+ let {others, damage, regen, steal} = build.identification;
+ let {healthRaw, healthPercent} = regen;
+ // defenses
+ let defensesBox = $("#build_defs > .build_content");
+ defensesBox.empty();
+ defensesBox.empty();
+ defensesBox.append("");
+ defensesBox = defensesBox.children("table");
+ defensesBox.append(`❤ Health: ${build.base.health} `);
+ defensesBox.append(`❤ Health Regen: ${healthRaw} ${nToString(healthPercent)}% = ${Math.round(healthRaw + Math.abs(healthRaw) * healthPercent / 100)} `);
+ defensesBox.append(" ");
+ elementList.forEach(elem => {
+ let raw = build.base.defense[elem] || 0;
+ let percent = build.identification.defense[elem] || 0;
+ let final = Math.round(raw + Math.abs(raw) * percent / 100);
+ defensesBox.append(`${elemIcons[elem]} ${capitalize(elem)} Defense:${raw} ${nToString(percent)}% = ${final} `);
+ });
+ // other ids
+ let othersBox = $("#build_ids > .build_content");
+ othersBox.empty();
+ othersBox.append("");
+ othersBox = othersBox.children("table");
+ skillList.forEach(skill => {
+ if (realReq.bonus[skill]) {
+ let value = realReq.bonus[skill];
+ othersBox.append(`${capitalize(skill)} ${nToString(value)} `);
+ }
+ });
+ for (let i in damage) {
+ if (damage.hasOwnProperty(i)) {
+ let value = damage[i];
+ if (value) {
+ othersBox.append(`${idMap.damage[i][1]} ${nToString(value)}${idMap.damage[i][0]} `);
+ }
+ }
+ }
+ for (let i in regen) {
+ if (regen.hasOwnProperty(i)) {
+ if (i.startsWith('health')) {
+ continue;
+ }
+ let value = regen[i];
+ if (value) {
+ othersBox.append(`${idMap.regen[i][1]} ${nToString(value)}${idMap.regen[i][0]} `);
+ }
+ }
+ }
+ for (let i in steal) {
+ if (steal.hasOwnProperty(i)) {
+ let value = steal[i];
+ if (value) {
+ othersBox.append(`${idMap.steal[i][1]} ${nToString(value)}${idMap.steal[i][0]} `);
+ }
+ }
+ }
+ let ordinals = ["1st", "2nd", "3rd", "4th"];
+ let spellCostPct = build.identification.spellCost.percent;
+ let spellCostRaw = build.identification.spellCost.raw;
+ for (let i = 0; i < 4; i++) {
+ if (spellCostRaw[i]) {
+ othersBox.append(`${ordinals[i]} Spell Cost: ${nToString(spellCostRaw[i])} `);
+ }
+ if (spellCostPct[i]) {
+ othersBox.append(`${ordinals[i]} Spell Cost: ${nToString(spellCostPct[i])}% `);
+ }
+ }
+ for (let i in others) {
+ if (others.hasOwnProperty(i)) {
+ let value = others[i];
+ if (value) {
+ othersBox.append(`${idMap.others[i][1]} ${nToString(value)}${idMap.others[i][0]} `);
+ }
+ }
+ }
+ // get skills
+ let skills = $('input.sp_input').map((i, v) => 1*v.value).toArray();
+ skillList.forEach((v, i) => {
+ skills[i] += realReq.bonus[v] || 0;
+ skills[i] = Math.max(0, Math.min(skills[i], 150));
+ });
+ let damagesBox = $('#build_dmg > .build_content');
+ damagesBox.empty();
+ if (build.items[8]) {
+ // calculate damages
+ // melee
+ let weaponDamage = JSON.parse(JSON.stringify(build.displayDamage));
+ let {damage} = build.identification;
+ let meleeDamage = {normal: {}, critical: {}};
+ for (let i in weaponDamage) {
+ if (!weaponDamage.hasOwnProperty(i)) continue;
+ meleeDamage.normal.neutral = build.displayDamage.neutral.map(x => x * (100 + skillBounsPct[skills[0]] + damage.meleePercent) / 100 + damage.meleeRaw).map(Math.floor);
+ meleeDamage.critical.neutral = build.displayDamage.neutral.map(x => x * (200 + skillBounsPct[skills[0]] + damage.meleePercent) / 100 + damage.meleeRaw).map(Math.floor);
+ meleeDamage.normal.total = [meleeDamage.normal.neutral[0], meleeDamage.normal.neutral[1]];
+ meleeDamage.critical.total = [meleeDamage.critical.neutral[0], meleeDamage.critical.neutral[1]];
+ elementList.forEach((elem, i) => {
+ meleeDamage.normal[elem] = build.displayDamage[elem].map(x => x * (100 + skillBounsPct[skills[0]] + skillBounsPct[skills[i]] + damage.meleePercent + damage[elem]) / 100).map(Math.floor);
+ meleeDamage.critical[elem] = build.displayDamage[elem].map(x => x * (200 + skillBounsPct[skills[0]] + skillBounsPct[skills[i]] + damage.meleePercent + damage[elem]) / 100).map(Math.floor);
+ meleeDamage.normal.total = sum(meleeDamage.normal.total, meleeDamage.normal[elem]);
+ meleeDamage.critical.total = sum(meleeDamage.critical.total, meleeDamage.critical[elem]);
+ });
+ }
+
+ // spell
+ let spells = [];
+ switch (build.items[8].info.type.toLowerCase()) {
+ case "spear":
+ // warrior
+ // bash
+ spells.push({spell: "Bash", subtitle: "First Explosion", damage: spellDamage(build, skills, 130, {neutral: 60, earth: 40, thunder: 0, water: 0, fire: 0, air: 0})});
+ spells.push({spell: "Bash", subtitle: "Second Explosion", damage: spellDamage(build, skills, 130, {neutral: 100, earth: 0, thunder: 0, water: 0, fire: 0, air: 0})});
+ spells.push({spell: "Bash", subtitle: "Total Damage", primary: true, damage: sum(spells[0].damage, spells[1].damage)});
+ // charge
+ spells.push({spell: "Charge", subtitle: "", primary: true, damage: spellDamage(build, skills, 150, {neutral: 60, earth: 0, thunder: 0, water: 0, fire: 40, air: 0})});
+ // uppercut
+ spells.push({spell: "Uppercut", subtitle: "First Damage", damage: spellDamage(build, skills, 300, {neutral: 85, earth: 15, thunder: 0, water: 0, fire: 0, air: 0})});
+ spells.push({spell: "Uppercut", subtitle: "Fireworks Damage", damage: spellDamage(build, skills, 50, {neutral: 85, earth: 0, thunder: 15, water: 0, fire: 0, air: 0})});
+ spells.push({spell: "Uppercut", subtitle: "Comet Damage", damage: spellDamage(build, skills, 50, {neutral: 100, earth: 0, thunder: 0, water: 0, fire: 0, air: 0})});
+ spells.push({spell: "Uppercut", subtitle: "Total Damage", primary: true, damage: sum(sum(spells[4].damage, spells[5].damage), spells[6].damage)});
+ // war scream
+ spells.push({spell: "War Scream", subtitle: "Per Hit", primary: true, damage: spellDamage(build, skills, 50, {neutral: 0, earth: 0, thunder: 0, water: 0, fire: 75, air: 25})});
+ break;
+ case "bow":
+ // archer
+ // arrow storm
+ spells.push({spell: "Arrow Storm", subtitle: "Per Arrow", damage: spellDamage(build, skills, 10, {neutral: 60, earth: 0, thunder: 25, water: 0, fire: 15, air: 0})});
+ spells.push({spell: "Arrow Storm", subtitle: "Per 60 Arrows", primary: true, damage: multiply(spells[0].damage, 60)});
+ // escape
+ spells.push({spell: "Escape", subtitle: "", primary: true, damage: spellDamage(build, skills, 100, {neutral: 50, earth: 0, thunder: 0, water: 0, fire: 0, air: 50})});
+ // bomb
+ spells.push({spell: "Bomb", subtitle: "", primary: true, damage: spellDamage(build, skills, 250, {neutral: 60, earth: 25, thunder: 0, water: 0, fire: 15, air: 0})});
+ // arrow shield
+ spells.push({spell: "Arrow Shield", subtitle: "", primary: true, damage: spellDamage(build, skills, 100, {neutral: 70, earth: 0, thunder: 0, water: 30, fire: 0, air: 0})});
+ spells.push({spell: "Arrow Shield", subtitle: "Arrow Rain Damage", damage: spellDamage(build, skills, 250, {neutral: 70, earth: 0, thunder: 0, water: 0, fire: 0, air: 30})});
+ break;
+ case "wand":
+ // mage
+ // heal
+ spells.push({spell: "Heal", subtitle: "", primary: true, heal: [heal(build, 0.12), heal(build, 0.06), heal(build, 0.06)]});
+ // teleport
+ spells.push({spell: "Teleport", subtitle: "", primary: true, damage: spellDamage(build, skills, 100, {neutral: 60, earth: 0, thunder: 40, water: 0, fire: 0, air: 0})});
+ // meteor
+ spells.push({spell: "Meteor", subtitle: "Explosion Damage", primary: true, damage: spellDamage(build, skills, 500, {neutral: 40, earth: 30, thunder: 0, water: 0, fire: 30, air: 0})});
+ spells.push({spell: "Meteor", subtitle: "Burning Damage", damage: spellDamage(build, skills, 125, {neutral: 100, earth: 0, thunder: 0, water: 0, fire: 0, air: 0})});
+ // ice snake
+ spells.push({spell: "Ice Snake", subtitle: "", primary: true, damage: spellDamage(build, skills, 70, {neutral: 50, earth: 0, thunder: 0, water: 50, fire: 0, air: 0})});
+ break;
+ case "dagger":
+ // assassin
+ // spin attack
+ spells.push({spell: "Spin Attack", subtitle: "", primary: true, damage: spellDamage(build, skills, 150, {neutral: 70, earth: 0, thunder: 30, water: 0, fire: 0, air: 0})});
+ // multihit
+ spells.push({spell: "Multihit", subtitle: "First 10 Hits", damage: spellDamage(build, skills, 30, {neutral: 70, earth: 0, thunder: 30, water: 0, fire: 0, air: 0})});
+ spells.push({spell: "Multihit", subtitle: "Last Hit", damage: spellDamage(build, skills, 30, {neutral: 40, earth: 0, thunder: 30, water: 30, fire: 0, air: 0})});
+ spells.push({spell: "Multihit", subtitle: "Total Damage", primary: true, damage: sum(multiply(spells[1].damage, 10), spells[2].damage)});
+ // smoke bomb
+ spells.push({spell: "Smoke Bomb", subtitle: "Total Damage", primary: true, damage: spellDamage(build, skills, 60, {neutral: 50, earth: 25, thunder: 0, water: 0, fire: 0, air: 25})});
+ spells.push({spell: "Smoke Bomb", subtitle: "Per Second", damage: multiply(spells[4].damage, 5)});
+ break;
+ case "relik":
+ // shaman
+ // totem
+ spells.push({spell: "Totem", subtitle: "Damage Per Second", primary: true, damage: spellDamage(build, skills, 20, {neutral: 60, earth: 0, thunder: 0, water: 0, fire: 20, air: 20})});
+ spells.push({spell: "Totem", subtitle: "Heal Per Second", primary: true, heal: [heal(build, 0.04)]});
+ spells.push({spell: "Totem", subtitle: "Landing Damage", damage: spellDamage(build, skills, 100, {neutral: 100, earth: 0, thunder: 0, water: 0, fire: 0, air: 0})});
+ // haul
+ spells.push({spell: "Haul", subtitle: "", primary: true, damage: spellDamage(build, skills, 100, {neutral: 100, earth: 0, thunder: 0, water: 0, fire: 0, air: 0})});
+ // explosive blender
+ spells.push({spell: "Aura", subtitle: "Center Damage", primary: true, damage: spellDamage(build, skills, 200, {neutral: 70, earth: 0, thunder: 0, water: 30, fire: 0, air: 0})});
+ // uproot
+ spells.push({spell: "Uproot", subtitle: "", primary: true, damage: spellDamage(build, skills, 50, {neutral: 70, earth: 30, thunder: 0, water: 0, fire: 0, air: 0})});
+ }
+ spells.sort((a,b) => (b.primary||0)-(a.primary||0));
+ let div = $('
');
+ div.append(`Melee Damage `);
+ let {normal, critical} = meleeDamage;
+ let normalAvg = (normal.total[0] + normal.total[1]) / 2;
+ let criticalAvg = (critical.total[0] + critical.total[1]) / 2;
+ let critChance = skillBounsPct[skills[1]];
+ let avg = Math.round(normalAvg * (1 - critChance / 100) + criticalAvg * critChance / 100);
+ let attackSpeeds = ['SUPER_SLOW', 'VERY_SLOW', 'SLOW', 'NORMAL', 'FAST', 'VERY_FAST', 'SUPER_FAST'];
+ let hitsPerSec = [0.51, 0.83, 1.5, 2.05, 2.5, 3.4, 4.3];
+ let speedTier = Math.min(6, Math.max(0, attackSpeeds.indexOf(build.items[8].base.attackSpeed) + build.identification.others.attackSpeedBonus));
+ div.append(`${capitalize(attackSpeeds[speedTier])} Attack Speed `);
+ div.append(`DPS: ${Math.floor(avg * hitsPerSec[speedTier] + build.identification.others.poison * (1 + skillBounsPct[skills[0]] / 100) / 3)} `);
+ div.append(`Average Damage: ${avg} `);
+ let leftDamageBox = $(`Normal Damage Total: ${normal.total[0]} - ${normal.total[1]} (${Math.round(normalAvg)})✤ ${normal.neutral[0]} - ${normal.neutral[1]}
`);
+ let rightDamageBox = $(`Critical Damage Total: ${critical.total[0]} - ${critical.total[1]} (${Math.round(criticalAvg)})✤ ${critical.neutral[0]} - ${critical.neutral[1]}
`);
+ elementList.forEach(elem => {
+ if (normal[elem][1]) {
+ leftDamageBox.append(`${elemIcons[elem]} ${normal[elem][0]} - ${normal[elem][1]} `);
+ rightDamageBox.append(`${elemIcons[elem]} ${critical[elem][0]} - ${critical[elem][1]} `);
+ }
+ });
+ div.append(leftDamageBox);
+ div.append(rightDamageBox);
+ damagesBox.append(div);
+ spells.forEach(spell => {
+ if (spell.damage) {
+ let div = $('
');
+ div.append(`${spell.spell} `);
+ let {normal, critical} = spell.damage;
+ let normalAvg = (normal.total[0] + normal.total[1]) / 2;
+ let criticalAvg = (critical.total[0] + critical.total[1]) / 2;
+ let critChance = skillBounsPct[skills[1]];
+ let avg = Math.round(normalAvg * (1 - critChance / 100) + criticalAvg * critChance / 100);
+ if (spell.subtitle.length) {
+ div.append(`${spell.subtitle} `);
+ }
+ div.append(`Average Damage: ${avg} `);
+ let leftDamageBox = $(`Normal Damage Total: ${normal.total[0]} - ${normal.total[1]} (${Math.round(normalAvg)})✤ ${normal.neutral[0]} - ${normal.neutral[1]}
`);
+ let rightDamageBox = $(`Critical Damage Total: ${critical.total[0]} - ${critical.total[1]} (${Math.round(criticalAvg)})✤ ${critical.neutral[0]} - ${critical.neutral[1]}
`);
+ elementList.forEach(elem => {
+ if (normal[elem][1]) {
+ leftDamageBox.append(`${elemIcons[elem]} ${normal[elem][0]} - ${normal[elem][1]} `);
+ rightDamageBox.append(`${elemIcons[elem]} ${critical[elem][0]} - ${critical[elem][1]} `);
+ }
+ });
+ div.append(leftDamageBox);
+ div.append(rightDamageBox);
+ damagesBox.append(div);
+ }
+ if (spell.heal) {
+ let div = $('
');
+ let {heal} = spell;
+ div.append(`${spell.spell} `);
+ if (spell.subtitle.length) {
+ div.append(`${spell.subtitle} `);
+ }
+ switch (spell.spell) {
+ case "Heal":
+ div.append(`Total Heal: ${heal[0] + heal[1] + heal[2]} `);
+ for (let i = 0; i < 3; i++) {
+ div.append(`${['1st', '2nd', '3rd'][i]} Pulse: ${heal[i]} `);
+ }
+ case "Totem":
+ div.append(`Heal: ${heal[0]} `);
+ break;
+ }
+ damagesBox.append(div);
+ }
+ });
+ } else {
+ // no weapon to calculate damages
+ damagesBox.html('No weapon to selected.');
+ }
+ }
+
+ function heal(build, ratio) {
+ return Math.floor(build.base.health * ratio * (1 + build.identification.damage.water / 200));
+ }
+
+ function generateItemBox(item, floatLeft) {
+ let itemBox;
+ let newLine = false;
+ if (floatLeft) {
+ itemBox = $(`
`);
+ } else {
+ itemBox = $(`
`);
+ }
+ // 1. name coloured according to tier
+ itemBox.append(`${item.displayName || item.info.name} `);
+ // 2. weapon attack speed
+ if (item.category === "weapon") {
+ itemBox.append(`${capitalize(item.base.attackSpeed)} Attack Speed `);
+ }
+ itemBox.append(" ");
+ if (item.category !== "weapon") {
+ // 3. armour health
+ if (item.base.health) {
+ itemBox.append(`❤ Health: ${nToString(item.base.health)} `);
+ newLine = true;
+ }
+ // 4. armour defenses
+ elementList.forEach(elem => {
+ let value = item.base.defense[elem];
+ if (value) {
+ itemBox.append(`${elemIcons[elem]} ${capitalize(elem)} Defense: ${nToString(value)} `);
+ newLine = true;
+ }
+ });
+ } else {
+ // 5. weapon damage
+ if (item.displayDamage.neutral[1] !== 0) {
+ itemBox.append(`${elemIcons.earth} Neutral Damage: ${item.displayDamage.neutral[0]}-${item.displayDamage.neutral[1]} `);
+ }
+ elementList.forEach(elem => {
+ let value = item.displayDamage[elem];
+ if (value[1] !== 0) {
+ itemBox.append(`${elemIcons[elem]} ${capitalize(elem)} Damage: ${value[0]}-${value[1]} `);
+ newLine = true;
+ }
+ });
+ }
+ // 6. powder special
+ if (item.category !== "accessory"){
+ let powders;
+ if (item.category === "weapon") {
+ powders = powderList.weapon;
+ } else {
+ powders = powderList[item.info.type.toLowerCase()];
+ }
+ let count = {E: 0, T: 0, W: 0, F: 0, A: 0};
+ let sum = {E: 0, T: 0, W: 0, F: 0, A: 0};
+ let specialElem;
+ let specialTier = 0;
+ for (const powder of powders) {
+ if (!powder) {
+ continue;
+ }
+ let elem = powder[0];
+ let tier = powder[1];
+ if (tier < 4) {
+ continue;
+ }
+ ++count[elem];
+ sum[elem] += 1*tier;
+ if (count[elem] == 2) {
+ specialElem = elem;
+ specialTier = sum[elem];
+ break;
+ }
+ }
+ if (specialTier) {
+ if (item.category === "weapon") {
+ // weapon specials
+ switch (specialElem) {
+ case 'E':
+ itemBox.append(` Quake `);
+ itemBox.append(` - Radius: ${specialTier / 2 + 1} blocks `);
+ itemBox.append(` - Damage: ${specialTier * 65 - 365}% ${elemIcons.earth} `);
+ break;
+ case 'T':
+ itemBox.append(` Chained Lightning `);
+ itemBox.append(` - Chains: ${specialTier - 3} `);
+ itemBox.append(` - Damage: ${specialTier * 40 - 240}% ${elemIcons.thunder} `);
+ break;
+ case 'W':
+ itemBox.append(` Curse `);
+ itemBox.append(` - Duration: ${specialTier - 3} seconds `);
+ itemBox.append(` - Damage Boost: +${specialTier * 30 - 150}% `);
+ break;
+ case 'F':
+ itemBox.append(` Courage `);
+ itemBox.append(` - Duration: ${specialTier / 2 + 2} seconds `);
+ itemBox.append(` - Damage: ${specialTier * 12.5 - 25}% ${elemIcons.fire} `);
+ itemBox.append(` - Damage Boost: +${specialTier * 20 - 90}% `);
+ break;
+ case 'A':
+ itemBox.append(` Wind Prison `);
+ itemBox.append(` - Duration: ${specialTier / 2 - 1} seconds `);
+ itemBox.append(` - Damage Boost: +${specialTier * 100 - 600}% `);
+ itemBox.append(` - Knockback: ${specialTier * 4 - 24} blocks `);
+ break;
+ }
+ } else {
+ // armour specials
+ switch (specialElem) {
+ case 'E':
+ itemBox.append(` Rage [% ❤ Missing] `);
+ itemBox.append(` - Damage: +${(specialTier - 4 + (specialTier == 12 ? 2 : 0)) / 10}% ${elemIcons.earth} `);
+ break;
+ case 'T':
+ itemBox.append(` Kill Streak [Mob Killed] `);
+ itemBox.append(` - Damage: +${specialTier * 1.5 - 9}% ${elemIcons.thunder} `);
+ itemBox.append(` - Duration: 5 seconds `);
+ break;
+ case 'W':
+ itemBox.append(` Concentration [Mana Used] `);
+ itemBox.append(` - Damage: ${specialTier - 7}% ${elemIcons.water} / Mana `);
+ itemBox.append(` - Duration: 1 Sec. / Mana `);
+ break;
+ case 'F':
+ itemBox.append(` Endurance [Hit Taken] `);
+ itemBox.append(` - Damage: ${specialTier - 6}% ${elemIcons.fire} `);
+ itemBox.append(` - Duration: 8 seconds `);
+ break;
+ case 'A':
+ itemBox.append(` Dodge [Near Mobs] `);
+ itemBox.append(` - Damage: ${specialTier - 6}% ${elemIcons.air} `);
+ itemBox.append(` - Duration: 6 seconds `);
+ break;
+ }
+ }
+ }
+ }
+ if (newLine) {
+ itemBox.append(" ");
+ newLine = false;
+ }
+ // 7. requirements
+ // 7.1 quest req
+ if (item.req.quest) {
+ itemBox.append(`✓ Quest Req: ${item.req.quest} `);
+ newLine = true;
+ }
+ // 7.2 class req
+ if (item.req.class) {
+ itemBox.append(`✓ Class Req: ${item.req.class} `);
+ newLine = true;
+ }
+ // 7.3 combat lvl req
+ if (item.req.level) {
+ itemBox.append(`✓ Combat Lv. Min: ${item.req.level} `);
+ newLine = true;
+ }
+ // 7.4 skill req
+ skillList.forEach(skill => {
+ let value = item.req[skill];
+ if (value) {
+ itemBox.append(`✓ ${capitalize(skill)} Min: ${value} `);
+ newLine = true;
+ }
+ });
+ if (newLine) {
+ itemBox.append(" ");
+ newLine = false;
+ }
+ // 8. bonus skills
+ skillList.forEach(skill => {
+ let value = item.base.skill[skill];
+ if (value) {
+ itemBox.append(`${nToString(value)} ${capitalize(skill)} `);
+ newLine = true;
+ }
+ });
+ if (newLine) {
+ itemBox.append(" ");
+ newLine = false;
+ }
+ // 9. ids
+ {
+ // damage
+ {
+ elementList.forEach(elem => {
+ let value = item.identification.damage[elem];
+ if (value) {
+ itemBox.append(`${nToString(value)}% ${capitalize(elem)} Damage `);
+ newLine = true;
+ }
+ });
+ for (let i in idMap.damage) {
+ if (idMap.damage.hasOwnProperty(i)) {
+ let value = item.identification.damage[i];
+ let line = idMap.damage[i];
+ if (value) {
+ itemBox.append(`${nToString(value)}${line[0]} ${line[1]} `);
+ }
+ }
+ }
+ }
+ // defenses
+ {
+ elementList.forEach(elem => {
+ let value = item.identification.defense[elem];
+ if (value) {
+ itemBox.append(`${nToString(value)}% ${capitalize(elem)} Defense `);
+ newLine = true;
+ }
+ });
+ }
+ // regen
+ {
+ for (let i in idMap.regen) {
+ if (idMap.regen.hasOwnProperty(i)) {
+ let value = item.identification.regen[i];
+ let line = idMap.regen[i];
+ if (value) {
+ itemBox.append(`${nToString(value)}${line[0]} ${line[1]} `);
+ }
+ }
+ }
+ }
+ // spell cost
+ {
+ let ordinals = ["1st", "2nd", "3rd", "4th"];
+ let spellCostPct = item.identification.spellCost.percent;
+ let spellCostRaw = item.identification.spellCost.raw;
+ for (let i = 0; i < 4; i++) {
+ if (spellCostRaw[i]) {
+ itemBox.append(`${nToString(spellCostRaw[i])} ${ordinals[i]} Spell Cost `);
+ newLine = true;
+ }
+ if (spellCostPct[i]) {
+ itemBox.append(`${nToString(spellCostPct[i])}% ${ordinals[i]} Spell Cost `);
+ newLine = true;
+ }
+ }
+ }
+ // steal
+ {
+ for (let i in idMap.steal) {
+ if (idMap.steal.hasOwnProperty(i)) {
+ let value = item.identification.steal[i];
+ let line = idMap.steal[i];
+ if (value) {
+ itemBox.append(`${nToString(value)}${line[0]} ${line[1]} `);
+ newLine = true;
+ }
+ }
+ }
+ }
+ // others
+ {
+ for (let i in idMap.others) {
+ if (idMap.others.hasOwnProperty(i)) {
+ let value = item.identification.others[i];
+ let line = idMap.others[i];
+ if (value) {
+ itemBox.append(`${nToString(value)}${line[0]} ${line[1]} `);
+ newLine = true;
+ }
+ }
+ }
+ }
+ }
+ if (newLine) {
+ itemBox.append(" ");
+ newLine = false;
+ }
+ // 10. powder slots
+ {
+ let type = (item.info.type || item.accessoryType).toLowerCase();
+ let {sockets} = item.info;
+ if (type == "relik" || type == "wand" || type == "bow" || type == "spear" || type == "dagger") {
+ type = "weapon";
+ }
+ let powderArray = (powderList[type] || []).filter(x => !!x);
+ let powderCount = Math.min(powderArray.length, sockets || 0);
+ if (sockets) {
+ if (!powderCount) {
+ itemBox.append(`[0/${sockets}] Powder Slots `);
+ } else {
+ let powderString = "";
+ let count = Math.min(powderArray.length, item.info.sockets);
+ for (let i = 0; i < count; i++) {
+ let elem = powderStats[powderArray[i][0].toUpperCase()][0][0];
+ powderString += `${elemIcons[elem]} `;
+ }
+ itemBox.append(`[${powderCount}/${sockets}] Powder Slots [${powderString}] `);
+ }
+ }
+ }
+ // 11. tier
+ itemBox.append(`${item.info.tier} Item `);
+ // 12. restrictions
+ if (item.restrictions) {
+ itemBox.append(`${item.restrictions} Item `);
+ }
+ // 13. lore
+ if (item.info.lore) {
+ itemBox.append(`${item.info.lore} `);
+ }
+ return itemBox;
+ }
+
+ function capitalize(s) {
+ return s.split("_").map(x => x.substr(0, 1).toUpperCase() + x.substr(1).toLowerCase()).join(" ");
+ }
+
+ function resetPos() {
+ const CONTAINER = "#item_list_box";
+ const SELECTOR = ".item.float_left";
+ const COLUMN_WIDTH = 250;
+ const VERTICAL_MARGIN = 40;
+ const HORIZONTAL_MARGIN = 24;
+
+ let container = $(CONTAINER);
+ let columns = Math.floor(container.width() / (HORIZONTAL_MARGIN + COLUMN_WIDTH));
+ let height_occupied = [];
+ let elems = container.children(SELECTOR);
+ container.css("position", "relative");
+ for (let i = 0; i < columns; i++) {
+ height_occupied.push(0);
+ }
+ for (let i = 0; i < elems.length; i++) {
+ // find the col with least occupied height
+ let idx = 0;
+ let min = height_occupied[0];
+ for (let j = 1; j < columns; j++) {
+ if (height_occupied[j] < min) {
+ min = height_occupied[j];
+ idx = j;
+ }
+ }
+ let $elem = $(elems[i]);
+ $elem.css("position", "absolute").css("top", min).css("left", COLUMN_WIDTH * idx + HORIZONTAL_MARGIN * idx);
+ height_occupied[idx] += $elem.height() + VERTICAL_MARGIN;
+ }
+ let h = Math.max.apply(this, height_occupied);
+ container.height(h);
+ }
+
+ function nToString(n) {
+ return (n >= 0 ? "+" : "") + n;
+ }
+
+ function findStatReq(items) {
+ items = items.slice(0, 8); // remove weapon since it always come last
+ let combinations = [];
+ for (let i = 0; i < 256; i++) {
+ let buildItems = [null, null, null, null, null, null, null, null, null];
+ for (let j = 0; j < 8; j++) {
+ if (items[j] && (i & (1 << j))) {
+ buildItems[j] = {name: items[j].displayName || items[j].info.name};
+ }
+ }
+ combinations.push(calculateBuild(buildItems));
+ }
+ let currentOrder = [0, 1, 2, 3, 4, 5, 6, 7];
+ currentOrder = currentOrder.filter(i => items[i]); // so that we don"t consider empty slots
+ // Absolute minimum, even if it means that you need to allocate 200 points in one skill
+ let currentMin;
+ let currentMinSum = 694201337;
+ // The minimum that is actually valid (<=200 in total, <=100 in any skill)
+ let currentValidMin;
+ let currentValidMinSum = 694201337;
+ do {
+ let bitSet = 0;
+ let currentReq = {
+ req: {
+ strength: 0,
+ dexterity: 0,
+ intelligence: 0,
+ defense: 0,
+ agility: 0
+ },
+ bonus: {
+ strength: 0,
+ dexterity: 0,
+ intelligence: 0,
+ defense: 0,
+ agility: 0
+ }
+ };
+ let sum = 0;
+ let valid = true;
+ for (let i = 0; i < 8; i++) {
+ bitSet |= 1 << currentOrder[i];
+ let build = combinations[bitSet];
+ let stageReq = build.req;
+ skillList.forEach(skill => {
+ let ownedPoints = currentReq.req[skill] + currentReq.bonus[skill];
+ let diff;
+ if (!stageReq[skill]) {
+ diff = 0;
+ } else if (stageReq[skill] > ownedPoints) {
+ diff = stageReq[skill] - ownedPoints;
+ } else {
+ diff = 0;
+ }
+ currentReq.req[skill] += diff;
+ currentReq.bonus[skill] = build.base.skill[skill];
+ sum += diff;
+ valid = sum <= 200 && currentReq[skill] <= 100;
+ });
+ }
+ if (sum < currentMinSum) {
+ currentReq.order = currentOrder.slice(0);
+ currentMinSum = sum;
+ currentMin = currentReq;
+ if (valid) {
+ currentValidMinSum = sum;
+ currentValidMin = currentReq;
+ }
+ }
+ } while (nextPermutation(currentOrder));
+ return currentValidMin || currentMin;
+ }
+
+ // overrides source with data, overriding an object with a primitive won"t work
+ function override(source, data) {
+ if (data === undefined) {
+ return source;
+ }
+ for (let i in data) {
+ if (data.hasOwnProperty(i)) {
+ let obj = source[i];
+ if (typeof obj === "object") {
+ override(obj, data[i]);
+ } else {
+ if (data[i] !== undefined) {
+ source[i] = data[i];
+ }
+ }
+ }
+ }
+ }
+
+ // similar to override(), but adds instead of assigns, and clones the source
+ function sum(left, right) {
+ return combine(left, right, (x, y) => {
+ if (Array.isArray(x)) {
+ return x.concat(y);
+ }
+ if (x !== undefined) {
+ return x + y;
+ }
+ return y;
+
+ });
+ }
+
+ function multiply(obj, val) {
+ let result = {};
+ for (let i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ if ("object" !== typeof obj[i]) {
+ result[i] = val * obj[i];
+ } else {
+ result[i] = multiply(obj[i], val);
+ }
+ }
+ }
+ return result;
+ }
+
+ function max(left, right) {
+ return combine(left, right, (x, y) => {
+ if (Array.isArray(x)) {
+ return x.concat(y);
+ }
+ if (x !== undefined) {
+ return Math.max(x, y);
+ } else {
+ return y;
+ }
+ });
+ }
+
+ // similar to override(), but adds instead of assigns, and clones the source
+ function combine(left, right, combiner) {
+ if (right === undefined || right === null) {
+ return left;
+ }
+ if (left === undefined || left === null) {
+ return right;
+ }
+ let result = JSON.parse(JSON.stringify(left));
+ if (typeof right === "string") {
+ return combiner(left, right);
+ }
+ for (let i in right) {
+ if (right.hasOwnProperty(i)) {
+ if (typeof left[i] === "object") {
+ result[i] = combine(left[i], right[i], combiner);
+ } else {
+ result[i] = combiner(left[i], right[i]);
+ }
+ }
+ }
+ return result;
+ }
+
+ function nextPermutation(array) {
+ /* The algorithm: find the longest decreasing subsequence from the end, since we don"t have a larger permutation
+ for such a sequence, then find the smallest number that is larger than the one before the decreasing
+ subsequence, and swap the smallest with the one before. That way we increment the whole sequence the least.
+ However this way the decreasing subsequence that we"ve found is still decreasing, so we reverse it to become an
+ increasing one, just like 39+1=40, the last digit is reset from the highest possible value to the lowest.
+ 6 8 7 4 3 [(5) 2 1] -> 6 8 7 4 (5) [3 2 1] -> 6 8 7 4 5 1 2 3
+ */
+ // The last index of the array, our starting point.
+ let i = array.length - 1;
+ let last = i;
+ // `i--` returns the value before the decrement, so it is one larger than the decremented `i`.
+ // So this expands to `while (i--, array[i + 1] < array[i]);`, which continues iff the next item is is smaller
+ // than the last item i.e. true if the pair is decreasing.
+ // When `i` reaches -1, it"s comparing undefined and a number, which gives false.
+ // When this completes, `i` is pointing at the index right before the longest decreasing subsequence from the end.
+ while (array[i--] < array[i]);
+ // if `i` is -1, the whole sequence is decreasing. There"s no next permutation.
+ if (!(i+1)) return false;
+ // This is the number to be swapped out, 3 in the example. We are going to find a number in the subsequence that
+ // is slightly larger than this.
+ let n = array[i];
+ // This is the starting point of our search for the slightly larger number.
+ let m = array[i+1];
+ let mi = i+1;
+ for (let j = last; j > i; j--) {
+ let k = array[j];
+ // This statements checks if the current item is larger than the number to be swapped out and smaller than
+ // the existing candidate because we want it to be as small as possible for minimum increment.
+ if (k > n && k < m) {
+ m = k;
+ mi = j;
+ }
+ }
+ // Usual swapping.
+ array[mi] = array[i];
+ array[i] = m;
+ // Recall that `i` is pointing at the index right before the longest decreasing subsequence from the end. Plus
+ // one and it's the start of the decreasing sequence. Remove the subsequence from the array by using .splice(),
+ // reverse it and push it back to the array.
+ // [6 8 7 4 5 3 2 1] -> [6 8 7 4 5] [3 2 1] -> [6 8 7 4 5] [1 2 3] -> [6 8 7 4 5 1 2 3]
+ array.push(...array.splice(i+1).reverse());
+ return true;
+ }
+
+ window.calculateBuild = calculateBuild;
+ window.findStatReq = findStatReq;
+});
\ No newline at end of file
diff --git a/build.js b/build.js
index 4961b26..e8f7d50 100644
--- a/build.js
+++ b/build.js
@@ -101,7 +101,7 @@ class Build{
}
this.availableSkillpoints = levelToSkillPoints(this.level);
this.equipment = [ helmet, chestplate, leggings, boots, ring1, ring2, bracelet, necklace ];
-
+ this.items = [helmet, chestplate, leggings, boots, ring1, ring2, bracelet, necklace, weapon];
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
let result = calculate_skillpoints(this.equipment, weapon);
console.log(result);
@@ -118,6 +118,9 @@ class Build{
}
/* Getters */
+
+ /* Get total health for build.
+ */
getHealth(){
health = parseInt(this.helmet.hp,10) + parseInt(this.helmet.hpBonus,10) + parseInt(this.chestplate.hp,10) + parseInt(this.chestplate.hpBonus,10) + parseInt(this.leggings.hp,10) + parseInt(this.leggings.hpBonus,10) + parseInt(this.boots.hp,10) + parseInt(this.boots.hpBonus,10) + parseInt(this.ring1.hp,10) + parseInt(this.ring1.hpBonus,10) + parseInt(this.ring2.hp,10) + parseInt(this.ring2.hpBonus,10) + parseInt(this.bracelet.hp,10) + parseInt(this.bracelet.hpBonus,10) + parseInt(this.necklace.hp,10) + parseInt(this.necklace.hpBonus,10) + parseInt(this.weapon.hp,10) + parseInt(this.weapon.hpBonus,10) + levelToHPBase(this.level);
if(health<5){
@@ -126,6 +129,60 @@ class Build{
return health;
}
}
+ /* Get total melee dps for build.
+ */
+ getMeleeDPS(){
+ let meleeMult = {
+ "SUPER_SLOW":"0.51",
+ "VERY_SLOW":"0.83",
+ "SLOW":"1.5",
+ "NORMAL":"2.05",
+ "FAST":"2.5",
+ "VERY_FAST":"3.1",
+ "SUPER_FAST":"4.3",
+ }
+ let stats = this.getBuildStats();
+ let nDam = stats.get("nDam");
+
+ return [];
+ }
+
+ /* Get all stats for this build. Returns a map w/ sums of all IDs.
+ @dep test.js.item_fields
+ @dep test.js.rolledIDs
+ @dep test.js.nonRolledIDs
+ @dep test.js.expandItem()
+ @pre The build itself should be valid. No checking of validity of pieces is done here.
+ @post The map returned will contain non-stacking IDs w/ a value null.
+ */
+ getBuildStats(){
+ //Create a map of this build's stats
+ //This is universal for every possible build, so it's possible to move this elsewhere.
+ let statMap = new Map();
+ for (const i in item_fields){
+ let id = item_fields[i];
+ if(stackingIDs.includes(id)){ //IDs stack - make it number
+ statMap.set(id,0);
+ }else if(standaloneIDs.includes(id)){ //IDs do not stack - string
+ statMap.set(id,"");
+ }
+ }
+ for (const i in this.items){
+ let item = expandItem(this.items[i]);
+ console.log(item,type(item));
+ if(item.has("fixID") && item.get("fixID")){//item has fixed IDs
+ for(const [key,value] in item.entries()){
+ console.log(key,value);
+ }
+ }else{//item does not have fixed IDs
+ for (const i in item) {
+ console.log(entry,": ",item.get(entry));
+ }
+ }
+ }
+
+ return statMap;
+ }
/* Setters */
diff --git a/test.js b/test.js
index 7012970..8ae78bf 100644
--- a/test.js
+++ b/test.js
@@ -23,6 +23,8 @@ let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
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", "agiReq", "defReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "exploding", "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 nonRolledIDs = ["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", "agiReq", "defReq","str", "dex", "int", "agi", "def", "fixID", "category", "id"];
let rolledIDs = ["hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "exploding", "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 stackingIDs = ["hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "exploding", "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", "fDef", "wDef", "aDef", "tDef", "eDef", "str", "dex", "int", "agi", "def"];
+let standaloneIDs = ["name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "lvl", "classReq", "strReq", "dexReq", "intReq", "agiReq", "defReq", "fixID", "category", "id"];
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes);
let itemLists = new Map();
for (const it of itemTypes) {
@@ -72,6 +74,8 @@ function init() {
item.skillpoints = [0, 0, 0, 0, 0];
item.has_negstat = false;
item.reqs = [0, 0, 0, 0, 0];
+ item.fixID = true;
+ item.tier = " ";//do not get rid of this @hpp
item.id = 10000 + i;
noneItems[i] = item;
@@ -289,7 +293,7 @@ function calculateBuild(){
setHTML("build-bracelet", expandedItemToString(expandItem(player_build.bracelet)));
setHTML("build-necklace", expandedItemToString(expandItem(player_build.necklace)));
setHTML("build-weapon", expandedItemToString(expandItem(player_build.weapon)));
-
+ setHTML("build-cumulative-stats", player_build.getMeleeDPS()); //Incomplete function
location.hash = encodeBuild();
}
/* Helper function that gets stats ranges for wearable items.
@@ -314,7 +318,6 @@ function expandItem(item){
}
}else{ //The item does not have fixed IDs.
for (const id in rolledIDs){
- console.log(id);
if(item[rolledIDs[id]]){
if(item[rolledIDs[id]] > 0){ // positive rolled IDs
minRolls.set(rolledIDs[id],idRound(item[rolledIDs[id]]*0.3));
@@ -336,7 +339,6 @@ function expandItem(item){
}
expandedItem.set("minRolls",minRolls);
expandedItem.set("maxRolls",maxRolls);
- console.log(expandedItem)
return expandedItem;
}
/* A second helper function that takes items from expandItem() and stringifies them.
@@ -344,7 +346,6 @@ function expandItem(item){
TODO: write the function
*/
function expandedItemToString(item){
- console.log(item);
let ids = ["lvl", "classReq","strReq", "dexReq", "intReq", "defReq","agiReq", "nDam", "eDam", "tDam", "wDam", "tDam", "aDam", "atkSpd", "hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "agi", "def", "hpBonus", "hprRaw", "hprPct", "sdRaw", "sdPct", "mdRaw", "mdPct", "mr", "ms", "ref", "ls", "poison", "thorns", "exploding", "spd", "atkTier", "eDamPct", "tDamPct", "wDamPct", "fDamPct", "aDamPct", "eDefPct", "tDefPct", "wDefPct", "fDefPct", "aDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "xpb", "lb", "lq", "spRegen", "eSteal", "gXp", "gSpd", "slots", "set", "quest", "restrict"];
let idPrefixes = {"lvl":"Combat Level Min: ", "classReq":"Class Req: ","strReq":"Strength Min: ","dexReq":"Dexterity Min: ","intReq":"Intelligence Min: ","defReq":"Defense Min: ","agiReq":"Agility Min: ", "nDam":"Neutral Damage: ", "eDam":"Earth Damage: ", "tDam":"Thunder Damage: ", "wDam":"Water Damage: ", "fDam":"Fire Damage: ", "aDam":"Air Damage: ", "atkSpd":"Attack Speed: ", "hp":"Health: ", "eDef":"Earth Defense: ", "tDef":"Thunder Defense: ", "wDef":"Water Defense: ", "fDef":"Fire Defense: ", "aDef":"Air Defense: ", "str":"Strength: ", "dex":"Dexterity: ", "int":"Intelligence: ", "def":"Defense: ","agi":"Agility: ", "hpBonus":"Health Bonus: ", "hprRaw":"Health Regen Raw: ", "hprPct":"Health Regen %: ", "sdRaw":"Raw Spell Damage: ", "sdPct":"Spell Damage %: ", "mdRaw":"Main Attack Neutral Damage: ", "mdPct":"Main Attack Damage %: ", "mr":"Mana Regen: ", "ms":"Mana Steal: ", "ref":"Reflection: ", "ls":"Life Steal: ", "poison":"Poison: ", "thorns":"Thorns: ", "exploding":"Expoding: ", "spd":"Walk Speed Bonus: ", "atkTier":"Attack Speed Bonus: ", "eDamPct":"Earth Damage %: ", "tDamPct":"Thunder Damage %: ", "wDamPct":"Water Damage %: ", "fDamPct":"Fire Damage %: ", "aDamPct":"Air Damage %: ", "eDefPct":"Earth Defense %: ", "tDefPct":"Thunder Defense %: ", "wDefPct":"Water Defense %: ", "fDefPct":"Fire Defense %: ", "aDefPct":"Air Defense %: ", "spPct1":"1st Spell Cost %: ", "spRaw1":"1st Spell Cost Raw: ", "spPct2":"2nd Spell Cost %: ", "spRaw2":"2nd Spell Cost Raw: ", "spPct3":"3rd Spell Cost %: ", "spRaw3":"3rd Spell Cost Raw: ", "spPct4":"4th Spell Cost %: ", "spRaw4":"4th Spell Cost Raw: ", "rainbowRaw":"Rainbow Spell Damage Raw: ", "sprint":"Sprint Bonus: ", "sprintReg":"Sprint Regen Bonus: ", "jh":"Jump Height: ", "xpb":"Combat XP Bonus: ", "lb":"Loot Bonus: ", "lq":"Loot Quality: ", "spRegen":"Soul Point Regen: ", "eSteal":"Stealing: ", "gXp":"Gathering XP Bonus: ", "gSpd":"Gathering Speed Bonus: ", "slots":"Powder Slots: ", "set":"This item belongs to the ", "quest":"This item is from the quest ", "restrict":""};
let idSuffixes = {"lvl":"", "classReq":"","strReq":"","dexReq":"","intReq":"","defReq":"","agiReq":"", "nDam":"", "eDam":"", "tDam":"", "wDam":"", "fDam":"", "aDam":"", "atkSpd":"", "hp":"", "eDef":"", "tDef":"", "wDef":"", "fDef":"", "aDef":"", "str":"", "dex":"", "int":"", "def":"","agi":"", "hpBonus":"", "hprRaw":"", "hprPct":"%", "sdRaw":"", "sdPct":"%", "mdRaw":"", "mdPct":"%", "mr":"/4s", "ms":"/4s", "ref":"%", "ls":"/4s", "poison":"/3s", "thorns":"%", "exploding":"%", "spd":"%", "atkTier":" tier", "eDamPct":"%", "tDamPct":"%", "wDamPct":"%", "fDamPct":"%", "aDamPct":"%", "eDefPct":"%", "tDefPct":"%", "wDefPct":"%", "fDefPct":"%", "aDefPct":"%", "spPct1":"%", "spRaw1":"", "spPct2":"%", "spRaw2":"", "spPct3":"%", "spRaw3":"", "spPct4":"%", "spRaw4":"", "rainbowRaw":"", "sprint":"%", "sprintReg":"%", "jh":"", "xpb":"%", "lb":"%", "lq":"%", "spRegen":"%", "eSteal":"%", "gXp":"%", "gSpd":"%", "slots":"", "set":" set.", "quest":".", "restrict":""};
@@ -357,7 +358,6 @@ function expandedItemToString(item){
itemString = itemString.concat(item.get(ids[i]), idSuffixes[ids[i]]," ");
}
if(rolledIDs.includes(ids[i])&& item.get("minRolls").get(ids[i]) && item.get("maxRolls").get(ids[i]) ){//rolled ID & non-0/non-null/non-und ID
- console.log("hi");
itemString = itemString.concat(idPrefixes[ids[i]]);
itemString = itemString.concat(item.get("minRolls").get(ids[i]), idSuffixes[ids[i]]," ");
}//Just don't do anything if else
@@ -369,7 +369,6 @@ function expandedItemToString(item){
itemString = itemString.concat(item.get(ids[i]), idSuffixes[ids[i]]," ");
}
if(rolledIDs.includes(ids[i])&& item.get("minRolls").get(ids[i]) && item.get("maxRolls").get(ids[i]) ){//rolled ID & non-0/non-null/non-und ID
- console.log("hi");
itemString = itemString.concat(idPrefixes[ids[i]]);
itemString = itemString.concat(item.get("minRolls").get(ids[i]), idSuffixes[ids[i]], " -> ", idRound(item.get("maxRolls").get(ids[i])),idSuffixes[ids[i]]," ");
}//Just don't do anything if else
@@ -390,6 +389,8 @@ function idRound(id){
}
}
+
+
function resetFields(){
setValue("helmet-choice", "");
setValue("helmet-powder", "");
@@ -413,3 +414,4 @@ function resetFields(){
}
load_init(init);
+