WynnBuilder version 6.9.42.0: Full custom sharing, custom integration into builder, new build sharing link alg, fully-functional crafter (added correct powder alg on crafted weapons), new clean icons for everything (Kiocifer), moved item and ing map definitions into load and load_ing, added item page for viewing items, and added map page for world map.

This commit is contained in:
ferricles 2021-03-13 23:55:08 -08:00
parent 7504a9b3f4
commit f8447a0f0b
70 changed files with 1602 additions and 604 deletions

View file

@ -36,6 +36,11 @@
</img>
<div class = "tooltiptext center">WynnCustom</div>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
<div >

View file

@ -96,16 +96,17 @@ class Build{
* @description Construct a build.
* @param {Number} level : Level of the player.
* @param {String[]} equipment : List of equipment names that make up the build.
* In order: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon.
* In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon.
* @param {Number[]} powders : Powder application. List of lists of integers (powder IDs).
* In order: Helmet, Chestplate, Leggings, Boots, Weapon.
* In order: boots, Chestplate, Leggings, Boots, Weapon.
* @param {Object[]} inputerrors : List of instances of error-like classes.
*/
constructor(level,equipment, powders, externalStats, inputerrors=[]){
let errors = inputerrors;
//this contains the Craft objects, if there are any crafted items. this.helmet, etc. will contain the statMap of the Craft (which is built to be an expandedItem).
//this contains the Craft objects, if there are any crafted items. this.boots, etc. will contain the statMap of the Craft (which is built to be an expandedItem).
this.craftedItems = [];
this.customItems = [];
// NOTE: powders is just an array of arrays of powder IDs. Not powder objects.
this.powders = powders;
if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") {
@ -114,7 +115,8 @@ class Build{
this.helmet = expandItem(helmet, this.powders[0]);
} else {
try {
let helmet = getCraftFromHash(equipment[0]);
//let boots = getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : (getCustomFromHash(equipment[0])? getCustomFromHash(equipment[0]) : undefined);
let helmet = getCustomFromHash(equipment[0]) ? getCustomFromHash(equipment[0]) : (getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : undefined);
if (helmet.statMap.get("type") !== "helmet") {
throw new Error("Not a helmet");
}
@ -122,7 +124,12 @@ class Build{
helmet.statMap.set("powders",this.powders[0].slice());
helmet.applyPowders();
this.helmet = helmet.statMap;
if (this.helmet.get("custom")) {
this.customItems.push(helmet);
} else if (this.helmet.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(helmet);
}
} catch (Error) {
//console.log(Error); //fix
const helmet = itemMap.get("No Helmet");
@ -137,7 +144,7 @@ class Build{
this.chestplate = expandItem(chestplate, this.powders[1]);
} else {
try {
let chestplate = getCraftFromHash(equipment[1]);
let chestplate = getCustomFromHash(equipment[1]) ? getCustomFromHash(equipment[1]) : (getCraftFromHash(equipment[1]) ? getCraftFromHash(equipment[1]) : undefined);
if (chestplate.statMap.get("type") !== "chestplate") {
throw new Error("Not a chestplate");
}
@ -145,7 +152,11 @@ class Build{
chestplate.statMap.set("powders",this.powders[1].slice());
chestplate.applyPowders();
this.chestplate = chestplate.statMap;
if (this.chestplate.get("custom")) {
this.customItems.push(chestplate);
} else if (this.chestplate.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(chestplate);
}
} catch (Error) {
const chestplate = itemMap.get("No Chestplate");
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
@ -159,7 +170,7 @@ class Build{
this.leggings = expandItem(leggings, this.powders[2]);
} else {
try {
let leggings = getCraftFromHash(equipment[2]);
let leggings = getCustomFromHash(equipment[2]) ? getCustomFromHash(equipment[2]) : (getCraftFromHash(equipment[2]) ? getCraftFromHash(equipment[2]) : undefined);
if (leggings.statMap.get("type") !== "leggings") {
throw new Error("Not a leggings");
}
@ -167,7 +178,11 @@ class Build{
leggings.statMap.set("powders",this.powders[2].slice());
leggings.applyPowders();
this.leggings = leggings.statMap;
if (this.leggings.get("custom")) {
this.customItems.push(leggings);
} else if (this.leggings.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(leggings);
}
} catch (Error) {
const leggings = itemMap.get("No Leggings");
this.powders[2] = this.powders[2].slice(0,leggings.slots);
@ -181,7 +196,7 @@ class Build{
this.boots = expandItem(boots, this.powders[3]);
} else {
try {
let boots = getCraftFromHash(equipment[3]);
let boots = getCustomFromHash(equipment[3]) ? getCustomFromHash(equipment[3]) : (getCraftFromHash(equipment[3]) ? getCraftFromHash(equipment[3]) : undefined);
if (boots.statMap.get("type") !== "boots") {
throw new Error("Not a boots");
}
@ -190,7 +205,11 @@ class Build{
boots.applyPowders();
this.boots = boots.statMap;
console.log(boots);
if (this.boots.get("custom")) {
this.customItems.push(boots);
} else if (this.boots.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(boots);
}
} catch (Error) {
const boots = itemMap.get("No Boots");
this.powders[3] = this.powders[3].slice(0,boots.slots);
@ -203,12 +222,16 @@ class Build{
this.ring1 = expandItem(ring, []);
}else{
try {
let ring = getCraftFromHash(equipment[4]);
let ring = getCustomFromHash(equipment[4]) ? getCustomFromHash(equipment[4]) : (getCraftFromHash(equipment[4]) ? getCraftFromHash(equipment[4]) : undefined);
if (ring.statMap.get("type") !== "ring") {
throw new Error("Not a ring");
}
this.ring1 = ring.statMap;
if (this.ring1.get("custom")) {
this.customItems.push(ring);
} else if (this.ring1.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(ring);
}
} catch (Error) {
const ring = itemMap.get("No Ring 1");
this.ring1 = expandItem(ring, []);
@ -220,12 +243,16 @@ class Build{
this.ring2 = expandItem(ring, []);
}else{
try {
let ring = getCraftFromHash(equipment[5]);
let ring = getCustomFromHash(equipment[5]) ? getCustomFromHash(equipment[5]) : (getCraftFromHash(equipment[5]) ? getCraftFromHash(equipment[5]) : undefined);
if (ring.statMap.get("type") !== "ring") {
throw new Error("Not a ring");
}
this.ring2 = ring.statMap;
if (this.ring2.get("custom")) {
this.customItems.push(ring);
} else if (this.ring2.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(ring);
}
} catch (Error) {
const ring = itemMap.get("No Ring 2");
this.ring2 = expandItem(ring, []);
@ -237,12 +264,16 @@ class Build{
this.bracelet = expandItem(bracelet, []);
}else{
try {
let bracelet = getCraftFromHash(equipment[6]);
let bracelet = getCustomFromHash(equipment[6]) ? getCustomFromHash(equipment[6]) : (getCraftFromHash(equipment[6]) ? getCraftFromHash(equipment[6]) : undefined);
if (bracelet.statMap.get("type") !== "bracelet") {
throw new Error("Not a bracelet");
}
this.bracelet = bracelet.statMap;
if (this.bracelet.get("custom")) {
this.customItems.push(bracelet);
} else if (this.bracelet.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(bracelet);
}
} catch (Error) {
const bracelet = itemMap.get("No Bracelet");
this.bracelet = expandItem(bracelet, []);
@ -254,12 +285,16 @@ class Build{
this.necklace = expandItem(necklace, []);
}else{
try {
let necklace = getCraftFromHash(equipment[7]);
let necklace = getCustomFromHash(equipment[7]) ? getCustomFromHash(equipment[7]) : (getCraftFromHash(equipment[7]) ? getCraftFromHash(equipment[7]) : undefined);
if (necklace.statMap.get("type") !== "necklace") {
throw new Error("Not a necklace");
}
this.necklace = necklace.statMap;
if (this.necklace.get("custom")) {
this.customItems.push(necklace);
} else if (this.necklace.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(necklace);
}
} catch (Error) {
const necklace = itemMap.get("No Necklace");
this.necklace = expandItem(necklace, []);
@ -277,12 +312,16 @@ class Build{
}
}else{
try {
let weapon = getCraftFromHash(equipment[8]);
let weapon = getCustomFromHash(equipment[8]) ? getCustomFromHash(equipment[8]) : (getCraftFromHash(equipment[8]) ? getCraftFromHash(equipment[8]) : undefined);
if (weapon.statMap.get("category") !== "weapon") {
throw new Error("Not a weapon");
}
this.weapon = weapon.statMap;
if (this.weapon.get("custom")) {
this.customItems.push(weapon);
} else if (this.weapon.get("crafted")) { //customs can also be crafted, but custom takes priority.
this.craftedItems.push(weapon);
}
this.powders[4] = this.powders[4].slice(0,this.weapon.slots);
this.weapon.set("powders",this.powders[4].slice());
document.getElementsByClassName("powder-specials")[0].style.display = "grid";
@ -294,7 +333,7 @@ class Build{
errors.push(new ItemNotFound(equipment[8], "weapon", true));
}
}
console.log(this.craftedItems)
//console.log(this.craftedItems)
if (level < 1) { //Should these be constants?
this.level = 1;
@ -362,6 +401,9 @@ class Build{
*/
getMeleeStats(){
const stats = this.statMap;
if (this.weapon.get("tier") === "Crafted") {
stats.set("damageBases", [this.weapon.get("nDamBaseHigh"),this.weapon.get("eDamBaseHigh"),this.weapon.get("tDamBaseHigh"),this.weapon.get("wDamBaseHigh"),this.weapon.get("fDamBaseHigh"),this.weapon.get("aDamBaseHigh")]);
}
let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier");
if(adjAtkSpd > 6){
adjAtkSpd = 6;

View file

@ -2,7 +2,7 @@ const url_tag = location.hash.slice(1);
console.log(url_base);
console.log(url_tag);
const BUILD_VERSION = "6.9.42";
const BUILD_VERSION = "6.9.42.0";
function setTitle() {
let text;
@ -82,15 +82,7 @@ let powderInputs = [
"weapon-powder",
];
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes);
let itemLists = new Map();
for (const it of itemTypes) {
itemLists.set(it, []);
}
let itemMap = new Map();
/* Mapping from item names to set names. */
let idMap = new Map();
let redirectMap = new Map();
/*
* Function that takes an item list and populates its corresponding dropdown.
@ -109,58 +101,7 @@ function populateItemList(type) {
* Populate dropdowns, add listeners, etc.
*/
function init() {
let noneItems = [
["armor", "helmet", "No Helmet"],
["armor", "chestplate", "No Chestplate"],
["armor", "leggings", "No Leggings"],
["armor", "boots", "No Boots"],
["accessory", "ring", "No Ring 1"],
["accessory", "ring", "No Ring 2"],
["accessory", "bracelet", "No Bracelet"],
["accessory", "necklace", "No Necklace"],
["weapon", "dagger", "No Weapon"],
];
for (let i = 0; i < 9; i++) {
let item = Object();
item.slots = 0;
item.category = noneItems[i][0];
item.type = noneItems[i][1];
item.name = noneItems[i][2];
item.displayName = item.name;
item.set = null;
item.quest = null;
item.skillpoints = [0, 0, 0, 0, 0];
item.has_negstat = false;
item.reqs = [0, 0, 0, 0, 0];
item.fixID = true;
item.tier = " ";//do not get rid of this @hpp
item.id = 10000 + i;
item.nDam = "0-0";
item.eDam = "0-0";
item.tDam = "0-0";
item.wDam = "0-0";
item.fDam = "0-0";
item.aDam = "0-0";
noneItems[i] = item;
}
items = items.concat(noneItems);
console.log(items);
for (const item of items) {
if (item.remapID === undefined) {
itemLists.get(item.type).push(item.displayName);
itemMap.set(item.displayName, item);
if (noneItems.includes(item)) {
idMap.set(item.id, "");
}
else {
idMap.set(item.id, item.displayName);
}
}
else {
redirectMap.set(item.id, item.remapID);
}
}
for (const armorType of armorTypes) {
populateItemList(armorType);
@ -171,12 +112,12 @@ function init() {
if (itemMap.has(item_name)) {
let item = itemMap.get(item_name);
nSlots = item["slots"];
console.log(item);
//console.log(item);
}
else {
let crafted_item = getCraftFromHash(item_name);
if (crafted_item != undefined) {
nSlots = crafted_item.statMap.get("slots");
let crafted_custom_item = getCraftFromHash(item_name) !== undefined ? getCraftFromHash(item_name) : (getCustomFromHash(item_name) !== undefined ? getCustomFromHash(item_name) : undefined);
if (crafted_custom_item !== undefined) {
nSlots = crafted_custom_item.statMap.get("slots");
}
}
if (nSlots !== undefined) {
@ -213,9 +154,10 @@ function init() {
// Add change listener to update weapon slots.
document.getElementById("weapon-choice").addEventListener("change", (event) => {
let item = itemMap.has(event.target.value) ? itemMap.get(event.target.value) : (getCraftFromHash(event.target.value) != undefined ? getCraftFromHash(event.target.value).statMap : undefined);
let item_name = event.target.value;
let item = itemMap.has(item_name) ? itemMap.get(item_name) : (getCraftFromHash(item_name) ? getCraftFromHash(item_name) : (getCustomFromHash(item_name) ? getCustomFromHash(item_name) : undefined));
if (item !== undefined && event.target.value !== "") {
document.getElementById("weapon-slots").textContent = (item["slots"] ? item["slots"] : (item.get ? item.get("slots") : 0))+ " slots";
document.getElementById("weapon-slots").textContent = (item["slots"] ? item["slots"] : (item.statMap !== undefined ? ( item.statMap.has("slots") ? item.statMap.get("slots") : 0): 0) )+ " slots";
} else {
document.getElementById("weapon-slots").textContent = "X slots";
}
@ -290,7 +232,25 @@ function decodeBuild(url_tag) {
}
info[1] = info_str.slice(start_idx);
}
if (version === "5") {
let info_str = info[1];
let start_idx = 0;
for (let i = 0; i < 9; ++i ) {
if (info_str.slice(start_idx,start_idx+3) === "CR-") {
equipment[i] = info_str.slice(start_idx, start_idx+20);
start_idx += 20;
} else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") {
let len = Base64.toInt(info_str.slice(start_idx,start_idx+3));
equipment[i] = info_str.slice(start_idx+3,start_idx+3+len);
start_idx += (3+len);
} else {
let equipment_str = info_str.slice(start_idx, start_idx+3);
equipment[i] = getItemNameFromID(Base64.toInt(equipment_str));
start_idx += 3;
}
}
info[1] = info_str.slice(start_idx);
}
if (version === "1") {
let powder_info = info[1];
powdering = parsePowdering(powder_info);
@ -303,7 +263,7 @@ function decodeBuild(url_tag) {
let powder_info = info[1].slice(10);
powdering = parsePowdering(powder_info);
} else if (version === "3" || version === "4"){
} else if (version === "3" || version === "4" || version === "5"){
level = Base64.toInt(info[1].slice(10,12));
setValue("level-choice",level);
save_skp = true;
@ -331,15 +291,19 @@ function decodeBuild(url_tag) {
*/
function encodeBuild() {
if (player_build) {
//@hpp update for 4_
let build_string = "4_";
let build_string = "5_";
let crafted_idx = 0;
let custom_idx = 0;
for (const item of player_build.items) {
if (item.get("crafted")) {
build_string += "-"+encodeCraft(player_build.craftedItems[crafted_idx])
crafted_idx += 1
}
else {
if (item.get("custom")) {
let custom = "CI-"+encodeCustom(player_build.customItems[custom_idx],true);
build_string += Base64.fromIntN(custom.length, 3) + custom;
custom_idx += 1;
} else if (item.get("crafted")) {
build_string += "CR-"+encodeCraft(player_build.craftedItems[crafted_idx]);
crafted_idx += 1;
} else {
build_string += Base64.fromIntN(item.get("id"), 3);
}
}
@ -439,7 +403,7 @@ function calculateBuild(save_skp, skp){
else
errors.push(new IncorrectInput(errorederrors[0], "t6 or e3", powderInputs[i]));
}
console.log("POWDERING: " + powdering);
//console.log("POWDERING: " + powdering);
powderings.push(powdering);
}
@ -794,7 +758,8 @@ function calculateBuildStats() {
for (const item of player_build.items) {
let item_lvl;
if (item.get("crafted")) {
item_lvl = parseInt(item.get("lvl").split("-")[0]);
//item_lvl = item.get("lvlLow") + "-" + item.get("lvl");
item_lvl = item.get("lvlLow");
}
else {
item_lvl = item.get("lvl");

View file

@ -3,8 +3,11 @@
//constructs a craft from a hash 'CR-qwoefsabaoe' or 'qwoefsaboe'
function getCraftFromHash(hash) {
let name = hash.slice();
try {
if (name.slice(0,3) === "CR-") {
name = name.substring(3);
} else {
throw new Error("Not a crafted item!");
}
version = name.substring(0,1);
name = name.substring(1);
@ -24,6 +27,10 @@ function getCraftFromHash(hash) {
let attackSpeed = atkSpds[atkSpd];
return new Craft(recipe,mat_tiers,ingreds,attackSpeed,"1"+name);
}
} catch (error) {
return undefined;
}
}
@ -42,7 +49,7 @@ class Craft{
this.ingreds = ingreds;
this.statMap = new Map(); //can use the statMap as an expanded Item
this.atkSpd = attackSpeed;
this.hash = hash;
this.hash = "CR-" + hash;
this.initCraftStats();
this.statMap.set("hash", this.hash);
}
@ -64,9 +71,9 @@ class Craft{
}
setHash(hash) {
this.hash = hash;
this.statMap.set("name", "CR-" + this.hash);
this.statMap.set("displayName", "CR-" + this.hash);
this.hash = "CR-" + hash;
this.statMap.set("name", this.hash);
this.statMap.set("displayName", this.hash);
this.statMap.set("hash", this.hash);
}
/* Get all stats for this build. Stores in this.statMap.
@ -77,13 +84,14 @@ class Craft{
let statMap = new Map();
statMap.set("minRolls", new Map());
statMap.set("maxRolls", new Map());
statMap.set("name", "CR-" + this.hash);
statMap.set("displayName", "CR-" + this.hash);
statMap.set("name", this.hash);
statMap.set("displayName", this.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("lvl", this.recipe.get("lvl")[1]);
statMap.set("lvlLow", this.recipe.get("lvl")[0]);
statMap.set("nDam", 0);
statMap.set("hp",0);
statMap.set("hpLow",0);
@ -223,9 +231,13 @@ class Craft{
let low2 = Math.floor(nDamBaseLow * 1.1);
let high1 = Math.floor(nDamBaseHigh * 0.9);
let high2 = Math.floor(nDamBaseHigh * 1.1);
statMap.set("nDamBaseLow", nDamBaseLow);
statMap.set("nDamBaseHigh", nDamBaseHigh);
statMap.set("nDamLow", low1+"-"+low2);
statMap.set("nDam", high1+"-"+high2);
for (const e in skp_elements) {
statMap.set(skp_elements[e]+"DamBaseLow", elemDamBaseLow[e]);
statMap.set(skp_elements[e]+"DamBaseHigh", elemDamBaseHigh[e]);
low1 = Math.floor(elemDamBaseLow[e] * 0.9);
low2 = Math.floor(elemDamBaseLow[e] * 1.1);
high1 = Math.floor(elemDamBaseHigh[e] * 0.9);

View file

@ -36,6 +36,11 @@
</img>
<div class = "tooltiptext center">WynnCustom</div>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
<div >
@ -225,7 +230,7 @@
<div class = "center">
<footer>
<div class="center" id="header2">
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with Atlas Inc (JavaScript required to function, nothing works without js)</p>
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with <a href = "./atlas.html" target = "_blank" class = "link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
</div>
<div class="center" id="credits">

View file

@ -9,7 +9,7 @@ console.log(ing_url_tag);
const ING_BUILD_VERSION = "6.9.41";
const ING_BUILD_VERSION = "6.9.42.0";
/*
* END testing section
*/
@ -28,87 +28,14 @@ let player_craft;
function setTitle() {
document.getElementById("header").textContent = "WynnCrafter version "+ING_BUILD_VERSION+" (ingredient db version "+ING_DB_VERSION+")";
document.getElementById("header").classList.add("funnynumber");
let disclaimer = document.createElement("p");
disclaimer.textContent = "THIS CRAFTER IS NEARLY COMPLETE. The effect of powders on crafted weapons is not accurate. If you know how the math behind it works, please contact ferricles on forums, discord, or ingame.";
document.getElementById("header").append(disclaimer);
}
let ingMap = new Map();
let ingList = [];
let recipeMap = new Map();
let recipeList = [];
let ingIDMap = new Map();
let recipeIDMap = new Map();
function init() {
//no ing
let ing = Object();
ing.name = "No Ingredient";
ing.displayName = "No Ingredient";
ing.tier = 0;
ing.lvl = 0;
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"];
ing.ids= {};
ing.itemIDs = {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
ing.consumableIDs = {"dura": 0, "charges": 0};
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
ing.id = 4000;
ingMap.set(ing["displayName"], ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
for (let i = 0; i < 5; i ++) {
for (const powderIng of powderIngreds) {
let ing = Object();
ing.name = "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1);
ing.displayName = ing.name
ing.tier = 0;
ing.lvl = 0;
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING"];
ing.ids = {};
ing.isPowder = true;
ing.pid = 6*i + powderIngreds.indexOf(powderIng);
ing.id = 4001 + ing.pid;
ing.itemIDs = {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
switch(i) {
case 0:
ing.itemIDs["strReq"] = powderIng["skpReq"];
break;
case 1:
ing.itemIDs["dexReq"] = powderIng["skpReq"];
break;
case 2:
ing.itemIDs["intReq"] = powderIng["skpReq"];
break;
case 3:
ing.itemIDs["defReq"] = powderIng["skpReq"];
break;
case 4:
ing.itemIDs["agiReq"] = powderIng["skpReq"];
break;
}
ing.consumableIDs = {"dura": 0, "charges": 0};
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
ingMap.set(ing["displayName"],ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
}
}
for (const ing of ings) {
ingMap.set(ing["displayName"], ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
}
for (const recipe of recipes) {
recipeMap.set(recipe["name"], recipe);
recipeList.push(recipe["name"]);
recipeIDMap.set(recipe["id"],recipe["name"]);
}
console.log("all ingredients");
console.log(ings);
console.log("all recipes");
@ -265,6 +192,10 @@ function encodeCraft(craft) {
function decodeCraft(ing_url_tag) {
if (ing_url_tag) {
if (ing_url_tag.slice(0,3) === "CR-") {
ing_url_tag = ing_url_tag.substring(3);
location.hash = location.hash.substring(3);
}
console.log(ing_url_tag);
let version = ing_url_tag.charAt(0);
let tag = ing_url_tag.substring(1);

View file

@ -1,12 +1,13 @@
Theme and overall inspiration: Wynndata (Dukio)
Theme, formatting, and overall inspiration: Wynndata (Dukio)
- https://wynndata.tk
The game, of course
- wynncraft.com
Additional Contributors:
- Kiocifer (Icons!)
- Phanta (WynnAtlas custom expression parser / item search)
- QuantumNep (Layout code/layout ideas)
- nbcss (Crafter figuring-out-the-gamifying)
- nbcss (Crafted Item mechanics reverse engineering)
- dr_carlos (Hiding UI elements properly, fade animations, proper error handling)
- Atlas Inc discord (feedback, ideas, etc)

178
custom.js
View file

@ -1,15 +1,98 @@
const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"] //I'm not sure why you would make a custom crafted but if you do you should be able to use it w/ the correct powder formula
const types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
const atkSpds = ["SUPER_SLOW","VERY_SLOW","SLOW","NORMAL","FAST","VERY_FAST","SUPER_FAST"];
const ci_save_order = ["name", "lore", "tier", "set", "slots", "type", "material", "drop", "quest", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq","str", "dex", "int", "agi", "def", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "majorIds", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd"];
const ci_save_order = ["name", "lore", "tier", "set", "slots", "type", "material", "drop", "quest", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq","str", "dex", "int", "agi", "def", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "majorIds", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd","durability","duration","charges"];
const nonRolled_strings = ["name","lore", "tier","set","type","material","drop","quest","majorIds","classReq","atkSpd","displayName", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "durability", "duration"];
//omitted restrict - it's always "Custom Item"
//omitted displayName - either it's the same as name (repetitive) or it's "Custom Item"
//omitted category - can always get this from type
//omitted fixId - we will denote this early in the string.
function getCustomFromHash(hash) {
let name = hash.slice();
let statMap;
try {
if (name.slice(0,3) === "CI-") {
name = name.substring(3);
} else {
throw new Error("Not a custom item!");
}
let version = name.charAt(0);
let fixID = Boolean(parseInt(name.charAt(1),10));
let tag = name.substring(2);
statMap = new Map();
statMap.set("minRolls", new Map());
statMap.set("maxRolls", new Map());
if (version === "1") {
//do the things
if (fixID) {
statMap.set("fixId", true);
}
while (tag !== "") {
let id = ci_save_order[Base64.toInt(tag.slice(0,2))];
let len = Base64.toInt(tag.slice(2,4));
if (rolledIDs.includes(id)) {
let sign = parseInt(tag.slice(4,5),10);
let minRoll = Base64.toInt(tag.slice(5,5+len));
if (!fixID) {
let maxRoll = Base64.toInt(tag.slice(5+len,5+2*len));
if (sign > 1) {
maxRoll *= -1;
}
if (sign % 2 == 1) {
minRoll *= -1;
}
statMap.get("minRolls").set(id,minRoll);
statMap.get("maxRolls").set(id,maxRoll);
tag = tag.slice(5+2*len);
} else {
if (sign != 0) {
minRoll *= -1;
}
statMap.get("minRolls").set(id,minRoll);
statMap.get("maxRolls").set(id,minRoll);
tag = tag.slice(5+len);
}
} else {
let val;
if (nonRolled_strings.includes(id)) {
if (id === "tier") {
val = tiers[Base64.toInt(tag.charAt(2))];
len = -1;
} else if (id === "type") {
val = types[Base64.toInt(tag.charAt(2))];
len = -1;
} else if (id === "atkSpd") {
val = attackSpeeds[Base64.toInt(tag.charAt(2))];
len = -1;
} else if (id === "classReq") {
val = classes[Base64.toInt(tag.charAt(2))];
len = -1;
} else { //general case
val = tag.slice(4,4+len).replaceAll("%20"," ");
}
tag = tag.slice(4+len);
} else {
let sign = parseInt(tag.slice(4,5),10);
val = Base64.toInt(tag.slice(5,5+len));
if (sign == 1) {
val *= -1;
}
tag = tag.slice(5+len);
}
statMap.set(id, val);
}
}
statMap.set("hash","CI-"+name);
return new Custom(statMap);
}
} catch (error) {
//console.log(statMap);
return undefined;
}
}
/** An object representing a Custom Item. Mostly for vanity purposes.
* @dep Requires the use of nonRolledIDs and rolledIDs from display.js.
@ -43,8 +126,13 @@ class Custom{
setHash(hash) {
this.hash = hash;
this.statMap.set("hash",hash);
let ihash = hash.slice();
if (ihash.slice(0,3) !== "CI-") {
ihash = "CI-" + hash;
}
this.hash = ihash;
this.statMap.set("hash",ihash);
}
updateName(name) {
@ -61,12 +149,30 @@ class Custom{
//this.setHashVerbose(); //do NOT move sethash from here please
this.statMap.set("custom", true);
if (this.statMap.get("tier") === "Crafted") {
this.statMap.set("crafted", true);
for (const e of skp_elements) {
this.statMap.set(e+"DamLow", this.statMap.get(e+"Dam"));
for (const n of ["nDam","eDam","tDam","wDam","fDam","aDam"]) {
if (!(this.statMap.has(n) && this.statMap.get(n))) {
this.statMap.set(n,"0-0");
}
}
for (const id of ci_save_order) {
if (rolledIDs.includes(id)) {
if (!(this.statMap.get("minRolls").has(id) && this.statMap.get("minRolls").get(id))) {
this.statMap.get("minRolls").set(id,0);
this.statMap.get("maxRolls").set(id,0);
}
} else {
if (nonRolled_strings.includes(id)) {
if (!(this.statMap.has(id)&&this.statMap.get(id))) {
this.statMap.set(id,"");
}
} else {
if (!(this.statMap.has(id)&&this.statMap.get(id))) {
this.statMap.set(id,0);
}
}
}
this.statMap.set("nDamLow", this.statMap.get("nDam"));
}
if (this.statMap.get("type")) {
@ -78,11 +184,51 @@ class Custom{
} else if (weaponTypes.includes(this.statMap.get("type"))) {
this.statMap.set("category","weapon");
} else if (consumableTypes.includes(this.statMap.get("type"))) {
this.statMap.set("category","consumable")
this.statMap.set("category","consumable");
}
}
if(this.statMap.get("category") !== "weapon") {
if (this.statMap.get("tier") === "Crafted") {
this.statMap.set("crafted", true);
for (const e of skp_elements) {
this.statMap.set(e+"DamLow", this.statMap.get(e+"Dam"));
}
this.statMap.set("nDamLow", this.statMap.get("nDam"));
this.statMap.set("hpLow", this.statMap.get("hp"));
for (const e of skp_order) {
this.statMap.get("minRolls").set(e,this.statMap.get(e));
this.statMap.get("maxRolls").set(e,this.statMap.get(e));
}
// for (const e of ["durability", "duration"]) {
// if (this.statMap.get(e) === "") {
// this.statMap.set(e, [0,0]);
// } else {
// this.statMap.set(e, [this.statMap.get(e).split("-")[0],this.statMap.get(e).split("-")[1]])
// }
// }
this.statMap.set("lvlLow",this.statMap.get("lvl"));
if (this.statMap.get("category") === "weapon") {
//this is for powder purposes.
//users will likely not stick to the 0.9,1.1 rule because custom item. We will get around this by breaking everything and rewarding users for sticking to 0.9,1.1.
this.statMap.set("nDamBaseLow", Math.floor((parseFloat(this.statMap.get("nDamLow")) + parseFloat(this.statMap.get("nDam"))) / 2) );
this.statMap.set("nDamBaseHigh", Math.floor((parseFloat(this.statMap.get("nDamLow")) + parseFloat(this.statMap.get("nDam"))) / 2) );
for (const e in skp_elements) {
statMap.set(skp_elements[e]+"DamBaseLow", Math.floor((parseFloat(this.statMap.get(skp_elements[e]+"DamLow")) + parseFloat(this.statMap.get(skp_elements[e]+"Dam"))) / 2));
statMap.set(skp_elements[e]+"DamBaseHigh", Math.floor((parseFloat(this.statMap.get(skp_elements[e]+"DamLow")) + parseFloat(this.statMap.get(skp_elements[e]+"Dam"))) / 2));
}
}
}
if (this.statMap.get("category") !== "weapon") {
this.statMap.set("atkSpd", "");
for (const n in ["nDam","eDam","tDam","wDam","fDam","aDam"]) {
//this.statMap.set(n,"");
}
} else {
}
@ -91,9 +237,13 @@ class Custom{
} else {
this.statMap.set("displayName", "Custom Item");
}
this.statMap.set("powders",[]);
this.statMap.set("reqs",[this.statMap.get("strReq"),this.statMap.get("dexReq"),this.statMap.get("intReq"),this.statMap.get("defReq"),this.statMap.get("agiReq")]);
this.statMap.set("powders", []);
this.statMap.set("skillpoints", [this.statMap.get("str"),this.statMap.get("dex"),this.statMap.get("int"),this.statMap.get("def"),this.statMap.get("agi")]);
this.statMap.set("restrict", "Custom Item")
}

File diff suppressed because it is too large Load diff

View file

@ -8,10 +8,10 @@ const custom_url_tag = location.hash.slice(1);
console.log(custom_url_base);
console.log(custom_url_tag);
const BUILD_VERSION = "6.9.41";
const CUSTOM_BUILD_VERSION = "6.9.42.0";
function setTitle() {
let text = "WynnCustom version "+BUILD_VERSION;
let text = "WynnCustom version "+CUSTOM_BUILD_VERSION;
document.getElementById("header").classList.add("funnynumber");
document.getElementById("header").textContent = text;
}
@ -33,10 +33,6 @@ let neg_range = [1.3,0.7];
let itemMap = new Map();
/* Mapping from item names to set names. */
let idMap = new Map();
let redirectMap = new Map();
let roll_range_ids = ["neg_roll_range-choice-min","neg_roll_range-choice-max","pos_roll_range-choice-min","pos_roll_range-choice-max"];
@ -44,18 +40,6 @@ let roll_range_ids = ["neg_roll_range-choice-min","neg_roll_range-choice-max","p
function init() {
try {
//directly from builder.js. Removed irrelevant materials and no noneitems used here.
for (const item of items) {
if (item.remapID === undefined) {
itemMap.set(item.displayName, item);
idMap.set(item.id, item.displayName);
}
else {
redirectMap.set(item.id, item.remapID);
}
}
console.log(itemMap);
populateFields();
decodeCustom(custom_url_tag);
@ -214,15 +198,18 @@ function calculateCustom() {
player_custom_item = new Custom(statMap);
let custom_str = encodeCustom(player_custom_item.statMap, false);
document.getElementById("right-container").classList.remove("sticky-box");
let custom_str = encodeCustom(player_custom_item.statMap, true);
location.hash = custom_str;
custom_str = encodeCustom(player_custom_item.statMap, true);
player_custom_item.setHash(custom_str);
console.log(player_custom_item.statMap.get("hash"));
displayExpandedItem(player_custom_item.statMap, "custom-stats");
//console.log(player_custom_item.statMap);
console.log(player_custom_item.statMap);
}catch (error) {
//USE THE ERROR <p>S!
@ -250,6 +237,9 @@ function calculateCustom() {
*/
function encodeCustom(custom, verbose) {
if (custom) {
if (custom.statMap) {
custom = custom.statMap;
}
let hash = "1";
//version 1
if (custom.has("fixID") && custom.get("fixID")) {
@ -262,36 +252,45 @@ function encodeCustom(custom, verbose) {
if (rolledIDs.includes(id)) {
let val_min = custom.get("minRolls").has(id) ? custom.get("minRolls").get(id) : 0;
let val_max = custom.get("maxRolls").has(id) ? custom.get("maxRolls").get(id) : 0;
let sign = (Boolean(val_min / Math.abs(val_min) < 0) | 0) + 2*(Boolean(val_max / Math.abs(val_max) < 0) | 0) // 0 - both pos 1 - min neg max pos 2 - min pos max neg (how?) 3 - min neg max neg
//console.log(id + ": " + sign);
let min_len = Math.max(1,Math.ceil(log(64,Math.abs(val_min))));
let max_len = Math.max(1,Math.ceil(log(64,Math.abs(val_max))));
let len = Math.max(min_len,max_len);
val_min = Math.abs(val_min);
val_max = Math.abs(val_max);
if ( val_min != 0 || val_max != 0 ) {
//hash += Base64.fromIntN(i,2) + Base64.fromIntN(val_min,Math.max(1,Math.ceil(log(64,Math.abs(val_min))))) + ":" + Base64.fromIntN(val_max,Math.max(1,Math.ceil(log(64,Math.abs(val_min))))) + "_";
if (custom.get("fixID")) {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + Base64.fromIntN(val_min, len);
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + sign + Base64.fromIntN(val_min, len);
} else {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + Base64.fromIntN(val_min, len) + Base64.fromIntN(val_max,len);
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + sign + Base64.fromIntN(val_min, len) + Base64.fromIntN(val_max,len);
}
}
} else {
let damages = ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam","nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_"];
let val = custom.get(id);
if (typeof(val) === "string" && val !== "") {
if ((damages.includes(id) && val === "0-0") || (!verbose && (id === "lore" || id === "majorIds"))) { continue; }
if ((damages.includes(id) && val === "0-0") || (!verbose && ["lore","majorIds","quest","materials","drop","set"].includes(id))) { continue; }
if (id === "type") {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(types.indexOf(val.substring(0,1).toUpperCase()+val.slice(1)),1);
} else if (id === "tier") {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(tiers.indexOf(val),1);
} else if (id === "atkSpd") {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(atkSpds.indexOf(val),1);
hash += Base64.fromIntN(i,2) + Base64.fromIntN(attackSpeeds.indexOf(val),1);
} else if (id === "classReq") {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(classes.indexOf(val),1);
} else {
hash += Base64.fromIntN(i,2) + Base64.fromIntN(val.replace(" ", "%20").length,2) + val.replace(" ", "%20"); //values cannot go above 4096 chars!!!! Is this ok?
hash += Base64.fromIntN(i,2) + Base64.fromIntN(val.replaceAll(" ", "%20").length,2) + val.replaceAll(" ", "%20"); //values cannot go above 4096 chars!!!! Is this ok?
}
} else if (typeof(val) === "number" && val != 0) {
let len = Math.max(1,Math.ceil(log(64,Math.abs(val))));
let sign = Boolean(val / Math.abs(val) < 0) | 0;
//console.log(sign);
//hash += Base64.fromIntN(i,2) + Base64.fromIntN(val,Math.max(1,Math.ceil(log(64,Math.abs(val))))) + "_";
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + Base64.fromIntN(val,len);
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + sign + Base64.fromIntN(Math.abs(val),len);
}
}
}
@ -303,6 +302,10 @@ function encodeCustom(custom, verbose) {
function decodeCustom(custom_url_tag) {
if (custom_url_tag) {
if (custom_url_tag.slice(0,3) === "CI-") {
custom_url_tag = custom_url_tag.substring(3);
location.hash = location.hash.substring(3);
}
let version = custom_url_tag.charAt(0);
let fixID = Boolean(parseInt(custom_url_tag.charAt(1),10));
let tag = custom_url_tag.substring(2);
@ -313,7 +316,7 @@ function decodeCustom(custom_url_tag) {
if (version === "1") {
//do the things
if (fixID) {
statMap.set("fixId", "true");
statMap.set("fixId", true);
toggleButton("fixID-choice");
toggleYN("fixID-choice");
toggleFixed(document.getElementById("fixID-choice"));
@ -322,45 +325,60 @@ function decodeCustom(custom_url_tag) {
let id = ci_save_order[Base64.toInt(tag.slice(0,2))];
let len = Base64.toInt(tag.slice(2,4));
if (rolledIDs.includes(id)) {
let minRoll = Base64.toInt(tag.slice(4,4+len));
let sign = parseInt(tag.slice(4,5),10);
let minRoll = Base64.toInt(tag.slice(5,5+len));
if (!fixID) {
let maxRoll = Base64.toInt(tag.slice(4+len,4+2*len));
let maxRoll = Base64.toInt(tag.slice(5+len,5+2*len));
if (sign > 1) {
maxRoll *= -1;
}
if (sign % 2 == 1) {
minRoll *= -1;
}
setValue(id+"-choice-min", minRoll);
setValue(id+"-choice-max", maxRoll);
statMap.get("minRolls").set(id,minRoll);
statMap.get("maxRolls").set(id,maxRoll);
tag = tag.slice(4+2*len);
tag = tag.slice(5+2*len);
} else {
if (sign != 0) {
minRoll *= -1;
}
setValue(id+"-choice-fixed", minRoll);
statMap.get("minRolls").set(id,minRoll);
statMap.get("maxRolls").set(id,minRoll);
tag = tag.slice(4+len);
tag = tag.slice(5+len);
}
} else {
let val;
let elem = document.getElementById(id+"-choice");
if (elem.classList.contains("number-input")) {
val = Base64.toInt(tag.slice(4,4+len));
} else if (elem.classList.contains("string-input") || classList.contains("array-input")) {
//let elem = document.getElementById(id+"-choice");
if (nonRolled_strings.includes(id)) {
if (id === "tier") {
val = tiers[Base64.toInt(tag.charAt(2))];
len = -1;
} else if (id === "type") {
val = types[Base64.toInt(tag.charAt(2))];
len = -1;
}
else if (id === "atkSpd") {
val = atkSpds[Base64.toInt(tag.charAt(2))];
} else if (id === "atkSpd") {
val = attackSpeeds[Base64.toInt(tag.charAt(2))];
len = -1;
} else if (id === "classReq") {
val = classes[Base64.toInt(tag.charAt(2))];
len = -1;
} else { //general case
val = tag.slice(4,4+len).replace("%20"," ");
val = tag.slice(4,4+len).replaceAll("%20"," ");
}
tag = tag.slice(4+len);
} else {
val = "";
let sign = parseInt(tag.slice(4,5),10);
val = Base64.toInt(tag.slice(5,5+len));
if (sign == 1) {
val *= -1;
}
tag = tag.slice(5+len);
}
statMap.set(id, val);
setValue(id+"-choice", val);
tag = tag.slice(4+len);
}
}
statMap.set("hash",custom_url_tag);
@ -404,9 +422,9 @@ function populateFields() {
class_list.appendChild(el);
}
let item_list = document.getElementById("base-list");
for (const [baseItem,value] of itemMap) {
for (const name of itemMap.keys()) {
let el = document.createElement("option");
el.value = baseItem;
el.value = name;
item_list.appendChild(el);
}
}
@ -471,10 +489,10 @@ function useBaseItem(elem) {
//If it starts with CR-, try creating a craft
if(!baseItem) {
baseItem = getCraftFromHash(itemName).statMap;
baseItem = getCraftFromHash(itemName) ? getCraftFromHash(itemName) : (getCustomFromHash(itemName) ? getCustomFromHash(itemName) : null);
baseItem = baseItem.statMap;
console.log(baseItem);
}
//If it starts with CI-, try creating a custom (TODO)
//If the item exists, go through stats and assign to values!
if(baseItem) {

View file

@ -3,7 +3,6 @@ const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["va
// If spell mult is 0, its melee damage and we don't multiply by attack speed.
// externalStats should be a map
function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, spellMultiplier, weapon, total_skillpoints, damageMultiplier, externalStats) {
let buildStats = new Map(stats);
if(externalStats) { //if nothing is passed in, then this hopefully won't trigger
for (const entry of externalStats) {
@ -21,6 +20,8 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
}
}
let powders = weapon.get("powders").slice();
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
let damages = [];
const rawDamages = buildStats.get("damageRaw");
@ -28,10 +29,14 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
const damage_vals = rawDamages[i].split("-").map(Number);
damages.push(damage_vals);
}
let damageBases = [];
if (weapon.get("tier") === "Crafted") {
damageBases = buildStats.get("damageBases").slice();
}
// Applying spell conversions
let neutralBase = damages[0].slice();
let neutralRemainingRaw = damages[0];
let neutralRemainingRaw = damages[0].slice();
for (let i = 0; i < 5; ++i) {
let conversionRatio = spellConversions[i+1]/100;
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
@ -43,11 +48,23 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
}
//console.log(damages);
let rawBoosts = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]];
let powders = weapon.get("powders").slice();
//Double powder apply for weapons - this implementation is wrong
//Powder application for Crafted weapons - this implementation is RIGHT YEAAAAAAAAA
//1st round - apply each as ingred, 2nd round - apply as normal
if (weapon.get("tier") === "Crafted") {
powders = powders.flatMap(x => [x,x]);
for (const p of powders) {
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 diff = Math.floor(damageBases[0] * powder.convert/100);
damageBases[0] -= diff;
damageBases[element+1] += diff + Math.floor( (powder.min + powder.max) / 2 );
}
//update all damages
for (let i = 0; i < damages.length; i++) {
damages[i] = [Math.floor(damageBases[i] * 0.9), Math.floor(damageBases[i] * 1.1)];
}
neutralRemainingRaw = damages[0].slice();
neutralBase = damages[0].slice();
}
//apply powders to weapon
@ -64,13 +81,10 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
}
damages[0] = neutralRemainingRaw;
damages[element+1][0] += powder.min;
damages[element+1][1] += powder.max;
}
//Double powder apply for weapons - this implementation is wrong
if (weapon.get("tier") === "Crafted") {
powders = powders.flatMap(x => [x,x]);
}
let damageMult = damageMultiplier;
let melee = false;

View file

@ -113,7 +113,7 @@ function expandRecipe(recipe) {
for (const id of normIDs) {
expandedRecipe.set(id,recipe[id]);
}
let rangeIDs = ["durability", "healthOrDamage", "lvl", "duration", "basicDuration"];
let rangeIDs = ["durability","lvl", "healthOrDamage", "duration", "basicDuration"];
for (const id of rangeIDs) {
if(recipe[id]){
expandedRecipe.set(id, [recipe[id]['minimum'], recipe[id]['maximum']]);
@ -444,9 +444,11 @@ function displayExpandedItem(item, parent_id){
}
} else {
stats.set("damageRaw", [item.get("nDamLow"), item.get("eDamLow"), item.get("tDamLow"), item.get("wDamLow"), item.get("fDamLow"), item.get("aDamLow")]);
stats.set("damageBases", [item.get("nDamBaseLow"),item.get("eDamBaseLow"),item.get("tDamBaseLow"),item.get("wDamBaseLow"),item.get("fDamBaseLow"),item.get("aDamBaseLow")]);
let resultsLow = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], 0, 0, 0, item, [0, 0, 0, 0, 0], 1, undefined);
let damagesLow = resultsLow[2];
stats.set("damageRaw", [item.get("nDam"), item.get("eDam"), item.get("tDam"), item.get("wDam"), item.get("fDam"), item.get("aDam")]);
stats.set("damageBases", [item.get("nDamBaseHigh"),item.get("eDamBaseHigh"),item.get("tDamBaseHigh"),item.get("wDamBaseHigh"),item.get("fDamBaseHigh"),item.get("aDamBaseHigh")]);
let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], 0, 0, 0, item, [0, 0, 0, 0, 0], 1, undefined);
let damages = results[2];
@ -531,6 +533,7 @@ function displayExpandedItem(item, parent_id){
active_elem = document.createElement('table');
active_elem.classList.add('itemtable');
}
active_elem.style.maxWidth = "100%";
parent_div.appendChild(active_elem);
}
else if (command.charAt(0) === "!") {
@ -540,7 +543,7 @@ function displayExpandedItem(item, parent_id){
}
}
else {
let id = command; //warp
let id = command;
if(( nonRolledIDs.includes(id) && item.get(id))){//nonRolledID & non-0/non-null/non-und ID
if (id === "slots") {
let p_elem = document.createElement("p");
@ -577,7 +580,19 @@ function displayExpandedItem(item, parent_id){
} else if (id === "majorIds") {
let p_elem = document.createElement("p");
p_elem.classList.add("itemp");
p_elem.textContent = "Major IDs: " + item.get(id).toString();
let title_elem = document.createElement("b");
title_elem.textContent = "Major IDs: ";
let b_elem = document.createElement("b");
b_elem.classList.add("Crafted");
b_elem.textContent = item.get(id).toString();
p_elem.appendChild(title_elem);
p_elem.appendChild(b_elem);
active_elem.appendChild(p_elem);
} else if (id === "lvl" && item.get("tier") === "Crafted") {
let p_elem = document.createElement("p");
p_elem.classList.add("itemp");
p_elem.textContent = "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id);
active_elem.appendChild(p_elem);
} else {
let p_elem;
@ -591,30 +606,31 @@ function displayExpandedItem(item, parent_id){
if (item.get("tier") !== " ") {
p_elem.classList.add(item.get("tier"));
}
if(item.get("crafted")) {
p_elem.remove();
p_elem = document.createElement("a");
p_elem.classList.add('itemp');
p_elem.classList.add("smalltitle");
p_elem.classList.add(item.get("tier"));
p_elem.href = url_base.replace("crafter.html","").replace("customizer.html","") + "crafter.html#" + item.get("hash");
p_elem.target = "_blank";
p_elem.textContent = item.get(id);
active_elem.appendChild(p_elem);
}
if(item.get("custom")) {
p_elem.remove();
p_elem = document.createElement("a");
p_elem.classList.add('itemp');
p_elem.classList.add("smalltitle");
p_elem.classList.add(item.get("tier"));
p_elem.href = url_base.replace("crafter.html","").replace("customizer.html","") + "customizer.html#" + item.get("hash");
p_elem.target = "_blank";
p_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "none");
if (item.get("custom")) {
p_elem.href = url_base.replace(/\w+.html/, "") + "customizer.html#" + item.get("hash");
p_elem.textContent = item.get("displayName");
} else if (item.get("crafted")) {
p_elem.href = url_base.replace(/\w+.html/, "") + "crafter.html#" + item.get("hash");
p_elem.textContent = item.get(id);
} else {
p_elem.href = url_base.replace(/\w+.html/, "") + "item.html#" + item.get("displayName");
p_elem.textContent = item.get("displayName");
active_elem.appendChild(p_elem);
}
p_elem.target = "_blank";
active_elem.appendChild(p_elem);
let img = document.createElement("img");
if (item && item.has("type")) {
img.src = "/media/items/generic-" + item.get("type") + ".png";
}
img.alt = item.get("type");
img.style = " z=index: 1;max-width: 64px; max-height: 64px; position: relative; top: 50%; transform: translateY(-50%);";
let bckgrd = document.createElement("p");
@ -625,6 +641,7 @@ function displayExpandedItem(item, parent_id){
bckgrd.appendChild(img);
} else if (id === "lore") {
p_elem.style = "font-style: italic";
p_elem.classList.add("lore");
} else if (skp_order.includes(id)) { //id = str, dex, int, def, or agi
if ( item.get("tier") !== "Crafted" && active_elem.nodeName === "DIV") {
p_elem.textContent = "";
@ -703,6 +720,11 @@ function displayExpandedItem(item, parent_id){
}
row.appendChild(desc_elem);
if (item.get("maxRolls").get(id) > 0) {
style = "positive";
} else if (item.get("maxRolls").get(id) < 0 ) {
style = "negative";
}
let max_elem = document.createElement('td');
max_elem.classList.add('right');
max_elem.classList.add(style);
@ -799,7 +821,12 @@ function displayExpandedItem(item, parent_id){
charges.classList.add("spaceleft");
active_elem.appendChild(charges);
}
if (typeof(dura) === "string") {
dura_elem.textContent += dura + suffix;
} else {
dura_elem.textContent += dura[0]+"-"+dura[1] + suffix;
}
active_elem.append(dura_elem);
}
@ -811,6 +838,17 @@ function displayExpandedItem(item, parent_id){
item_desc_elem.textContent = item.get("tier")+" "+item.get("type");
active_elem.append(item_desc_elem);
}
//Show item hash if applicable
if (item.get("crafted") || item.get("custom")) {
let item_desc_elem = document.createElement("p");
item_desc_elem.classList.add('itemp');
item_desc_elem.style.maxWidth = "100%";
item_desc_elem.style.wordWrap = "break-word";
item_desc_elem.style.wordBreak = "break-word";
item_desc_elem.textContent = item.get("hash");
active_elem.append(item_desc_elem);
}
}
/* Displays stats about a recipe that are NOT displayed in the craft stats.
@ -821,7 +859,7 @@ function displayRecipeStats(craft, parent_id) {
let elem = document.getElementById(parent_id);
elem.textContent = "";
recipe = craft["recipe"];
tiers = craft["mat_tiers"];
mat_tiers = craft["mat_tiers"];
ingreds = [];
for (const n of craft["ingreds"]) {
ingreds.push(n.get("name"));
@ -838,7 +876,7 @@ function displayRecipeStats(craft, parent_id) {
mats.classList.add("itemp");
mats.textContent = "Crafting Materials: ";
for (let i = 0; i < 2; i++) {
let tier = tiers[i];
let tier = mat_tiers[i];
let row = document.createElement("p");
row.classList.add("left");
let b = document.createElement("b");
@ -1733,7 +1771,7 @@ function displayPowderSpecials(parent_elem, powderSpecials, build) {
if(key === "Damage"){
effect.textContent += elementIcons[powderSpecialStats.indexOf(special[0])];
}
if(special[0]["weaponSpecialName"] === "Air Prison" && key === "Damage Boost") {
if(special[0]["weaponSpecialName"] === "Wind Prison" && key === "Damage Boost") {
effect.textContent += " (only 1st hit)";
}
specialEffects.appendChild(effect);
@ -2006,3 +2044,151 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell
}
}
}
/** Displays the ID costs of an item
*
* @param {Map} item - the statMap of an item.
* @param {String} elemID - the id of the parent element.
*/
function displayIDCosts(item, elemID) {
let parent_elem = document.getElementById(elemID);
let tier = item.get("tier");
if ( (item.has("fixID") && item.get("fixID")) || ["Normal","Crafted","Custom","none", " ",].includes(item.get("tier"))) {
return;
} else {
/** Returns the number of inventory slots minimum an amount of emeralds would take up + the configuration of doing so.
* Returns an array of [invSpace, E, EB, LE, Stx LE]
*
* @param {number} ems - the total numerical value of emeralds to compact.
*/
function emsToInvSpace(ems) {
let stx = Math.floor(ems/262144);
ems -= stx*4096*64;
let LE = Math.floor(ems/4096);
ems -= LE*4096;
let EB = Math.floor(ems/64);
ems -= EB*64;
let e = ems;
return [ stx + Math.ceil(LE/64) + Math.ceil(EB/64) + Math.ceil(e/64) , e, EB, LE, stx];
}
/**
*
* @param {String} tier - item tier
* @param {Number} lvl - item level
*/
function getIDCost(tier, lvl) {
switch (tier) {
case "Unique":
return Math.round(0.5*lvl + 3);
case "Rare":
return Math.round(1.2*lvl + 8);
case "Legendary":
return Math.round(4.5*lvl + 12);
case "Fabled":
return Math.round(12*lvl + 26);
case "Mythic":
return Math.round(18*lvl + 90);
case "Set":
return Math.round(1.5*lvl + 8)
default:
return -1;
}
}
parent_elem.style = "display: visible";
let lvl = item.get("lvl");
if (typeof(lvl) === "string") { lvl = parseFloat(lvl); }
let title_elem = document.createElement("p");
title_elem.classList.add("smalltitle");
title_elem.style.color = "white";
title_elem.textContent = "Identification Costs";
parent_elem.appendChild(title_elem);
parent_elem.appendChild(document.createElement("br"));
let grid_item = document.createElement("div");
grid_item.style.display = "flex";
grid_item.style.flexDirection = "rows";
grid_item.style.flexWrap = "wrap";
grid_item.style.gap = "5px";
parent_elem.appendChild(grid_item);
let IDcost = getIDCost(tier, lvl);
let initIDcost = IDcost;
let invSpace = emsToInvSpace(IDcost);
let rerolls = 0;
while(invSpace[0] <= 28 && IDcost > 0) {
let container = document.createElement("div");
container.classList.add("container");
container.style = "grid-item-" + (rerolls+1);
container.style.maxWidth = "max(120px, 15%)";
let container_title = document.createElement("p");
container_title.style.color = "white";
if (rerolls == 0) {
container_title.textContent = "Initial ID Cost: ";
} else {
container_title.textContent = "Reroll to [" + (rerolls+1) + "] Cost:";
}
container.appendChild(container_title);
let total_cost_container = document.createElement("p");
let total_cost_number = document.createElement("b");
total_cost_number.classList.add("Set");
total_cost_number.textContent = IDcost + " ";
let total_cost_suffix = document.createElement("b");
total_cost_suffix.textContent = "emeralds."
total_cost_container.appendChild(total_cost_number);
total_cost_container.appendChild(total_cost_suffix);
container.appendChild(total_cost_container);
let OR = document.createElement("p");
OR.classList.add("center");
OR.textContent = "OR";
container.appendChild(OR);
let esuffixes = ["", "emeralds.", "EB.", "LE.", "stacks of LE."];
for (let i = 4; i > 0; i--) {
let n_container = document.createElement("p");
let n_number = document.createElement("b");
n_number.classList.add("Set");
n_number.textContent = invSpace[i] + " ";
let n_suffix = document.createElement("b");
n_suffix.textContent = esuffixes[i];
n_container.appendChild(n_number);
n_container.appendChild(n_suffix);
container.appendChild(n_container);
}
grid_item.appendChild(container);
rerolls += 1;
IDcost = Math.round(initIDcost * (5 ** rerolls));
invSpace = emsToInvSpace(IDcost);
}
}
}
/** Displays Additional Info for
*
* @param {Map} item - the statMap of the item
* @param {String} elemID - the parent element's id
* @returns
*/
function displayAdditionalInfo(item, elemID) {
let parent_elem = document.getElementById(elemID);
parent_elem.classList.add("left");
let droptype_elem = document.createElement("div");
droptype_elem.classList.add("container");
droptype_elem.style.marginBottom = "5px";
droptype_elem.textContent = "Drop type: " + (item.has("drop") ? item.get("drop"): "NEVER");
parent_elem.appendChild(droptype_elem);
let warning_elem = document.createElement("div");
warning_elem.classList.add("container");
warning_elem.style.marginBottom ="5px";
warning_elem.textContent = "This page is incomplete. Will work on it later.";
parent_elem.appendChild(warning_elem);
return;
}

File diff suppressed because one or more lines are too long

View file

@ -21,23 +21,28 @@
<div class = "headerleft">
<a href = "./" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/builder.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnBuilder</div>
</img>
</a>
<a href = "./crafter.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/crafter.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnCrafter</div>
</img>
</a>
<a href = "./items.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/searcher.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnAtlas</div>
</img>
</a>
<a href = "./customizer.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/custom.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnCustom</div>
</img>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
@ -73,19 +78,19 @@
<input class="iteminput" list="helmet-items" id="helmet-choice" name="helmet-choice" placeholder="No Helmet" tabindex="1"/>
<datalist id="helmet-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label id="helmet-slots" for="helmet-powder">X slots</label><br>
<input class="powderinput" type="text" id="helmet-powder" name="helmet-powder" placeholder="Example: t6t6" tabindex="2"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label for="ring1-choice">Ring 1:</label><br>
<input class="iteminput" list="ring1-items" id="ring1-choice" name="ring1-choice" placeholder="No Ring 1" tabindex="11"/>
<datalist id="ring1-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
</tr>
<tr>
@ -94,19 +99,19 @@
<input class="iteminput" list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate" tabindex="3"/>
<datalist id="chestplate-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label id="chestplate-slots" for="chestplate-powder">X slots</label><br>
<input class="powderinput" type="text" id="chestplate-powder" name="chestplate-powder" tabindex="4"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label for="ring2-choice">Ring 2:</label><br>
<input class="iteminput" list="ring2-items" id="ring2-choice" name="ring2-choice" placeholder="No Ring 2" tabindex="12"/>
<datalist id="ring2-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
</tr>
<tr>
@ -115,19 +120,19 @@
<input class="iteminput" list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings" tabindex="5"/>
<datalist id="leggings-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label id="leggings-slots" for="leggings-powder">X slots</label><br>
<input class="powderinput" type="text" id="leggings-powder" name="leggings-powder" tabindex="6"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label for="bracelet-choice">Bracelet:</label><br>
<input class="iteminput" list="bracelet-items" id="bracelet-choice" name="bracelet-choice" placeholder="No Bracelet" tabindex="13"/>
<datalist id="bracelet-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
</tr>
<tr>
@ -136,19 +141,19 @@
<input class="iteminput" list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots" tabindex="7"/>
<datalist id="boots-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label id="boots-slots" for="boots-powder">X slots</label><br>
<input class="powderinput" type="text" id="boots-powder" name="boots-powder" tabindex="8"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<label for="necklace-choice">Necklace:</label><br>
<input class="iteminput" list="necklace-items" id="necklace-choice" name="necklace-choice" placeholder="No Necklace" tabindex="14"/>
<datalist id="necklace-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
</tr>
<tr>
@ -158,19 +163,19 @@
<input class="iteminput" list="weapon-items" id="weapon-choice" name="weapon-choice" placeholder="No Weapon" value="" tabindex="9"/>
<datalist id="weapon-items">
</datalist>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<br>
<label id="weapon-slots" for="weapon-powder">X slots</label><br>
<input class="powderinput" type="text" id="weapon-powder" name="weapon-powder" tabindex="10"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; white-space: nowrap; word-break:break-word;"></p>
</td>
<td class="left">
<br/>
<label for="level-choice">Level:</label><br>
<input class="iteminput" id="level-choice" name="level-choice" value="106" tabindex="15"/>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif;"></p>
<p class="error" style="color: red; top: 30px; font-size: 10px; padding: 0; margin: 0; height: 5px; font-family: 'Nunito', sans-serif; word-break:break-word;"></p>
</td>
</tr>
</table>
@ -993,7 +998,7 @@
<div class = "center">
<footer>
<div class="center" id="header2">
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with Atlas Inc (JavaScript required to function, nothing works without js)</p>
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with <a href = "./atlas.html" target = "_blank" class = "link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
</div>
<div class="center" id="credits">
@ -1010,6 +1015,8 @@
<script type="text/javascript" src="display.js"></script>
<script type="text/javascript" src="load.js"></script>
<script type="text/javascript" src="load_ing.js"></script>
<script type="text/javascript" src="custom.js"></script>
<script type="text/javascript" src="customizer.js"></script>
<script type="text/javascript" src="craft.js"></script>
<script type="text/javascript" src="crafter.js"></script>
<script type="text/javascript" src="build.js"></script>

31
item-narrow.css Normal file
View file

@ -0,0 +1,31 @@
.container{
width: 95%;
border: 3px solid #BCBCBC;
border-radius: 3px;
padding: 2% 4% 4%;
}
.title{
text-align: center;
font-size: 150%;
}
.smalltitle{
text-align: center;
font-size: 125%;
margin-top: 10px;
margin-bottom: 4px;
}
.overall-container {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 5px;
grid-auto-rows: minmax(60px, auto);
}
.overall {
margin-top: 10px;
margin-bottom: 40px;
width: 96%;
}
.identification-costs {
margin-top: 10px;
margin-bottom: 20px;
}

31
item-wide.css Normal file
View file

@ -0,0 +1,31 @@
.container{
width: 95%;
border: 3px solid #BCBCBC;
border-radius: 3px;
padding: 2% 4% 4%;
}
.title{
text-align: center;
font-size: 150%;
}
.smalltitle{
text-align: center;
font-size: 125%;
margin-top: 10px;
margin-bottom: 4px;
}
.overall-container {
display: grid;
grid-template-columns: 1fr 3fr;
gap: max(10%,100px);
grid-auto-rows: minmax(60px, auto);
}
.overall {
margin-top: 10px;
margin-bottom: 40px;
width: 96%;
}
.identification-costs {
margin-top: 10px;
margin-bottom: 20px;
}

88
item.html Normal file
View file

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<meta name="HandheldFriendly" content="true" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
<!-- nunito font, copying wynndata -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="items.css">
<link rel="stylesheet" media="screen and (min-width: 1100px)" href="item-wide.css"/>
<link rel="stylesheet" media="screen and (max-width: 1099px)" href="item-narrow.css"/>
<link rel="icon" href="./favicon.png">
<link rel="manifest" href="manifest.json">
<title>Wynn Clientside</title>
</head>
<body class="all">
<div class="center">
<header class = "header nomarginp">
<div class = "headerleft">
<a href = "./" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/builder.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnBuilder</div>
</a>
<a href = "./crafter.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/crafter.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnCrafter</div>
</a>
<a href = "./items.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/searcher.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnAtlas</div>
</a>
<a href = "./customizer.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/custom.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnCustom</div>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
<div >
<p class = "itemp" id = "header">Item Info</p>
</div>
</div>
<div class = "headerright">
</div>
</header>
</div>
<br>
<div class = "overall center">
<div class = "overall-container" style = "display:grid">
<div class = "item-view-container hide-container-grid" display = "grid-item-1">
<p class = "title"></p>
<div class = "item-view container" id = "item-view">
</div>
</div>
<div class = "info" display = "grid-item-2">
<p class = "title" >Additional Info</p>
<div class = "additional-info" id = "additional-info"></div>
</div>
</div>
<div class = "container identification-costs" id = "identification-costs" style = "display: none">
</div>
</div>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript" src="build_utils.js"></script>
<script type="text/javascript" src="damage_calc.js"></script>
<script type="text/javascript" src="powders.js"></script>
<script type="text/javascript" src="load.js"></script>
<script type="text/javascript" src="load_ing.js"></script>
<script type="text/javascript" src="crafter.js"></script>
<script type="text/javascript" src="craft.js"></script>
<script type="text/javascript" src="display.js"></script>
<script type="text/javascript" src="custom.js"></script>
<script type="text/javascript" src="customizer.js"></script>
<script type="text/javascript" src = "item.js"></script>
</body>
</html>

53
item.js Normal file
View file

@ -0,0 +1,53 @@
/*
* TESTING SECTION
*/
const item_url_base = location.href.split("#")[0];
const item_url_tag = location.hash.slice(1);
console.log(item_url_base);
console.log(item_url_tag);
const ITEM_BUILD_VERSION = "6.9.42";
function setTitle() {
let text = "WynnInfo version "+ITEM_BUILD_VERSION;
document.getElementById("header").classList.add("funnynumber");
document.getElementById("header").textContent = text;
}
setTitle();
/*
* END testing section
*/
let item;
function init() {
//console.log(item_url_tag);
//displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view");
try{
if(itemMap) {
item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []);
displayExpandedItem(item, "item-view");
displayIDCosts(item, "identification-costs");
displayAdditionalInfo(item, "additional-info");
console.log(item);
}
} catch (error) {
console.log(error);
}
}
load_init(init);
//load_ing_init(init);

View file

@ -40,6 +40,11 @@
</img>
<div class = "tooltiptext center">WynnCustom</div>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
<div >

View file

@ -33,6 +33,11 @@
</img>
<div class = "tooltiptext center">WynnCustom</div>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
<div >

77
load.js
View file

@ -5,7 +5,11 @@ let db;
let reload = false;
let items;
let sets;
let itemMap;
let idMap;
let redirectMap;
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes);
let itemLists = new Map();
/*
* Load item set from local DB. Calls init() on success.
*/
@ -20,7 +24,7 @@ async function load_local(init_func) {
request.onsuccess = function(event) {
console.log("Successfully read local item db.");
items = request.result;
console.log(items);
//console.log(items);
let request2 = sets_store.openCursor();
sets = {};
@ -37,6 +41,7 @@ async function load_local(init_func) {
else {
console.log("Successfully read local set db.");
//console.log(sets);
init_maps();
init_func();
}
}
@ -75,6 +80,8 @@ async function load(init_func) {
items = result.items;
sets = result.sets;
// let clear_tx = db.transaction(['item_db', 'set_db'], 'readwrite');
// let clear_items = clear_tx.objectStore('item_db');
// let clear_sets = clear_tx.objectStore('item_db');
@ -105,6 +112,7 @@ async function load(init_func) {
add_promises.push(add_tx.complete);
Promise.all(add_promises).then((values) => {
db.close();
init_maps();
init_func();
});
}
@ -152,3 +160,68 @@ function load_init(init_func) {
console.log("DB setup complete...");
}
}
function init_maps() {
//warp
itemMap = new Map();
/* Mapping from item names to set names. */
idMap = new Map();
redirectMap = new Map();
for (const it of itemTypes) {
itemLists.set(it, []);
}
let noneItems = [
["armor", "helmet", "No Helmet"],
["armor", "chestplate", "No Chestplate"],
["armor", "leggings", "No Leggings"],
["armor", "boots", "No Boots"],
["accessory", "ring", "No Ring 1"],
["accessory", "ring", "No Ring 2"],
["accessory", "bracelet", "No Bracelet"],
["accessory", "necklace", "No Necklace"],
["weapon", "dagger", "No Weapon"],
];
for (let i = 0; i < 9; i++) {
let item = Object();
item.slots = 0;
item.category = noneItems[i][0];
item.type = noneItems[i][1];
item.name = noneItems[i][2];
item.displayName = item.name;
item.set = null;
item.quest = null;
item.skillpoints = [0, 0, 0, 0, 0];
item.has_negstat = false;
item.reqs = [0, 0, 0, 0, 0];
item.fixID = true;
item.tier = "Normal";//do not get rid of this @hpp
item.id = 10000 + i;
item.nDam = "0-0";
item.eDam = "0-0";
item.tDam = "0-0";
item.wDam = "0-0";
item.fDam = "0-0";
item.aDam = "0-0";
noneItems[i] = item;
}
items = items.concat(noneItems);
//console.log(items);
for (const item of items) {
if (item.remapID === undefined) {
itemLists.get(item.type).push(item.displayName);
itemMap.set(item.displayName, item);
if (noneItems.includes(item)) {
idMap.set(item.id, "");
}
else {
idMap.set(item.id, item.displayName);
}
}
else {
redirectMap.set(item.id, item.remapID);
}
}
console.log(itemMap);
}

View file

@ -7,6 +7,15 @@ let ireload = false;
let ings;
let recipes;
let ingMap = new Map();
let ingList = [];
let recipeMap = new Map();
let recipeList = [];
let ingIDMap = new Map();
let recipeIDMap = new Map();
/*
* Load item set from local DB. Calls init() on success.
*/
@ -29,6 +38,7 @@ async function ing_load_local(init_func) {
request4.onsuccess = function(event) {
console.log("Successfully read local recipe db.");
recipes = request4.result;
init_ing_maps();
init_func();
}
await get_tx.complete;
@ -86,6 +96,7 @@ async function load_ings(init_func) {
add_promises.push(add_tx3.complete);
Promise.all(add_promises).then((values) => {
idb.close();
init_ing_maps();
init_func();
});
}
@ -132,3 +143,70 @@ function load_ing_init(init_func) {
console.log("DB setup complete...");
}
}
function init_ing_maps() {
let ing = Object();
ing.name = "No Ingredient";
ing.displayName = "No Ingredient";
ing.tier = 0;
ing.lvl = 0;
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"];
ing.ids= {};
ing.itemIDs = {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
ing.consumableIDs = {"dura": 0, "charges": 0};
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
ing.id = 4000;
ingMap.set(ing["displayName"], ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
for (let i = 0; i < 5; i ++) {
for (const powderIng of powderIngreds) {
let ing = Object();
ing.name = "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1);
ing.displayName = ing.name
ing.tier = 0;
ing.lvl = 0;
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING"];
ing.ids = {};
ing.isPowder = true;
ing.pid = 6*i + powderIngreds.indexOf(powderIng);
ing.id = 4001 + ing.pid;
ing.itemIDs = {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
switch(i) {
case 0:
ing.itemIDs["strReq"] = powderIng["skpReq"];
break;
case 1:
ing.itemIDs["dexReq"] = powderIng["skpReq"];
break;
case 2:
ing.itemIDs["intReq"] = powderIng["skpReq"];
break;
case 3:
ing.itemIDs["defReq"] = powderIng["skpReq"];
break;
case 4:
ing.itemIDs["agiReq"] = powderIng["skpReq"];
break;
}
ing.consumableIDs = {"dura": 0, "charges": 0};
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
ingMap.set(ing["displayName"],ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
}
}
for (const ing of ings) {
ingMap.set(ing["displayName"], ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
}
for (const recipe of recipes) {
recipeMap.set(recipe["name"], recipe);
recipeList.push(recipe["name"]);
recipeIDMap.set(recipe["id"],recipe["name"]);
}
}

16
map-narrow.css Normal file
View file

@ -0,0 +1,16 @@
.mapdiv{
height: 100vh;
background: #121516;
}
.container{
width: 95%;
border: 3px solid #BCBCBC;
border-radius: 3px;
padding: 2% 4% 4%;
}
.coord-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: 5px;
grid-auto-rows: minmax(60px, auto);
}

22
map-wide.css Normal file
View file

@ -0,0 +1,22 @@
.mapdiv{
height: 100vh;
background: #121516;
}
.overall-container {
display: grid;
grid-template-columns: 80% 20%;
grid-column-gap: 5px;
grid-auto-rows: minmax(60px, auto);
}
.coord-container {
display: grid;
grid-template-columns: 30% 30% 30%;
grid-column-gap: 5px;
grid-auto-rows: minmax(60px, auto);
}
.container{
width: 95%;
border: 3px solid #BCBCBC;
border-radius: 3px;
}

99
map.html Normal file
View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<meta name="HandheldFriendly" content="true" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
<!-- nunito font, copying wynndata -->
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" media="screen and (min-width: 1100px)" href="map-wide.css"/>
<link rel="stylesheet" media="screen and (max-width: 1099px)" href="map-narrow.css"/>
<link rel="icon" href="./favicon.png">
<link rel="manifest" href="manifest.json">
<!--Leaflet for map-->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin="anonymous"/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin="anonymous"></script>
<title>WynnGPS</title>
</head>
<body class="all">
<div class="center">
<header class = "header nomarginp">
<div class = "headerleft">
<a href = "./" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/builder.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnBuilder</div>
</a>
<a href = "./crafter.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/crafter.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnCrafter</div>
</a>
<a href = "./items.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/searcher.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnAtlas</div>
</a>
<a href = "./customizer.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/custom.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnCustom</div>
</a>
<a href = "./map.html" class = "nomarginp iconlink tooltip">
<img src = "/media/icons/compass.png" class = "left linkoptions headericon">
</img>
<div class = "tooltiptext center">WynnGPS</div>
</a>
</div>
<div class = "headercenter">
<div>
<p class = "itemp" id = "header">WynnGPS</p>
</div>
</div>
<div class = "headerright">
</div>
</header>
</div>
<br>
<div class = "overall-container" display = "grid">
<div id = "mapdiv" class ="mapdiv container" display = "grid-item-1">
</div>
<div id = "mapoptions-container" class = "container" display = "grid-item-2">
<div id = "coord-container" class = "center coord-container" display = "grid">
<div></div>
<p class = "title center">Options</p>
<div></div>
<div display = "grid-item-1">X</div>
<div display = "grid-item-2"></div>
<div display = "grid-item-3">Z</div>
<div id = "coord-x" display = "grid-item-4"></div>
<div id = "coord-img" display = "grid-item-5">
<img src = "/media/icons/compass2.png" alt style = "max-width:32px; max-height:32px"/>
</div>
<div id = "coord-z" display = "grid-item-6"></div>
<div id = "marker-coord-x" display = "none"></div>
<div id = "marker-coord-img" display = "none">
<img src = "/media/icons/marker.png" alt style = "max-width:32px; max-height:32px"/>
</div>
<div id = "marker-coord-z" display = "none"></div>
</div>
</div>
</div>
<script type = "text/javascript" src="map.js"></script>
</body>
</html>

137
map.js Normal file
View file

@ -0,0 +1,137 @@
/*
* TESTING SECTION
*/
const map_url_base = location.href.split("#")[0];
const map_url_tag = location.hash.slice(1);
console.log(map_url_base);
console.log(map_url_tag);
const MAP_BUILD_VERSION = "6.9.42.0";
function setTitle() {
let text = "WynnGPS version "+MAP_BUILD_VERSION;
document.getElementById("header").classList.add("funnynumber");
document.getElementById("header").textContent = text;
}
setTitle();
/*
* END testing section
*/
var map;
var image;
let terrs = [];
let claims = [];
var marker;
let markers = [];
//latitude, longitude is y, x!!!!
const bounds = [[0,0], [6484, 4090]];
/** Thanks to kristofbolyai's github page for showing me how to do all of this.
*
*/
function init(){
map_elem = document.getElementById("mapdiv");
let coordx_elem = document.getElementById("coord-x");
let coordz_elem = document.getElementById("coord-z");
map = L.map("mapdiv", {
crs: L.CRS.Simple,
minZoom: -4,
maxZoom: 2,
zoomControl: false,
zoom: 1
}).setView([0,0], 1);
L.imageOverlay("/media/maps/world-map.png", bounds).addTo(map);
map.fitBounds(bounds);
L.control.zoom({
position: 'topleft'
}).addTo(map);
if (map_url_tag) {
let coords = map_url_tag.split(",");
let x = parseFloat(coords[0]);
let y = parseFloat(coords[1]);
if (parseFloat(coords[0]) && parseFloat(coords[1])) {
placeMarker(xytolatlng(x,y)[0], xytolatlng(x,y)[1]);
}
}
map.addEventListener('mousemove', function(ev) {
lat = Math.round(ev.latlng.lat);
lng = Math.round(ev.latlng.lng);
let coords = latlngtoxy(lat,lng);
coordx_elem.textContent = coords[0];
coordz_elem.textContent = coords[1];
});
map.on('contextmenu', function(ev) {
if (ev.originalEvent.which == 3) {
lat = Math.round(ev.latlng.lat);
lng = Math.round(ev.latlng.lng);
console.log([lat,lng]);
placeMarker(lat, lng);
}
});
map_elem.style.background = "#121516";
}
/** Places the marker at x, y.
*
* @param {Number} lng - longitude
* @param {Number} lat - latitude
*/
function placeMarker(lat, lng) {
let coords = latlngtoxy(lat,lng);
if (marker) {
map.removeLayer(marker);
}
marker = L.marker([lat, lng], {icon: L.icon({
iconUrl: '/media/icons/marker.png',
iconSize: [32, 32],
iconAnchor: [16, 32],
shadowUrl: '/media/icons/shadow.png',
shadowSize: [1,1],
shadowAnchor: [16, 32],
})});
let mcdx = document.getElementById("marker-coord-x");
mcdx.textContent = coords[0];
mcdx.style.display = "grid-item-7";
let mcdi = document.getElementById("marker-coord-img");
mcdx.style.display = "grid-item-8";
let mcdz = document.getElementById("marker-coord-z");
mcdz.textContent = coords[1];
mcdx.style.display = "grid-item-9";
location.hash = coords[0] + "," + coords[1]
marker.addTo(map);
}
//Wynn coordinates: down right = ++
//Leaflet coordinates: up right = ++
//may not be 100% accurate, but "close enough."
function xytolatlng(x, y) {
return [-y-123, x+2392]; //lat, lng
}
function latlngtoxy(lat, lng) {
return [lng-2392, -lat-123]; //x, y
}
init();

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 934 B

After

Width:  |  Height:  |  Size: 12 KiB

BIN
media/icons/compass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/compass2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 B

After

Width:  |  Height:  |  Size: 14 KiB

BIN
media/icons/cursor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 923 B

After

Width:  |  Height:  |  Size: 12 KiB

BIN
media/icons/marker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
media/icons/marker2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 924 B

After

Width:  |  Height:  |  Size: 6 KiB

BIN
media/icons/shadow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 B

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 484 B

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 682 B

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 13 KiB

BIN
media/maps/world-map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 MiB

View file

@ -59,5 +59,5 @@ let powderSpecialStats = [
_ps("Chain Lightning",new Map([ ["Chains", [5,6,7,8,9]], ["Damage", [200,225,250,275,300]] ]),"Kill Streak",new Map([ ["Damage", [3,4.5,6,7.5,9]],["Duration", [5,5,5,5,5]],["Description", "Mob Killed"] ]),200), //t
_ps("Curse",new Map([ ["Duration", [7,7.5,8,8.5,9]],["Damage Boost", [90,120,150,180,210]] ]),"Concentration",new Map([ ["Damage", [1,2,3,4,5]],["Duration",[1,1,1,1,1]],["Description", "Mana Used"] ]),150), //w
_ps("Courage",new Map([ ["Duration", [6,6.5,7,7.5,8]],["Damage", [75,87.5,100,112.5,125]],["Damage Boost", [70,90,110,130,150]] ]),"Endurance",new Map([ ["Damage", [2,3,4,5,6]],["Duration", [8,8,8,8,8]],["Description", "Hit Taken"] ]),200), //f
_ps("Air Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [400,450,500,550,600]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a
_ps("Wind Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [400,450,500,550,600]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a
];

View file

@ -140,9 +140,9 @@ function calculate_skillpoints(equipment, weapon) {
// Crafted skillpoint does not count initially.
for (const item of crafted) {
console.log(item)
//console.log(item)
result = apply_to_fit(skillpoints, item, allFalse.slice(), activeSetCounts);
console.log(result)
//console.log(result)
needed_skillpoints = result[0];
total_diff = result[1];

View file

@ -6,6 +6,16 @@
justify-content: center;
}
.title{
text-align: center;
font-size: 150%;
}
.smalltitle{
text-align: center;
font-size: 125%;
margin-top: 10px;
margin-bottom: 4px;
}
.error {
color: red;
top: 30px;
@ -24,17 +34,17 @@
}
.headerleft {
display: inline-block;
width: 20%;
width: 30%;
text-align: left;
}
.headercenter {
display: inline-block;
width: 60%;
width: 40%;
text-align: center;
}
.headerright{
display: inline-block;
width: 20%;
width: 30%;
text-align: right;
}
.iconlink {
@ -342,6 +352,12 @@ input {
color: #aaa;
}
/* Tier colors tier colors */
.none {
color: #aaa;
}
.lore {
color: #555;
}
.Normal{
color: #fff;
}
@ -450,7 +466,6 @@ button.toggleOn:hover {
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
color: #aaa;
background: #110110;
width: min(200%, 75vw);
@ -460,11 +475,13 @@ button.toggleOn:hover {
padding: 0 0;
position: absolute;
z-index: 1;
}
.center-parent {
}
.recipe {
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}

View file

@ -60,8 +60,8 @@ translate_mappings = {
"sockets": "slots",
#"type": "type",
#"armorType": "armorType", (deleted)
#"armorColor": "color", (deleted)
#"addedLore": "lore", (deleted)
"armorColor": "color", #(deleted)
"addedLore": "lore", #(deleted)
#"material": "material", (deleted)
"dropType": "drop",
#"quest": "quest",
@ -146,8 +146,8 @@ translate_mappings = {
}
delete_keys = [
"addedLore",
"skin",
#"addedLore",
#"skin",
#"armorType",
#"armorColor",
#"material"

View file

@ -11,7 +11,9 @@ let accessoryTypes = [ "ring", "bracelet", "necklace" ];
let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
let consumableTypes = [ "potion", "scroll", "food"];
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
let classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"];
const classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"];
const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"] //I'm not sure why you would make a custom crafted but if you do you should be able to use it w/ the correct powder formula
const types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
let skpReqs = skp_order.map(x => x + "Req");