wynnbuilder-forked-for-changes/utils.js

375 lines
14 KiB
JavaScript
Raw Normal View History

let getUrl = window.location;
const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
2021-01-18 20:23:37 +00:00
let skp_order = ["str","dex","int","def","agi"];
let skill = ["Strength", "Dexterity", "Intelligence", "Defense", "Agility"];
let skp_elements = ["e","t","w","f","a"];
2021-01-28 00:52:34 +00:00
let damageClasses = ["Neutral","Earth","Thunder","Water","Fire","Air"];
// Set up item lists for quick access later.
let armorTypes = [ "helmet", "chestplate", "leggings", "boots" ];
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"];
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
//0.51, 0.82, 1.50, 2.05, 2.50, 3.11, 4.27
const classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"];
const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"] //I'm not sure why you would make a custom crafted but if you do you should be able to use it w/ the correct powder formula
const types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
2021-03-16 05:40:05 +00:00
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes);
2021-01-18 20:23:37 +00:00
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
let skpReqs = skp_order.map(x => x + "Req");
2021-04-21 05:28:16 +00:00
//File reading for ID translations for JSON purposes
2021-04-30 00:03:50 +00:00
let reversetranslations = new Map();
let translations = new Map([["name", "name"], ["displayName", "displayName"], ["tier", "tier"], ["set", "set"], ["sockets", "slots"], ["type", "type"], ["dropType", "drop"], ["quest", "quest"], ["restrictions", "restrict"], ["damage", "nDam"], ["fireDamage", "fDam"], ["waterDamage", "wDam"], ["airDamage", "aDam"], ["thunderDamage", "tDam"], ["earthDamage", "eDam"], ["attackSpeed", "atkSpd"], ["health", "hp"], ["fireDefense", "fDef"], ["waterDefense", "wDef"], ["airDefense", "aDef"], ["thunderDefense", "tDef"], ["earthDefense", "eDef"], ["level", "lvl"], ["classRequirement", "classReq"], ["strength", "strReq"], ["dexterity", "dexReq"], ["intelligence", "intReq"], ["agility", "agiReq"], ["defense", "defReq"], ["healthRegen", "hprPct"], ["manaRegen", "mr"], ["spellDamage", "sdPct"], ["damageBonus", "mdPct"], ["lifeSteal", "ls"], ["manaSteal", "ms"], ["xpBonus", "xpb"], ["lootBonus", "lb"], ["reflection", "ref"], ["strengthPoints", "str"], ["dexterityPoints", "dex"], ["intelligencePoints", "int"], ["agilityPoints", "agi"], ["defensePoints", "def"], ["thorns", "thorns"], ["exploding", "expd"], ["speed", "spd"], ["attackSpeedBonus", "atkTier"], ["poison", "poison"], ["healthBonus", "hpBonus"], ["soulPoints", "spRegen"], ["emeraldStealing", "eSteal"], ["healthRegenRaw", "hprRaw"], ["spellDamageRaw", "sdRaw"], ["damageBonusRaw", "mdRaw"], ["bonusFireDamage", "fDamPct"], ["bonusWaterDamage", "wDamPct"], ["bonusAirDamage", "aDamPct"], ["bonusThunderDamage", "tDamPct"], ["bonusEarthDamage", "eDamPct"], ["bonusFireDefense", "fDefPct"], ["bonusWaterDefense", "wDefPct"], ["bonusAirDefense", "aDefPct"], ["bonusThunderDefense", "tDefPct"], ["bonusEarthDefense", "eDefPct"], ["type", "type"], ["identified", "fixID"], ["skin", "skin"], ["category", "category"], ["spellCostPct1", "spPct1"], ["spellCostRaw1", "spRaw1"], ["spellCostPct2", "spPct2"], ["spellCostRaw2", "spRaw2"], ["spellCostPct3", "spPct3"], ["spellCostRaw3", "spRaw3"], ["spellCostPct4", "spPct4"], ["spellCostRaw4", "spRaw4"], ["rainbowSpellDamageRaw", "rainbowRaw"], ["sprint", "sprint"], ["sprintRegen", "sprintReg"], ["jumpHeight", "jh"], ["lootQuality", "lq"], ["gatherXpBonus", "gXp"], ["gatherSpeed", "gSpd"]]);
2021-04-30 00:03:50 +00:00
for (const [k, v] of translations) {
reversetranslations.set(v, k);
2021-04-21 05:28:16 +00:00
}
2021-04-30 00:03:50 +00:00
2021-04-21 05:28:16 +00:00
console.log(translations);
2021-02-12 17:00:06 +00:00
function clamp(num, low, high){
return Math.min(Math.max(num, low), high);
}
2021-01-07 06:41:41 +00:00
// Permutations in js reference (also cool algorithm):
// https://stackoverflow.com/a/41068709
function perm(a){
if (a.length == 0) return [[]];
var r = [[a[0]]],
t = [],
s = [];
if (a.length == 1) return r;
for (var i = 1, la = a.length; i < la; i++){
for (var j = 0, lr = r.length; j < lr; j++){
r[j].push(a[i]);
t.push(r[j]);
for(var k = 1, lrj = r[j].length; k < lrj; k++){
for (var l = 0; l < lrj; l++) s[l] = r[j][(k+l)%lrj];
t[t.length] = s;
s = [];
}
}
r = t;
t = [];
}
return r;
}
2021-02-17 18:08:47 +00:00
function round_near(value) {
let eps = 0.00000001;
if (Math.abs(value - Math.round(value)) < eps) {
return Math.round(value);
}
return value;
}
function setText(id, text) {
document.getElementById(id).textContent = text;
}
2021-01-07 06:41:41 +00:00
function setHTML(id, html) {
document.getElementById(id).innerHTML = html;
}
function setValue(id, value) {
2021-01-07 10:23:54 +00:00
let el = document.getElementById(id);
el.value = value;
el.dispatchEvent(new Event("change"));
2021-01-07 06:41:41 +00:00
}
2021-01-07 08:34:31 +00:00
2021-01-08 20:17:37 +00:00
function getValue(id) {
return document.getElementById(id).value;
}
function log(b, n) {
return Math.log(n) / Math.log(b);
}
2021-01-07 08:34:31 +00:00
// Base 64 encoding tools
// https://stackoverflow.com/a/27696695
2021-01-07 10:23:54 +00:00
// Modified for fixed precision
2021-01-07 08:34:31 +00:00
Base64 = (function () {
var digitsStr =
// 0 8 16 24 32 40 48 56 63
// v v v v v v v v v
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
var digits = digitsStr.split('');
var digitsMap = {};
for (var i = 0; i < digits.length; i++) {
digitsMap[digits[i]] = i;
}
return {
2021-01-07 10:23:54 +00:00
fromIntV: function(int32) {
2021-01-07 08:34:31 +00:00
var result = '';
while (true) {
result = digits[int32 & 0x3f] + result;
int32 >>>= 6;
if (int32 === 0)
break;
}
return result;
},
2021-01-07 10:23:54 +00:00
fromIntN: function(int32, n) {
var result = '';
for (let i = 0; i < n; ++i) {
result = digits[int32 & 0x3f] + result;
2021-01-09 09:09:47 +00:00
int32 >>= 6;
2021-01-07 10:23:54 +00:00
}
return result;
},
2021-01-07 08:34:31 +00:00
toInt: function(digitsStr) {
var result = 0;
var digits = digitsStr.split('');
for (var i = 0; i < digits.length; i++) {
result = (result << 6) + digitsMap[digits[i]];
}
return result;
2021-01-07 10:23:54 +00:00
},
2021-01-09 09:09:47 +00:00
toIntSigned: function(digitsStr) {
var result = 0;
var digits = digitsStr.split('');
if (digits[0] && (digitsMap[digits[0]] & 0x20)) {
result = -1;
}
for (var i = 0; i < digits.length; i++) {
result = (result << 6) + digitsMap[digits[i]];
}
return result;
}
2021-01-07 08:34:31 +00:00
};
})();
// Base64.fromInt(-2147483648); // gives "200000"
// Base64.toInt("200000"); // gives -2147483648
2021-01-10 10:02:23 +00:00
/*
2021-01-15 00:30:49 +00:00
Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative.
2021-01-10 10:02:23 +00:00
Pct would be 0.80 for 80%, -1.20 for 120%, etc
Example Outputs:
raw: -100
pct: +0.20, output = -80
pct: +1.20, output = 0
pct: -0.20, output = -120
pct: -1.20, output = -220
raw: +100
pct: +0.20, output = 120
pct: +1.20, output = 220
pct: -0.20, output = 80
2021-01-15 00:30:49 +00:00
pct: -1.20, output = -20
2021-01-10 10:02:23 +00:00
*/
function rawToPct(raw, pct){
final = 0;
if (raw < 0){
final = (Math.min(0, raw - (raw * pct) ));
}else if(raw > 0){
final = raw + (raw * pct);
2021-01-10 10:02:23 +00:00
}else{ //do nothing - final's already 0
}
return final;
}
/*
* Clipboard utilities
* From: https://stackoverflow.com/a/30810322
*/
function fallbackCopyTextToClipboard(text) {
var textArea = document.createElement("textarea");
//
// *** This styling is an extra step which is likely not required. ***
//
// Why is it here? To ensure:
// 1. the element is able to have focus and selection.
// 2. if the element was to flash render it has minimal visual impact.
// 3. less flakyness with selection and copying which **might** occur if
// the textarea element is not visible.
//
// The likelihood is the element won't even render, not even a
// flash, so some of these are just precautions. However in
// Internet Explorer the element is visible whilst the popup
// box asking the user for permission for the web page to
// copy to the clipboard.
//
// Place in the top-left corner of screen regardless of scroll position.
textArea.style.position = 'fixed';
textArea.style.top = 0;
textArea.style.left = 0;
// Ensure it has a small width and height. Setting to 1px / 1em
// doesn't work as this gives a negative w/h on some browsers.
textArea.style.width = '2em';
textArea.style.height = '2em';
// We don't need padding, reducing the size if it does flash render.
textArea.style.padding = 0;
// Clean up any borders.
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
// Avoid flash of the white box if rendered for any reason.
textArea.style.background = 'transparent';
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copying text command was ' + msg);
} catch (err) {
console.log('Oops, unable to copy');
}
document.body.removeChild(textArea);
}
function copyTextToClipboard(text) {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
navigator.clipboard.writeText(text).then(function() {
console.log('Async: Copying to clipboard was successful!');
}, function(err) {
console.error('Async: Could not copy text: ', err);
});
}
2021-02-17 18:29:41 +00:00
/**
* Generates a random color using the #(R)(G)(B) format.
2021-03-16 05:40:05 +00:00
*/
function randomColor() {
return '#' + Math.round(Math.random() * 0xFFFFFF).toString(16);
2021-03-16 05:40:05 +00:00
}
/**
* Generates a random color, but lightning must be relatively high (>0.5).
2021-03-16 05:40:05 +00:00
*
* @returns a random color in RGB 6-bit form.
*/
function randomColorLight() {
return randomColorHSL([0,1],[0,1],[0.5,1]);
}
/** Generates a random color given HSL restrictions.
*
* @returns a random color in RGB 6-bit form.
*/
function randomColorHSL(h,s,l) {
var letters = '0123456789abcdef';
let h_var = h[0] + (h[1]-h[0])*Math.random(); //hue
let s_var = s[0] + (s[1]-s[0])*Math.random(); //saturation
let l_var = l[0] + (l[1]-l[0])*Math.random(); //lightness
let rgb = hslToRgb(h_var,s_var,l_var);
let color = "#";
for (const c of rgb) {
color += letters[Math.floor(c/16)] + letters[c%16];
}
return color;
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255]. Not written by wynnbuilder devs.
*
* @param {number} h The hue
* @param {number} s The saturation
* @param {number} l The lightness
* @return {Array} The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
var hue2rgb = function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
/** Creates a tooltip.
*
* @param {DOM Element} elem - the element to make a tooltip
* @param {String} element_type - the HTML element type that the tooltiptext should be.
* @param {String} tooltiptext - the text to display in the tooltip.
* @param {DOM Element} parent - the parent elem. optional.
* @param {String[]} classList - a list of classes to add to the element.
*/
function createTooltip(elem, element_type, tooltiptext, parent, classList) {
elem = document.createElement(element_type);
elem.classList.add("tooltiptext");
if (tooltiptext.includes("\n")) {
let texts = tooltiptext.split("\n");
for (const t of texts) {
let child = document.createElement(element_type);
child.textContent = t;
elem.appendChild(child);
}
} else {
elem.textContent = tooltiptext;
}
for (const c of classList) {
elem.classList.add(c);
}
if (parent) {
parent.classList.add("tooltip");
parent.appendChild(elem);
}
return elem;
}
/** A generic function that toggles the on and off state of a button.
*
* @param {String} button_id - the id name of the button.
*/
function toggleButton(button_id) {
let elem = document.getElementById(button_id);
if (elem.tagName === "BUTTON") {
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
elem.classList.remove("toggleOn");
} else {
elem.classList.add("toggleOn");
}
}
2021-04-30 00:03:50 +00:00
}
/** A utility function that reloads the page forcefully.
*
*/
async function hardReload() {
//https://gist.github.com/rmehner/b9a41d9f659c9b1c3340
const dbs = await window.indexedDB.databases();
await dbs.forEach(db => { window.indexedDB.deleteDatabase(db.name) });
location.reload();
}