Merge pull request #8 from phantamanta44/atlas-dual-filter
Segregated-filter expression-based item search
This commit is contained in:
commit
7047212e31
22 changed files with 262399 additions and 623 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
|||
*.swp
|
||||
*.bat
|
||||
sets/
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
|
|
6
build.js
6
build.js
|
@ -133,9 +133,9 @@ class Build{
|
|||
this.powders[2] = this.powders[2].slice(0,leggings.slots);
|
||||
this.leggings = expandItem(leggings, this.powders[2]);
|
||||
}else{
|
||||
const chestplate = itemMap.get("No Leggings");
|
||||
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
|
||||
this.chestplate = expandItem(chestplate, this.powders[1]);
|
||||
const leggings = itemMap.get("No Leggings");
|
||||
this.powders[2] = this.powders[2].slice(0,leggings.slots);
|
||||
this.leggings = expandItem(leggings, this.powders[2]);
|
||||
errors.push(new ItemNotFound(equipment[2], "leggings", true));
|
||||
}
|
||||
if(itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") {
|
||||
|
|
84
builder.js
84
builder.js
|
@ -3,11 +3,18 @@ const url_tag = location.hash.slice(1);
|
|||
console.log(url_base);
|
||||
console.log(url_tag);
|
||||
|
||||
const BUILD_VERSION = "6.9.7";
|
||||
const BUILD_VERSION = "6.9.9";
|
||||
|
||||
function setTitle() {
|
||||
document.getElementById("header").textContent = "WynnBuilder version "+BUILD_VERSION+" (db version "+DB_VERSION+")";
|
||||
let text;
|
||||
if (url_base.includes("hppeng-wynn")) {
|
||||
text = "WynnBuilder UNSTABLE version "+BUILD_VERSION+" (db version "+DB_VERSION+")";
|
||||
}
|
||||
else {
|
||||
text = "WynnBuilder version "+BUILD_VERSION+" (db version "+DB_VERSION+")";
|
||||
document.getElementById("header").classList.add("funnynumber");
|
||||
}
|
||||
document.getElementById("header").textContent = text;
|
||||
}
|
||||
|
||||
setTitle();
|
||||
|
@ -71,18 +78,6 @@ let equipment_names = [
|
|||
let equipmentInputs = equipment_fields.map(x => x + "-choice");
|
||||
let buildFields = equipment_fields.map(x => "build-"+x);
|
||||
|
||||
let powderIDs = new Map();
|
||||
let powderNames = new Map();
|
||||
let _powderID = 0;
|
||||
for (const x of skp_elements) {
|
||||
for (let i = 1; i <= 6; ++i) {
|
||||
// Support both upper and lowercase, I guess.
|
||||
powderIDs.set(x.toUpperCase()+i, _powderID);
|
||||
powderIDs.set(x+i, _powderID);
|
||||
powderNames.set(_powderID, x+i);
|
||||
_powderID++;
|
||||
}
|
||||
}
|
||||
let powderInputs = [
|
||||
"helmet-powder",
|
||||
"chestplate-powder",
|
||||
|
@ -90,45 +85,6 @@ let powderInputs = [
|
|||
"boots-powder",
|
||||
"weapon-powder",
|
||||
];
|
||||
// Ordering: [dmgMin, dmgMax, convert, defPlus, defMinus (+6 mod 5)]
|
||||
class Powder {
|
||||
constructor(min, max, convert, defPlus, defMinus) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.convert = convert;
|
||||
this.defPlus = defPlus;
|
||||
this.defMinus = defMinus;
|
||||
}
|
||||
}
|
||||
function _p(a,b,c,d,e) { return new Powder(a,b,c,d,e); } //bruh moment
|
||||
|
||||
let powderStats = [
|
||||
_p(3,6,17,2,1), _p(6,9,21,4,2), _p(8,14,25,8,3), _p(11,16,31,14,5), _p(15,18,38,22,9), _p(18,22,46,30,13),
|
||||
_p(1,8,9,3,1), _p(1,13,11,5,1), _p(2,18,14,9,2), _p(3,24,17,14,4), _p(3,32,22,20,7), _p(5,40,28,28,10),
|
||||
_p(3,4,13,3,1), _p(4,7,15,6,1), _p(6,10,17,11,2), _p(8,12,21,18,4), _p(11,14,26,28,7), _p(13,17,32,40,10),
|
||||
_p(2,5,14,3,1), _p(4,8,16,5,2), _p(6,10,19,9,3), _p(9,13,24,16,5), _p(12,16,30,25,9), _p(15,19,37,36,13),
|
||||
_p(2,6,11,3,1), _p(4,9,14,6,2), _p(7,10,17,10,3), _p(9,13,22,16,5), _p(13,18,28,24,9), _p(16,18,35,34,13)
|
||||
];
|
||||
|
||||
//Ordering: [weapon special name, weapon special effects, armor special name, armor special effects]
|
||||
class PowderSpecial{
|
||||
constructor(wSpName, wSpEff, aSpName, aSpEff, cap){
|
||||
this.weaponSpecialName = wSpName;
|
||||
this.weaponSpecialEffects = wSpEff;
|
||||
this.armorSpecialName = aSpName;
|
||||
this.armorSpecialEffects = aSpEff;
|
||||
this.cap = cap;
|
||||
}
|
||||
}
|
||||
function _ps(a,b,c,d,e) { return new PowderSpecial(a,b,c,d,e); } //bruh moment
|
||||
|
||||
let powderSpecialStats = [
|
||||
_ps("Quake",new Map([["Radius",[5,5.5,6,6.5,7]], ["Damage",[155,220,285,350,415]] ]),"Rage",new Map([ ["Damage", [0.3,0.4,0.5,0.7,1.0]],["Description", "% " + "\u2764" + " Missing"] ]),400), //e
|
||||
_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
|
||||
];
|
||||
|
||||
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes);
|
||||
let itemLists = new Map();
|
||||
|
@ -138,6 +94,7 @@ for (const it of itemTypes) {
|
|||
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.
|
||||
|
@ -194,6 +151,7 @@ function init() {
|
|||
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)) {
|
||||
|
@ -203,6 +161,10 @@ function init() {
|
|||
idMap.set(item.id, item.displayName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
redirectMap.set(item.id, item.remapID);
|
||||
}
|
||||
}
|
||||
|
||||
for (const armorType of armorTypes) {
|
||||
populateItemList(armorType);
|
||||
|
@ -255,6 +217,13 @@ function init() {
|
|||
decodeBuild(url_tag);
|
||||
}
|
||||
|
||||
function getItemNameFromID(id) {
|
||||
if (redirectMap.has(id)) {
|
||||
return getItemNameFromID(redirectMap.get(id));
|
||||
}
|
||||
return idMap.get(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate fields based on url, and calculate build.
|
||||
*/
|
||||
|
@ -270,7 +239,7 @@ function decodeBuild(url_tag) {
|
|||
if (version === "0" || version === "1" || version === "2" || version === "3") {
|
||||
let equipments = info[1];
|
||||
for (let i = 0; i < 9; ++i ) {
|
||||
equipment[i] = idMap.get(Base64.toInt(equipments.slice(i*3,i*3+3)));
|
||||
equipment[i] = getItemNameFromID(Base64.toInt(equipments.slice(i*3,i*3+3)));
|
||||
}
|
||||
}
|
||||
if (version === "1") {
|
||||
|
@ -767,7 +736,8 @@ function calculateBuildStats() {
|
|||
//skpRow.classList.add("left");
|
||||
let td = document.createElement("p");
|
||||
//td.classList.add("left");
|
||||
let skpSummary = document.createElement("b");
|
||||
|
||||
/* let skpSummary = document.createElement("b");
|
||||
skpSummary.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Total: (";
|
||||
//skpSummary.classList.add("itemp");
|
||||
td.appendChild(skpSummary);
|
||||
|
@ -786,12 +756,12 @@ function calculateBuildStats() {
|
|||
let skpEnd = document.createElement("b");
|
||||
skpEnd.textContent = ")";
|
||||
td.appendChild(skpEnd);
|
||||
skpRow.append(td);
|
||||
skpRow.append(td); */
|
||||
|
||||
let remainingSkp = document.createElement("p");
|
||||
remainingSkp.classList.add("center");
|
||||
let remainingSkpTitle = document.createElement("b");
|
||||
remainingSkpTitle.textContent = "Remaining skillpoints: ";
|
||||
remainingSkpTitle.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Remaining skillpoints: ";
|
||||
let remainingSkpContent = document.createElement("b");
|
||||
//remainingSkpContent.textContent = "" + (levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints < 0 ? "< 0" : levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints);
|
||||
remainingSkpContent.textContent = "" + (levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints);
|
||||
|
|
198
clean.json
198
clean.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"items": [
|
||||
{
|
||||
"name": "Demon Tide (1.20)",
|
||||
"name": "Demon Tide",
|
||||
"tier": "Legendary",
|
||||
"type": "leggings",
|
||||
"set": null,
|
||||
|
@ -35506,6 +35506,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Cancer\u058e",
|
||||
"displayName": "Cancer",
|
||||
"tier": "Legendary",
|
||||
"type": "helmet",
|
||||
"set": null,
|
||||
|
@ -265939,8 +265940,8 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "boots",
|
||||
"name": "Ensa\u2019s Ideals (1.20)",
|
||||
"displayName": "Ensa\u2019s Ideals (1.20)",
|
||||
"name": "Ensa's Ideals (1.20)",
|
||||
"displayName": "Ensa's Ideals (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -266378,8 +266379,8 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "ring",
|
||||
"name": "Tisaun\u2019s Honor (1.20)",
|
||||
"displayName": "Tisaun\u2019s Honor (1.20)",
|
||||
"name": "Tisaun's Honor (1.20)",
|
||||
"displayName": "Tisaun's Honor (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -266718,8 +266719,8 @@
|
|||
{
|
||||
"tier": "Unique",
|
||||
"type": "chestplate",
|
||||
"name": "Nether\u2019s Reach (1.20)",
|
||||
"displayName": "Nether\u2019s Reach (1.20)",
|
||||
"name": "Nether's Reach (1.20)",
|
||||
"displayName": "Nether's Reach (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -266792,8 +266793,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "dagger",
|
||||
"name": "Bob\u2019s Mythic Daggers (1.20)",
|
||||
"displayName": "Bob\u2019s Mythic Daggers (1.20)",
|
||||
"name": "Bob's Mythic Daggers (1.20)",
|
||||
"displayName": "Bob's Mythic Daggers (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -267206,8 +267207,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "spear",
|
||||
"name": "Bob\u2019s Mythic Spear (1.20)",
|
||||
"displayName": "Bob\u2019s Mythic Spear (1.20)",
|
||||
"name": "Bob's Mythic Spear (1.20)",
|
||||
"displayName": "Bob's Mythic Spear (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -267445,8 +267446,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "chestplate",
|
||||
"name": "Terra\u2019s Mold (1.20)",
|
||||
"displayName": "Terra\u2019s Mold (1.20)",
|
||||
"name": "Terra's Mold (1.20)",
|
||||
"displayName": "Terra's Mold (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -267900,8 +267901,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "ring",
|
||||
"name": "Old Keeper\u2019s Ring (1.20)",
|
||||
"displayName": "Old Keeper\u2019s Ring (1.20)",
|
||||
"name": "Old Keeper's Ring (1.20)",
|
||||
"displayName": "Old Keeper's Ring (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -268068,7 +268069,7 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "leggings",
|
||||
"name": "Paradox (1.20)",
|
||||
"name": "One For All",
|
||||
"displayName": "Paradox (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
|
@ -268272,7 +268273,7 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "boots",
|
||||
"name": "Dragon Dance (1.20)",
|
||||
"name": "Paradox",
|
||||
"displayName": "Dragon Dance (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
|
@ -268560,8 +268561,8 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "leggings",
|
||||
"name": "Lion\u2019s Pelt (1.20)",
|
||||
"displayName": "Lion\u2019s Pelt (1.20)",
|
||||
"name": "Lion's Pelt (1.20)",
|
||||
"displayName": "Lion's Pelt (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -269385,8 +269386,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "chestplate",
|
||||
"name": "Tisaun\u2019s Valor (1.20)",
|
||||
"displayName": "Tisaun\u2019s Valor (1.20)",
|
||||
"name": "Tisaun's Valor (1.20)",
|
||||
"displayName": "Tisaun's Valor (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -269965,8 +269966,8 @@
|
|||
{
|
||||
"tier": "Unique",
|
||||
"type": "helmet",
|
||||
"name": "Sano\u2019s Care (1.20)",
|
||||
"displayName": "Sano\u2019s Care (1.20)",
|
||||
"name": "Sano's Care (1.20)",
|
||||
"displayName": "Sano's Care (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -270772,12 +270773,11 @@
|
|||
{
|
||||
"tier": "Set",
|
||||
"type": "ring",
|
||||
"name": "Black Catalyst Set (1.20)",
|
||||
"displayName": "Black Catalyst Set (1.20)",
|
||||
"name": "Black Catalyst Set",
|
||||
"displayName": "Black Catalyst Set (DEPRECATED!!!)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
"restrict": "1.20 item",
|
||||
"fixID": true,
|
||||
"strReq": 0,
|
||||
"dexReq": 0,
|
||||
|
@ -272002,8 +272002,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "boots",
|
||||
"name": "Nether\u2019s Scar (1.20)",
|
||||
"displayName": "Nether\u2019s Scar (1.20)",
|
||||
"name": "Nether's Scar (1.20)",
|
||||
"displayName": "Nether's Scar (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -272146,8 +272146,8 @@
|
|||
{
|
||||
"tier": "Unique",
|
||||
"type": "leggings",
|
||||
"name": "Bantisu\u2019s Approach (1.20)",
|
||||
"displayName": "Bantisu\u2019s Approach (1.20)",
|
||||
"name": "Bantisu's Approach (1.20)",
|
||||
"displayName": "Bantisu's Approach (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -272406,8 +272406,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "helmet",
|
||||
"name": "Gale\u2019s Sight (1.20)",
|
||||
"displayName": "Gale\u2019s Sight (1.20)",
|
||||
"name": "Gale's Sight (1.20)",
|
||||
"displayName": "Gale's Sight (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -272584,8 +272584,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "bow",
|
||||
"name": "Bob\u2019s Mythic Bow (1.20)",
|
||||
"displayName": "Bob\u2019s Mythic Bow (1.20)",
|
||||
"name": "Bob's Mythic Bow (1.20)",
|
||||
"displayName": "Bob's Mythic Bow (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -272824,8 +272824,8 @@
|
|||
{
|
||||
"tier": "Unique",
|
||||
"type": "chestplate",
|
||||
"name": "Ahm\u2019s Remains (1.20)",
|
||||
"displayName": "Ahm\u2019s Remains (1.20)",
|
||||
"name": "Ahm's Remains (1.20)",
|
||||
"displayName": "Ahm's Remains (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -273249,8 +273249,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "wand",
|
||||
"name": "Bob\u2019s Mythic Wand (1.20)",
|
||||
"displayName": "Bob\u2019s Mythic Wand (1.20)",
|
||||
"name": "Bob's Mythic Wand (1.20)",
|
||||
"displayName": "Bob's Mythic Wand (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -273535,8 +273535,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "boots",
|
||||
"name": "Cerid\u2019s Ingenuity (1.20)",
|
||||
"displayName": "Cerid\u2019s Ingenuity (1.20)",
|
||||
"name": "Cerid's Ingenuity (1.20)",
|
||||
"displayName": "Cerid's Ingenuity (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -273845,8 +273845,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "leggings",
|
||||
"name": "Ohms\u2019 Rage (1.20)",
|
||||
"displayName": "Ohms\u2019 Rage (1.20)",
|
||||
"name": "Ohms' Rage (1.20)",
|
||||
"displayName": "Ohms' Rage (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -274024,8 +274024,8 @@
|
|||
{
|
||||
"tier": "Legendary",
|
||||
"type": "necklace",
|
||||
"name": "Ensa\u2019s Faith (1.20)",
|
||||
"displayName": "Ensa\u2019s Faith (1.20)",
|
||||
"name": "Ensa's Faith (1.20)",
|
||||
"displayName": "Ensa's Faith (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -274798,8 +274798,8 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "leggings",
|
||||
"name": "Rodoroc\u2019s Guard (1.20)",
|
||||
"displayName": "Rodoroc\u2019s Guard (1.20)",
|
||||
"name": "Rodoroc's Guard (1.20)",
|
||||
"displayName": "Rodoroc's Guard (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -275277,7 +275277,7 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "boots",
|
||||
"name": "Weatherwalkers (1.20)",
|
||||
"name": "All For One",
|
||||
"displayName": "Weatherwalkers (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
|
@ -275654,8 +275654,8 @@
|
|||
{
|
||||
"tier": "Rare",
|
||||
"type": "dagger",
|
||||
"name": "Skien\u2019s Paranoia (1.20)",
|
||||
"displayName": "Skien\u2019s Paranoia (1.20)",
|
||||
"name": "Skien's Paranoia (1.20)",
|
||||
"displayName": "Skien's Paranoia (1.20)",
|
||||
"set": null,
|
||||
"quest": null,
|
||||
"classReq": null,
|
||||
|
@ -276257,11 +276257,11 @@
|
|||
"atkSpd": "SLOW",
|
||||
"lvl": 97,
|
||||
"classReq": null,
|
||||
"strReq": 40,
|
||||
"dexReq": 40,
|
||||
"intReq": 40,
|
||||
"agiReq": 40,
|
||||
"defReq": 40,
|
||||
"strReq": 35,
|
||||
"dexReq": 35,
|
||||
"intReq": 35,
|
||||
"agiReq": 35,
|
||||
"defReq": 35,
|
||||
"hprPct": 0,
|
||||
"mr": 0,
|
||||
"sdPct": 0,
|
||||
|
@ -276384,6 +276384,80 @@
|
|||
"gXp": 0,
|
||||
"gSpd": 0,
|
||||
"id": 10406
|
||||
},
|
||||
{
|
||||
"name": "Cursed Jackboots",
|
||||
"tier": "Legendary",
|
||||
"type": "boots",
|
||||
"set": null,
|
||||
"quest": "Lost Soles",
|
||||
"poison": 0,
|
||||
"thorns": 0,
|
||||
"sprint": 0,
|
||||
"category": "armor",
|
||||
"slots": 2,
|
||||
"drop": "never",
|
||||
"hp": 1400,
|
||||
"fDef": 0,
|
||||
"wDef": 50,
|
||||
"aDef": 0,
|
||||
"tDef": 80,
|
||||
"eDef": 0,
|
||||
"lvl": 60,
|
||||
"classReq": null,
|
||||
"strReq": 0,
|
||||
"dexReq": 0,
|
||||
"intReq": 0,
|
||||
"agiReq": 0,
|
||||
"defReq": 0,
|
||||
"hprPct": -20,
|
||||
"mr": 0,
|
||||
"sdPct": 0,
|
||||
"mdPct": 0,
|
||||
"ls": 0,
|
||||
"ms": 2,
|
||||
"xpb": 0,
|
||||
"lb": 0,
|
||||
"ref": 0,
|
||||
"str": 0,
|
||||
"dex": 7,
|
||||
"int": 7,
|
||||
"agi": 0,
|
||||
"def": 0,
|
||||
"expd": 0,
|
||||
"spd": 0,
|
||||
"atkTier": 0,
|
||||
"hpBonus": 0,
|
||||
"spRegen": 0,
|
||||
"eSteal": 0,
|
||||
"hprRaw": 0,
|
||||
"sdRaw": 100,
|
||||
"mdRaw": 0,
|
||||
"fDamPct": 0,
|
||||
"wDamPct": 10,
|
||||
"aDamPct": 0,
|
||||
"tDamPct": 10,
|
||||
"eDamPct": 0,
|
||||
"fDefPct": 0,
|
||||
"wDefPct": 0,
|
||||
"aDefPct": 0,
|
||||
"tDefPct": 0,
|
||||
"eDefPct": -35,
|
||||
"spPct1": 0,
|
||||
"spRaw1": 0,
|
||||
"spPct2": 0,
|
||||
"spRaw2": 0,
|
||||
"spPct3": 0,
|
||||
"spRaw3": 0,
|
||||
"spPct4": 0,
|
||||
"spRaw4": 0,
|
||||
"rainbowRaw": 0,
|
||||
"sprintReg": 0,
|
||||
"jh": 0,
|
||||
"lq": 0,
|
||||
"gXp": 0,
|
||||
"gSpd": 0,
|
||||
"id": 10407
|
||||
}
|
||||
],
|
||||
"sets": {
|
||||
|
@ -276998,13 +277072,17 @@
|
|||
"xpb": -5
|
||||
},
|
||||
{
|
||||
"mr": 1,
|
||||
"sdPct": 10,
|
||||
"xpb": 30,
|
||||
"expd": 10,
|
||||
"hpBonus": 325,
|
||||
"hp": -325,
|
||||
"str": 0,
|
||||
"dex": 0,
|
||||
"int": 0,
|
||||
"def": 0,
|
||||
"agi": 0,
|
||||
"xpb": 25,
|
||||
"spRegen": 10,
|
||||
"sdRaw": 90
|
||||
"sdPct": 8,
|
||||
"spPct1": -12,
|
||||
"spPct3": -12
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
File diff suppressed because one or more lines are too long
55
craft.js
55
craft.js
|
@ -10,12 +10,12 @@ class Craft{
|
|||
@param mat_tiers: [1->3, 1->3]. An array with 2 numbers.
|
||||
@param ingreds: []. An array with 6 entries, each with an ingredient Map.
|
||||
*/
|
||||
constructor(recipe, mat_tiers, ingreds) {
|
||||
constructor(recipe, mat_tiers, ingreds, attackSpeed) {
|
||||
this.recipe = recipe;
|
||||
this.mat_tiers = mat_tiers;
|
||||
this.ingreds = ingreds;
|
||||
this.statMap = new Map(); //can use the statMap as an expanded Item
|
||||
|
||||
this.atkSpd = attackSpeed;
|
||||
this.initCraftStats();
|
||||
}
|
||||
|
||||
|
@ -83,13 +83,60 @@ class Craft{
|
|||
}
|
||||
//statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]);
|
||||
statMap.set("category","weapon");
|
||||
statMap.set("atkSpd",this.atkSpd);
|
||||
}
|
||||
statMap.set("powders","");
|
||||
|
||||
/* Change certain IDs based on material tier.
|
||||
healthOrDamage changes.
|
||||
duration and durability change. (but not basicDuration)
|
||||
|
||||
*/
|
||||
let matmult = 1;
|
||||
let sorted = this.mat_tiers.sort();
|
||||
//TODO - idfk how this works
|
||||
if( sorted[0] == 1 && sorted[1] == 1) {
|
||||
matmult = 1;
|
||||
} else if( sorted[0] == 1 && sorted[1] == 2) {
|
||||
matmult = 1.079;
|
||||
}else if( sorted[0] == 1 && sorted[1] == 3) {
|
||||
matmult = 1.15;
|
||||
}else if( sorted[0] == 2 && sorted[1] == 2) {
|
||||
matmult = 1.24;
|
||||
}else if( sorted[0] == 2 && sorted[1] == 3) {
|
||||
matmult = 1.3;
|
||||
}else if( sorted[0] == 3 && sorted[1] == 3) {
|
||||
matmult = 1.4;
|
||||
}
|
||||
let low = this.recipe.get("healthOrDamage")[0];
|
||||
let high = this.recipe.get("healthOrDamage")[1];
|
||||
if (statMap.get("category") === "consumable") {
|
||||
//duration modifier
|
||||
if(statMap.has("hp")) { //hack
|
||||
statMap.set("hp", Math.floor( low * matmult )+ "-" + Math.floor( high * matmult ));
|
||||
} else {
|
||||
statMap.set("duration", [Math.floor( statMap.get("duration")[0] * matmult ), Math.floor( statMap.get("duration")[1] * matmult )]);
|
||||
}
|
||||
} else {
|
||||
//durability modifier
|
||||
statMap.set("durability", [Math.floor( statMap.get("durability")[0] * matmult ), Math.floor( statMap.get("durability")[1] * matmult )]);
|
||||
}
|
||||
if (statMap.get("category") === "weapon") {
|
||||
//attack damages oh boy
|
||||
let ratio = 2.05; //UNSURE IF THIS IS HOW IT'S DONE. MIGHT BE DONE WITH SKETCHY FLOORING.
|
||||
if (this.atkSpd === "SLOW") {
|
||||
ratio /= 1.5;
|
||||
} else if (this.atkSpd === "NORMAL") {
|
||||
ratio = 1;
|
||||
} else if (this.atkSpd = "FAST") {
|
||||
ratio /= 2.5;
|
||||
}
|
||||
low *= ratio*matmult;
|
||||
high *= ratio*matmult;
|
||||
this.recipe.get("healthOrDamage")[0] = low;
|
||||
this.recipe.get("healthOrDamage")[1] = high;
|
||||
}
|
||||
/* END SECTION */
|
||||
|
||||
//calc ingredient effectivenesses -> see https://wynndata.tk/cr/585765168
|
||||
let eff = [[100,100],[100,100],[100,100]];
|
||||
|
@ -141,10 +188,6 @@ class Craft{
|
|||
}
|
||||
}
|
||||
}
|
||||
//apply material tiers - the good thing is that this should be symmetric.
|
||||
for (const mat of this.mat_tiers) {
|
||||
|
||||
}
|
||||
|
||||
//apply ingredient effectivness - on ids, and reqs (itemIDs). NOT on durability, duration, or charges.
|
||||
let eff_flat = eff.flat();
|
||||
|
|
31
crafter.html
31
crafter.html
|
@ -100,6 +100,26 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<label for = "atkSpdChoices"> Attack Speed (weapons only):</label>
|
||||
<table class = "center" id = "atkSpdChoices">
|
||||
<tr>
|
||||
<td class = "center">
|
||||
<button class = "button" id = "slow-atk-button" onclick = "toggleAtkSpd('slow-atk-button')">
|
||||
Slow
|
||||
</button>
|
||||
</td>
|
||||
<td class = "center">
|
||||
<button class = "button" id = "normal-atk-button" onclick = "toggleAtkSpd('normal-atk-button')">
|
||||
Normal
|
||||
</button>
|
||||
</td>
|
||||
<td class = "center">
|
||||
<button class = "button" id = "fast-atk-button" onclick = "toggleAtkSpd('fast-atk-button')">
|
||||
Fast
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class = "center">
|
||||
<tr>
|
||||
<td class = "center">
|
||||
|
@ -125,12 +145,12 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class = "recipe hide-container-block" style = "display:none">
|
||||
<div class = "recipe center hide-container-block" style = "display:none">
|
||||
<div class = "recipe-stats">
|
||||
<div class = "center" id = "recipe-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "crafted hide-container-block" style = "display:none">
|
||||
<div class = "crafted center hide-container-block" style = "display:none">
|
||||
<div class = "craft-stats">
|
||||
<div class = "center" id = "craft-stats"></div>
|
||||
</div>
|
||||
|
@ -140,10 +160,8 @@
|
|||
<div class = "craft-warnings">
|
||||
<div class = "center" id = "craft-warnings"></div>
|
||||
</div>
|
||||
<p>
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<div class="ingredients-container hide-container-grid" id = "ingreds" style = "display:none">
|
||||
<p class="center title hide-container-block" style = "display:block">
|
||||
Ingredients
|
||||
|
@ -180,6 +198,7 @@
|
|||
</div>
|
||||
|
||||
<script type="text/javascript" src="utils.js"></script>
|
||||
<script type="text/javascript" src="powders.js"></script>
|
||||
<script type="text/javascript" src="build_utils.js"></script>
|
||||
<script type="text/javascript" src="skillpoints.js"></script>
|
||||
<script type="text/javascript" src="damage_calc.js"></script>
|
||||
|
|
25
crafter.js
25
crafter.js
|
@ -86,6 +86,18 @@ function updateMaterials() {
|
|||
document.getElementById("mat-2").textContent = "Material 2 Tier:";
|
||||
}
|
||||
}
|
||||
function toggleAtkSpd(buttonId) {
|
||||
let buttons = ["slow-atk-button", "normal-atk-button", "fast-atk-button"];
|
||||
let elem = document.getElementById(buttonId);
|
||||
if (elem.classList.contains("toggleOn")) {
|
||||
elem.classList.remove("toggleOn");
|
||||
} else {
|
||||
for (const button of buttons) {
|
||||
document.getElementById(button).classList.remove("toggleOn");
|
||||
}
|
||||
elem.classList.add("toggleOn");
|
||||
}
|
||||
}
|
||||
|
||||
function calculateCraft() {
|
||||
//Make things display.
|
||||
|
@ -117,9 +129,15 @@ function calculateCraft() {
|
|||
for (i = 1; i < 7; i++) {
|
||||
getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i))));
|
||||
}
|
||||
|
||||
let atkSpd = "NORMAL"; //default attack speed will be normal.
|
||||
for (const b of ["slow-atk-button", "normal-atk-button", "fast-atk-button"]) {
|
||||
button = document.getElementById(b);
|
||||
if (button.classList.contains("toggleOn")) {
|
||||
atkSpd = b.split("-")[0].toUpperCase();
|
||||
}
|
||||
}
|
||||
//create the craft
|
||||
player_craft = new Craft(recipe,mat_tiers,ingreds);
|
||||
player_craft = new Craft(recipe,mat_tiers,ingreds,atkSpd);
|
||||
console.log(player_craft);
|
||||
/*console.log(recipe)
|
||||
console.log(levelrange)
|
||||
|
@ -130,6 +148,9 @@ function calculateCraft() {
|
|||
|
||||
//Display Recipe Stats
|
||||
displayRecipeStats(player_craft, "recipe-stats");
|
||||
for(let i = 0; i < 6; i++) {
|
||||
displayExpandedIngredient(player_craft["ingreds"][i],"tooltip-" + i);
|
||||
}
|
||||
//Display Craft Stats
|
||||
displayCraftStats(player_craft, "craft-stats");
|
||||
//Display Ingredients' Stats
|
||||
|
|
|
@ -6,13 +6,15 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
|
|||
|
||||
let buildStats = new Map(stats);
|
||||
if(externalStats) { //if nothing is passed in, then this hopefully won't trigger
|
||||
for (const [key,value] of externalStats) {
|
||||
for (let i = 0; i < externalStats.length; i++) {
|
||||
const key = externalStats[i][0];
|
||||
const value = externalStats[i][1];
|
||||
if (typeof value === "number") {
|
||||
buildStats.set(key, buildStats.get(key) + value);
|
||||
} else if (Array.isArray(value)) {
|
||||
arr = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
arr[i] = buildStats.get(key)[i] + value[i];
|
||||
for (let j = 0; j < value.length; j++) {
|
||||
arr[j] = buildStats.get(key)[j] + value[j];
|
||||
}
|
||||
buildStats.set(key, arr);
|
||||
}
|
||||
|
@ -21,8 +23,9 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
|
|||
|
||||
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
|
||||
let damages = [];
|
||||
for (const damage_string of buildStats.get("damageRaw")) {
|
||||
const damage_vals = damage_string.split("-").map(Number);
|
||||
const rawDamages = buildStats.get("damageRaw");
|
||||
for (let i = 0; i < rawDamages.length; i++) {
|
||||
const damage_vals = rawDamages[i].split("-").map(Number);
|
||||
damages.push(damage_vals);
|
||||
}
|
||||
|
||||
|
@ -40,10 +43,11 @@ function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier,
|
|||
}
|
||||
//console.log(damages);
|
||||
let rawBoosts = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]];
|
||||
for (const powderID of weapon.get("powders")) {
|
||||
const powder = powderStats[powderID];
|
||||
const powders = weapon.get("powders");
|
||||
for (let i = 0; i < powders.length; i++) {
|
||||
const powder = powderStats[powders[i]];
|
||||
// Bitwise to force conversion to integer (integer division).
|
||||
const element = (powderID/6) | 0;
|
||||
const element = (powders[i]/6) | 0;
|
||||
let conversionRatio = powder.convert/100;
|
||||
if (neutralRemainingRaw[1] > 0) {
|
||||
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
|
||||
|
@ -146,7 +150,7 @@ const spell_table = {
|
|||
{ title: "Charge", cost: 4, parts: [
|
||||
{ subtitle: "Total Damage", type: "damage", multiplier: 150, conversion: [60, 0, 0, 0, 40, 0], summary: true },
|
||||
] },
|
||||
{ title: "Uppercut", cost: 10, parts: [
|
||||
{ title: "Uppercut", cost: 9, parts: [
|
||||
{ subtitle: "First Damage", type: "damage", multiplier: 300, conversion: [70, 20, 10, 0, 0, 0] },
|
||||
{ subtitle: "Fireworks Damage", type: "damage", multiplier: 50, conversion: [60, 0, 40, 0, 0, 0] },
|
||||
{ subtitle: "Crash Damage", type: "damage", multiplier: 50, conversion: [80, 0, 20, 0, 0, 0] },
|
||||
|
@ -177,7 +181,7 @@ const spell_table = {
|
|||
{ title: "Spin Attack", cost: 6, parts: [
|
||||
{ subtitle: "Total Damage", type: "damage", multiplier: 150, conversion: [70, 0, 30, 0, 0, 0], summary: true},
|
||||
] },
|
||||
{ title: "Vanish", cost: 1, parts: [
|
||||
{ title: "Vanish", cost: 2, parts: [
|
||||
{ subtitle: "No Damage", type: "none", summary: true }
|
||||
] },
|
||||
{ title: "Multihit", cost: 8, parts: [
|
||||
|
@ -194,7 +198,7 @@ const spell_table = {
|
|||
{ title: "Totem", cost: 4, parts: [
|
||||
{ subtitle: "Smash Damage", type: "damage", multiplier: 100, conversion: [80, 0, 0, 0, 20, 0]},
|
||||
{ subtitle: "Damage Tick", type: "damage", multiplier: 20, conversion: [80, 0, 0, 0, 0, 20]},
|
||||
{ subtitle: "Heal Tick", type: "heal", strength: 0.04, summary: true },
|
||||
{ subtitle: "Heal Tick", type: "heal", strength: 0.03, summary: true },
|
||||
] },
|
||||
{ title: "Haul", cost: 1, parts: [
|
||||
{ subtitle: "Total Damage", type: "damage", multiplier: 100, conversion: [80, 0, 20, 0, 0, 0], summary: true },
|
||||
|
@ -203,7 +207,7 @@ const spell_table = {
|
|||
{ subtitle: "One Wave", type: "damage", multiplier: 200, conversion: [70, 0, 0, 30, 0, 0], summary: true },
|
||||
] },
|
||||
{ title: "Uproot", cost: 6, parts: [
|
||||
{ subtitle: "Total Damage", type: "damage", multiplier: 50, conversion: [70, 30, 0, 0, 0, 0], summary: true },
|
||||
{ subtitle: "Total Damage", type: "damage", multiplier: 100, conversion: [70, 30, 0, 0, 0, 0], summary: true },
|
||||
] },
|
||||
],
|
||||
"powder": [ //This is how instant-damage powder specials are implemented. To view time-boosted damage powder specials (curse, 2nd courage, air prison, view @TODO)
|
||||
|
|
27
display.js
27
display.js
|
@ -249,6 +249,7 @@ function displayBuildStats(parent_id,build){
|
|||
"#table",
|
||||
"str", "dex", "int", "def", "agi",
|
||||
"mr", "ms",
|
||||
"hprRaw", "hprPct",
|
||||
"sdRaw", "sdPct",
|
||||
"mdRaw", "mdPct",
|
||||
"ref", "thorns",
|
||||
|
@ -419,7 +420,7 @@ function displayExpandedItem(item, parent_id){
|
|||
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];
|
||||
let damage_keys = [ "nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_" ];
|
||||
for (const i in damage_keys) {
|
||||
for (let i = 0; i < damage_keys.length; i++) {
|
||||
item.set(damage_keys[i], damages[i][0]+"-"+damages[i][1]);
|
||||
}
|
||||
}
|
||||
|
@ -482,7 +483,8 @@ function displayExpandedItem(item, parent_id){
|
|||
let active_elem;
|
||||
let fix_id = item.has("fixID") && item.get("fixID");
|
||||
let elemental_format = false;
|
||||
for (const command of display_commands) {
|
||||
for (let i = 0; i < display_commands.length; i++) {
|
||||
const command = display_commands[i];
|
||||
if (command.charAt(0) === "#") {
|
||||
if (command === "#cdiv") {
|
||||
active_elem = document.createElement('div');
|
||||
|
@ -695,12 +697,12 @@ function displayExpandedItem(item, parent_id){
|
|||
effects = powderSpecial["armorSpecialEffects"];
|
||||
specialTitle.textContent += powderSpecial["armorSpecialName"] + ": ";
|
||||
}
|
||||
for (const [key,value] of effects) {
|
||||
if (key !== "Description") {
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
if (effects[i][0] !== "Description") {
|
||||
let effect = document.createElement("p");
|
||||
effect.classList.add("itemp");
|
||||
effect.textContent += key + ": " + value[power] + specialSuffixes.get(key);
|
||||
if(key === "Damage"){
|
||||
effect.textContent += effects[i][0] + ": " + effects[i][1][power] + specialSuffixes.get(effects[i][0]);
|
||||
if(effects[i][0] === "Damage"){
|
||||
effect.textContent += elementIcons[skp_elements.indexOf(element)];
|
||||
}
|
||||
if (element === "w") {
|
||||
|
@ -802,7 +804,7 @@ function displayRecipeStats(craft, parent_id) {
|
|||
|
||||
let ingredTable = document.createElement("table");
|
||||
ingredTable.classList.add("itemtable");
|
||||
ingredTable.style.tableLayout = "fixed";
|
||||
ingredTable.classList.add("ingredTable");
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let row = document.createElement("tr");
|
||||
for (let j = 0; j < 2; j++) {
|
||||
|
@ -811,6 +813,7 @@ function displayRecipeStats(craft, parent_id) {
|
|||
cell.style.width = "50%";
|
||||
cell.classList.add("center");
|
||||
cell.classList.add("box");
|
||||
cell.classList.add("tooltip");
|
||||
let b = document.createElement("b");
|
||||
b.textContent = ingredName;
|
||||
b.classList.add("space");
|
||||
|
@ -825,10 +828,15 @@ function displayRecipeStats(craft, parent_id) {
|
|||
cell.appendChild(b);
|
||||
cell.appendChild(eff);
|
||||
row.appendChild(cell);
|
||||
|
||||
let tooltip = document.createElement("div");
|
||||
tooltip.classList.add("tooltiptext");
|
||||
tooltip.classList.add("center");
|
||||
tooltip.id = "tooltip-" + (2*i + j);
|
||||
cell.appendChild(tooltip);
|
||||
}
|
||||
ingredTable.appendChild(row);
|
||||
}
|
||||
|
||||
elem.appendChild(ldiv);
|
||||
elem.appendChild(ingredTable);
|
||||
}
|
||||
|
@ -841,7 +849,6 @@ function displayCraftStats(craft, parent_id) {
|
|||
|
||||
//Displays an ingredient in item format. However, an ingredient is too far from a normal item to display as one.
|
||||
function displayExpandedIngredient(ingred, parent_id) {
|
||||
console.log(ingred);
|
||||
let parent_elem = document.getElementById(parent_id);
|
||||
parent_elem.textContent = "";
|
||||
let display_order = [
|
||||
|
@ -1875,7 +1882,7 @@ function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spell
|
|||
let overallhealLabel = document.createElement("p");
|
||||
let first = document.createElement("b");
|
||||
let second = document.createElement("b");
|
||||
first.textContent = part.subtitle + ":";
|
||||
first.textContent = part.subtitle + ": ";
|
||||
second.textContent = heal_amount;
|
||||
overallhealLabel.appendChild(first);
|
||||
second.classList.add("Set");
|
||||
|
|
File diff suppressed because one or more lines are too long
502
index.html
502
index.html
|
@ -25,7 +25,7 @@
|
|||
<div class="summary">
|
||||
<div class="equipment" style="grid-column:1/span 2;grid-row:1">
|
||||
<div style="grid-column:1/span 2;grid-row:1;width: 40vw">
|
||||
<table class="center">
|
||||
<table class="left">
|
||||
<tr>
|
||||
<th class="center smalltitle">
|
||||
<label>Equipment</label>
|
||||
|
@ -38,19 +38,19 @@
|
|||
<tr>
|
||||
<td class="left">
|
||||
<label for="helmet-choice">Helmet:</label><br>
|
||||
<input class="iteminput" list="helmet-items" id="helmet-choice" name="helmet-choice" placeholder="No Helmet"/>
|
||||
<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>
|
||||
</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"/>
|
||||
<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>
|
||||
</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"/>
|
||||
<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>
|
||||
|
@ -59,19 +59,19 @@
|
|||
<tr>
|
||||
<td class="left">
|
||||
<label for="chestplate-choice">Chestplate:</label><br>
|
||||
<input class="iteminput" list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate" />
|
||||
<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>
|
||||
</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" />
|
||||
<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>
|
||||
</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" />
|
||||
<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>
|
||||
|
@ -80,19 +80,19 @@
|
|||
<tr>
|
||||
<td class="left">
|
||||
<label for="leggings-choice">Leggings:</label><br>
|
||||
<input class="iteminput" list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings" />
|
||||
<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>
|
||||
</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" />
|
||||
<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>
|
||||
</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" />
|
||||
<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>
|
||||
|
@ -101,19 +101,19 @@
|
|||
<tr>
|
||||
<td class="left">
|
||||
<label for="boots-choice">Boots:</label><br>
|
||||
<input class="iteminput" list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots" />
|
||||
<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>
|
||||
</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" />
|
||||
<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>
|
||||
</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"/>
|
||||
<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>
|
||||
|
@ -123,7 +123,7 @@
|
|||
<td class="left">
|
||||
<br>
|
||||
<label for="weapon-choice">Weapon:</label><br>
|
||||
<input class="iteminput" list="weapon-items" id="weapon-choice" name="weapon-choice" placeholder="No Weapon" value=""/>
|
||||
<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>
|
||||
|
@ -131,24 +131,24 @@
|
|||
<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" />
|
||||
<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>
|
||||
</td>
|
||||
<td class="left">
|
||||
<br/>
|
||||
<label for="level-choice">Level:</label><br>
|
||||
<input class="iteminput" id="level-choice" name="level-choice" placeholder="106" value=""/>
|
||||
<input class="iteminput" id="level-choice" name="level-choice" placeholder="106" value="" 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>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="center" style="grid-column:1/span 3;grid-row:2">
|
||||
<table class="center">
|
||||
<table class="left">
|
||||
<tr>
|
||||
<td>
|
||||
<br/>
|
||||
<button class = "button" id = "calc-button" onclick = "calculateBuild()">
|
||||
<button class = "button" id = "calc-button" onclick = "calculateBuild()" tabindex="16">
|
||||
Update Items
|
||||
</button>
|
||||
</td>
|
||||
|
@ -172,13 +172,13 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br><br>
|
||||
<br>
|
||||
</div>
|
||||
<div class="skillpoints" style="grid-column:1/span 3;grid-row:3;">
|
||||
<div class="center" style="grid-column:1;grid-row:1">
|
||||
<div>
|
||||
<label for="str-skp" class="skpLabel" id="str-skp-label">Strength:</label><br>
|
||||
<input type="string" id="str-skp" name="str-skp" value="0" class="skpInput"/>
|
||||
<label for="str-skp" class="skpLabel Earth" id="str-skp-label">Strength:</label><br>
|
||||
<input type="string" id="str-skp" name="str-skp" value="0" class="skpInput" tabindex="17"/>
|
||||
</div>
|
||||
<div id="str-skp-assign" class="skpDesc">
|
||||
Manually Assigned: 0
|
||||
|
@ -191,8 +191,8 @@
|
|||
</div>
|
||||
<div class="center" style="grid-column:2;grid-row:1">
|
||||
<div>
|
||||
<label for="dex-skp" class="skpLabel" id="dex-skp-label">Dexterity:</label><br>
|
||||
<input type="string" id="dex-skp" name="dex-skp" value="0" class="skpInput"/>
|
||||
<label for="dex-skp" class="skpLabel Thunder" id="dex-skp-label">Dexterity:</label><br>
|
||||
<input type="string" id="dex-skp" name="dex-skp" value="0" class="skpInput" tabindex="18"/>
|
||||
</div>
|
||||
<div id="dex-skp-assign" class="skpDesc">
|
||||
Manually Assigned: 0
|
||||
|
@ -205,8 +205,8 @@
|
|||
</div>
|
||||
<div class="center" style="grid-column:3;grid-row:1">
|
||||
<div>
|
||||
<label for="int-skp" class="skpLabel" id="int-skp-label">Intelligence:</label><br>
|
||||
<input type="string" id="int-skp" name="int-skp" value="0" class="skpInput"/>
|
||||
<label for="int-skp" class="skpLabel Water" id="int-skp-label">Intelligence:</label><br>
|
||||
<input type="string" id="int-skp" name="int-skp" value="0" class="skpInput" tabindex="19"/>
|
||||
</div>
|
||||
<div id="int-skp-assign" class="skpDesc">
|
||||
Manually Assigned: 0
|
||||
|
@ -219,8 +219,8 @@
|
|||
</div>
|
||||
<div class="center" style="grid-column:4;grid-row:1">
|
||||
<div>
|
||||
<label for="def-skp" class="skpLabel" id="def-skp-label">Defense:</label><br>
|
||||
<input type="string" id="def-skp" name="def-skp" value="0" class="skpInput"/>
|
||||
<label for="def-skp" class="skpLabel Fire" id="def-skp-label">Defense:</label><br>
|
||||
<input type="string" id="def-skp" name="def-skp" value="0" class="skpInput" tabindex="20"/>
|
||||
</div>
|
||||
<div id="def-skp-assign" class="skpDesc">
|
||||
Manually Assigned: 0
|
||||
|
@ -233,8 +233,8 @@
|
|||
</div>
|
||||
<div class="center" style="grid-column:5;grid-row:1">
|
||||
<div>
|
||||
<label for="agi-skp" class="skpLabel" id="agi-skp-label">Agility:</label><br>
|
||||
<input type="string" id="agi-skp" name="agi-skp" value="0" class="skpInput"/>
|
||||
<label for="agi-skp" class="skpLabel Air" id="agi-skp-label">Agility:</label><br>
|
||||
<input type="string" id="agi-skp" name="agi-skp" value="0" class="skpInput" tabindex="21"/>
|
||||
</div>
|
||||
<div id="agi-skp-assign" class="skpDesc">
|
||||
Manually Assigned: 0
|
||||
|
@ -247,234 +247,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="center" style="grid-column:1/span 3;grid-row:4">
|
||||
<div class="nomargin" id="summary-box">
|
||||
|
||||
</div>
|
||||
<br><br>
|
||||
<div class="externalBuffs hide-container-block" id="buff-box" style="display: none;">
|
||||
<table class="externalBuffs" style="padding-bottom:20px">
|
||||
<tr>
|
||||
<p class = "buffs-title itemp title">
|
||||
Spell Boosts & Powder Specials:
|
||||
</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "vanish-boost" onclick = "updateBoosts('vanish-boost', true)">
|
||||
Vanish (+80%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "warscream-boost" onclick = "updateBoosts('warscream-boost', true)">
|
||||
War Scream (+10%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "yourtotem-boost" onclick = "updateBoosts('yourtotem-boost', true)">
|
||||
Your Totem (+35%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "allytotem-boost" onclick = "updateBoosts('allytotem-boost', true)">
|
||||
Ally Totem (+15%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "bash-boost" onclick = "updateBoosts('bash-boost', true)">
|
||||
Bash (+50%)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class = "powder-specials" style="display: none;">
|
||||
<div class = "powder-special-stats" style = "grid-column:1;grid-row:1">
|
||||
<div class = "center" id = "powder-special-stats">
|
||||
<p class = "itemp">Powder Specials</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style = "grid-column:2;grid-row:1">
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:1">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Earth">
|
||||
Quake
|
||||
</p>
|
||||
<p class = "Quake itemp" id = "Quake">
|
||||
<b>
|
||||
<button class = "button" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:2">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Thunder">
|
||||
Chain Lightning
|
||||
</p>
|
||||
<p class = "Chain_Lightning itemp" id = "Chain_Lightning">
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:3">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Water">
|
||||
Curse
|
||||
</p>
|
||||
<p class = "Curse itemp" id = "Curse">
|
||||
<b>
|
||||
<button class = "button" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:4">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Fire">
|
||||
Courage
|
||||
</p>
|
||||
<p class = "Courage itemp" id = "Courage">
|
||||
<b>
|
||||
<button class = "button" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:5">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Air">
|
||||
Air Prison
|
||||
</p>
|
||||
<p class = "Air_Prison itemp" id = "Air_Prison">
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-1" onclick = "updatePowderSpecials('Air_Prison-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-2" onclick = "updatePowderSpecials('Air_Prison-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-3" onclick = "updatePowderSpecials('Air_Prison-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-4" onclick = "updatePowderSpecials('Air_Prison-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-5" onclick = "updatePowderSpecials('Air_Prison-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nomargin" id="summary-box"></div>
|
||||
<div class="center">
|
||||
<button class = "button" id = "update-button" onclick = "updateStats()">
|
||||
<button class = "button" id = "update-button" onclick = "updateStats()" tabindex="22">
|
||||
Update Stats
|
||||
</button>
|
||||
<button class = "button" id = "show-id-button" onclick = "toggleID()">
|
||||
<button class = "button" id = "show-id-button" onclick = "toggleID()" tabindex="23">
|
||||
Edit IDs
|
||||
</button>
|
||||
</div><br>
|
||||
</div>
|
||||
<div class="id-box fade-in" id="id-edit" style="display: none">
|
||||
<div class="id-edit1">
|
||||
<table>
|
||||
|
@ -870,7 +651,221 @@
|
|||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="externalBuffs hide-container-block" id="buff-box" style="display: none;">
|
||||
<table class="externalBuffs" style="padding-bottom:20px">
|
||||
<tr>
|
||||
<p class = "buffs-title itemp smalltitle">
|
||||
Spell Boosts & Powder Specials:
|
||||
</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "vanish-boost" onclick = "updateBoosts('vanish-boost', true)">
|
||||
Vanish (+80%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "warscream-boost" onclick = "updateBoosts('warscream-boost', true)">
|
||||
War Scream (+10%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "yourtotem-boost" onclick = "updateBoosts('yourtotem-boost', true)">
|
||||
Your Totem (+35%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "allytotem-boost" onclick = "updateBoosts('allytotem-boost', true)">
|
||||
Ally Totem (+15%)
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button class = "button-narrow" id = "bash-boost" onclick = "updateBoosts('bash-boost', true)">
|
||||
Bash (+50%)
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class = "powder-specials" style="display: none;">
|
||||
<div class = "powder-special-stats" style = "grid-column:1;grid-row:1">
|
||||
<div class = "center" id = "powder-special-stats">
|
||||
<p class = "itemp">Powder Specials</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style = "grid-column:2;grid-row:1">
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:1">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Earth">
|
||||
Quake
|
||||
</p>
|
||||
<p class = "Quake itemp" id = "Quake">
|
||||
<b>
|
||||
<button class = "button" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:2">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Thunder">
|
||||
Chain Lightning
|
||||
</p>
|
||||
<p class = "Chain_Lightning itemp" id = "Chain_Lightning">
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:3">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Water">
|
||||
Curse
|
||||
</p>
|
||||
<p class = "Curse itemp" id = "Curse">
|
||||
<b>
|
||||
<button class = "button" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:4">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Fire">
|
||||
Courage
|
||||
</p>
|
||||
<p class = "Courage itemp" id = "Courage">
|
||||
<b>
|
||||
<button class = "button" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "powder-special" style = "grid-column:2;grid-row:5">
|
||||
<div class = "center" id = "powder-special">
|
||||
<p class = "itemp Air">
|
||||
Air Prison
|
||||
</p>
|
||||
<p class = "Air_Prison itemp" id = "Air_Prison">
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-1" onclick = "updatePowderSpecials('Air_Prison-1', true)">
|
||||
4
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-2" onclick = "updatePowderSpecials('Air_Prison-2', true)">
|
||||
4.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-3" onclick = "updatePowderSpecials('Air_Prison-3', true)">
|
||||
5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-4" onclick = "updatePowderSpecials('Air_Prison-4', true)">
|
||||
5.5
|
||||
</button>
|
||||
</b>
|
||||
<b>
|
||||
<button class = "button" id = "Air_Prison-5" onclick = "updatePowderSpecials('Air_Prison-5', true)">
|
||||
6
|
||||
</button>
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wide-space"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -972,6 +967,7 @@
|
|||
</div>
|
||||
|
||||
<script type="text/javascript" src="utils.js"></script>
|
||||
<script type="text/javascript" src="powders.js"></script>
|
||||
<script type="text/javascript" src="build_utils.js"></script>
|
||||
<script type="text/javascript" src="skillpoints.js"></script>
|
||||
<script type="text/javascript" src="damage_calc.js"></script>
|
||||
|
|
53
items.html
Normal file
53
items.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html scroll-behavior="smooth">
|
||||
<head>
|
||||
<!-- 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="icon" href="favicon.png">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<title>WynnAtlas</title>
|
||||
</head>
|
||||
<body class="all" style="overflow-y: scroll">
|
||||
<div class="header" id="header">
|
||||
WynnAtlas
|
||||
</div>
|
||||
<div class="center" id="header2">
|
||||
Made by: hppeng and ferricles
|
||||
</div>
|
||||
<div class="center" id="credits">
|
||||
<a href="credits.txt">Additional credits</a>
|
||||
</div>
|
||||
<div class="center" id="main" style="padding: 2%">
|
||||
<div id="search-container" style="margin-bottom: 1.5%">
|
||||
<div class="left" id="search-filter" style="display: inline-block; vertical-align: top">
|
||||
<label for="search-filter-field">Filter By:</label>
|
||||
<br>
|
||||
<input id="search-filter-field" type="text" placeholder="str >= 15 & dex >= 10" style="width: 25vw; padding: 8px">
|
||||
<br>
|
||||
<div id="search-filter-error" style="color: #ff0000"></div>
|
||||
</div>
|
||||
<div class="left" id="search-sort" style="display: inline-block; vertical-align: top">
|
||||
<label for="search-sort-field">Sort By:</label>
|
||||
<br>
|
||||
<input id="search-sort-field" type="text" placeholder="str + dex; meleerawdmg + spellrawdmg" style="width: 25vw; padding: 8px">
|
||||
<br>
|
||||
<div id="search-sort-error" style="color: #ff0000"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="item-list-container">
|
||||
<div class="left" id="item-list" style="display: flex; flex-flow: row wrap"></div>
|
||||
<div class="center" id="item-list-footer"></div>
|
||||
</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="display.js"></script>
|
||||
<script type="text/javascript" src="query.js"></script>
|
||||
<script type="text/javascript" src="load.js"></script>
|
||||
<script type="text/javascript" src="items.js"></script>
|
||||
</body>
|
||||
</html>
|
193
items.js
Normal file
193
items.js
Normal file
|
@ -0,0 +1,193 @@
|
|||
// represents a field containing a query expression string
|
||||
class ExprField {
|
||||
constructor(fieldId, errorTextId, compiler) {
|
||||
this.field = document.getElementById(fieldId);
|
||||
this.errorText = document.getElementById(errorTextId);
|
||||
this.compiler = compiler;
|
||||
this.output = null;
|
||||
this.text = null;
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.field.value;
|
||||
}
|
||||
|
||||
compile() {
|
||||
if (this.value === this.text) return false;
|
||||
this.text = this.value;
|
||||
this.errorText.innerText = '';
|
||||
try {
|
||||
this.output = this.compiler(this.text);
|
||||
} catch (e) {
|
||||
this.errorText.innerText = e.message;
|
||||
this.output = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function compareLexico(ia, keysA, ib, keysB) {
|
||||
for (let i = 0; i < keysA.length; i++) { // assuming keysA and keysB are the same length
|
||||
let aKey = keysA[i], bKey = keysB[i];
|
||||
if (typeof aKey !== typeof bKey) throw new Error(`Incomparable types ${typeof aKey} and ${typeof bKey}`); // can this even happen?
|
||||
switch (typeof aKey) {
|
||||
case 'string':
|
||||
aKey = aKey.toLowerCase();
|
||||
bKey = bKey.toLowerCase();
|
||||
if (aKey < bKey) return -1;
|
||||
if (aKey > bKey) return 1;
|
||||
break;
|
||||
case 'number': // sort numeric stuff in reverse order
|
||||
if (aKey < bKey) return 1;
|
||||
if (aKey > bKey) return -1;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Incomparable type ${typeof aKey}`);
|
||||
}
|
||||
}
|
||||
return ib.lvl - ia.lvl;
|
||||
}
|
||||
|
||||
function stringify(v) {
|
||||
return typeof v === 'number' ? (Math.round(v * 100) / 100).toString() : v;
|
||||
}
|
||||
|
||||
function init() {
|
||||
const itemList = document.getElementById('item-list');
|
||||
const itemListFooter = document.getElementById('item-list-footer');
|
||||
|
||||
// compile the search db from the item db
|
||||
const searchDb = items.filter(i => !i.remapID).map(i => [i, expandItem(i, [])]);
|
||||
|
||||
// init item list elements
|
||||
const ITEM_LIST_SIZE = 64;
|
||||
const itemEntries = [];
|
||||
for (let i = 0; i < ITEM_LIST_SIZE; i++) {
|
||||
const itemElem = document.createElement('div');
|
||||
itemElem.classList.add('box');
|
||||
itemElem.setAttribute('id', `item-entry-${i}`);
|
||||
itemElem.style.display = 'none';
|
||||
itemElem.style.width = '20vw';
|
||||
itemElem.style.margin = '1vw';
|
||||
itemElem.style.verticalAlign = 'top';
|
||||
itemList.append(itemElem);
|
||||
itemEntries.push(itemElem);
|
||||
}
|
||||
|
||||
// the two search query input boxes
|
||||
const searchFilterField = new ExprField('search-filter-field', 'search-filter-error', function(exprStr) {
|
||||
const expr = compileQueryExpr(exprStr);
|
||||
return expr !== null ? expr : (i, ie) => true;
|
||||
});
|
||||
const searchSortField = new ExprField('search-sort-field', 'search-sort-error', function(exprStr) {
|
||||
const subExprs = exprStr.split(';').map(compileQueryExpr).filter(f => f != null);
|
||||
return function(i, ie) {
|
||||
const sortKeys = [];
|
||||
for (let k = 0; k < subExprs.length; k++) sortKeys.push(subExprs[k](i, ie));
|
||||
return sortKeys;
|
||||
};
|
||||
});
|
||||
|
||||
// updates the current search state from the search query input boxes
|
||||
function updateSearch() {
|
||||
// compile query expressions, aborting if nothing has changed or either fails to compile
|
||||
const changed = searchFilterField.compile() | searchSortField.compile();
|
||||
if (!changed || searchFilterField.output === null || searchSortField.output === null) return;
|
||||
|
||||
// update url query string
|
||||
const newUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}`
|
||||
+ `?f=${encodeURIComponent(searchFilterField.value)}&s=${encodeURIComponent(searchSortField.value)}`;
|
||||
window.history.pushState({ path: newUrl }, '', newUrl);
|
||||
|
||||
// hide old search results
|
||||
itemListFooter.innerText = '';
|
||||
for (const itemEntry of itemEntries) itemEntry.style.display = 'none';
|
||||
|
||||
// index and sort search results
|
||||
const searchResults = [];
|
||||
try {
|
||||
for (let i = 0; i < searchDb.length; i++) {
|
||||
const item = searchDb[i][0], itemExp = searchDb[i][1];
|
||||
if (checkBool(searchFilterField.output(item, itemExp))) {
|
||||
searchResults.push({ item, itemExp, sortKeys: searchSortField.output(item, itemExp) });
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
searchFilterField.errorText.innerText = e.message;
|
||||
return;
|
||||
}
|
||||
if (searchResults.length === 0) {
|
||||
itemListFooter.innerText = 'No results!';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
searchResults.sort((a, b) => compareLexico(a.item, a.sortKeys, b.item, b.sortKeys));
|
||||
} catch (e) {
|
||||
searchSortField.errorText.innerText = e.message;
|
||||
return;
|
||||
}
|
||||
|
||||
// display search results
|
||||
const searchMax = Math.min(searchResults.length, ITEM_LIST_SIZE);
|
||||
for (let i = 0; i < searchMax; i++) {
|
||||
const result = searchResults[i];
|
||||
itemEntries[i].style.display = 'inline-block';
|
||||
displayExpandedItem(result.itemExp, `item-entry-${i}`);
|
||||
if (result.sortKeys.length > 0) {
|
||||
const sortKeyListContainer = document.createElement('div');
|
||||
sortKeyListContainer.classList.add('itemleft');
|
||||
const sortKeyList = document.createElement('ul');
|
||||
sortKeyList.classList.add('itemp', 'T0');
|
||||
sortKeyList.style.marginLeft = '1.75em';
|
||||
sortKeyListContainer.append(sortKeyList);
|
||||
for (let j = 0; j < result.sortKeys.length; j++) {
|
||||
const sortKeyElem = document.createElement('li');
|
||||
sortKeyElem.innerText = stringify(result.sortKeys[j]);
|
||||
sortKeyList.append(sortKeyElem);
|
||||
}
|
||||
itemEntries[i].append(sortKeyListContainer);
|
||||
}
|
||||
}
|
||||
if (searchMax < searchResults.length) {
|
||||
itemListFooter.innerText = `${searchResults.length - searchMax} more...`;
|
||||
}
|
||||
}
|
||||
|
||||
// updates the search state from the input boxes after a brief delay, to prevent excessive DOM updates
|
||||
let updateSearchTask = null;
|
||||
function scheduleSearchUpdate() {
|
||||
if (updateSearchTask !== null) {
|
||||
clearTimeout(updateSearchTask);
|
||||
}
|
||||
updateSearchTask = setTimeout(() => {
|
||||
updateSearchTask = null;
|
||||
updateSearch();
|
||||
}, 500);
|
||||
}
|
||||
searchFilterField.field.addEventListener('input', e => scheduleSearchUpdate());
|
||||
searchSortField.field.addEventListener('input', e => scheduleSearchUpdate());
|
||||
|
||||
// parse query string, display initial search results
|
||||
if (window.location.search.startsWith('?')) {
|
||||
for (const entryStr of window.location.search.substring(1).split('&')) {
|
||||
const ndx = entryStr.indexOf('=');
|
||||
if (ndx !== -1) {
|
||||
switch (entryStr.substring(0, ndx)) {
|
||||
case 'f':
|
||||
searchFilterField.field.value = decodeURIComponent(entryStr.substring(ndx + 1));
|
||||
break;
|
||||
case 's':
|
||||
searchSortField.field.value = decodeURIComponent(entryStr.substring(ndx + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateSearch();
|
||||
|
||||
// focus the query filter text box
|
||||
searchFilterField.field.focus();
|
||||
searchFilterField.field.select();
|
||||
}
|
||||
|
||||
load_init(init);
|
9
load.js
9
load.js
|
@ -1,4 +1,4 @@
|
|||
const DB_VERSION = 24;
|
||||
const DB_VERSION = 27;
|
||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
|
||||
|
||||
let db;
|
||||
|
@ -35,7 +35,7 @@ async function load_local(init_func) {
|
|||
}
|
||||
else {
|
||||
console.log("Successfully read local set db.");
|
||||
console.log(sets);
|
||||
//console.log(sets);
|
||||
init_func();
|
||||
}
|
||||
}
|
||||
|
@ -48,12 +48,14 @@ async function load_local(init_func) {
|
|||
* Clean bad item data. For now just assigns display name if it isn't already assigned.
|
||||
*/
|
||||
function clean_item(item) {
|
||||
if (item.remapID === undefined) {
|
||||
if (item.displayName === undefined) {
|
||||
item.displayName = item.name;
|
||||
}
|
||||
item.skillpoints = [item.str, item.dex, item.int, item.def, item.agi];
|
||||
item.has_negstat = item.str < 0 || item.dex < 0 || item.int < 0 || item.def < 0 || item.agi < 0;
|
||||
item.reqs = [item.strReq, item.dexReq, item.intReq, item.defReq, item.agiReq];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -77,7 +79,8 @@ async function load(init_func) {
|
|||
await clear_tx.complete;
|
||||
|
||||
let add_tx = db.transaction(['item_db', 'set_db'], 'readwrite');
|
||||
add_tx.onabort = function() {
|
||||
add_tx.onabort = function(e) {
|
||||
console.log(e);
|
||||
console.log("Not enough space...");
|
||||
};
|
||||
let items_store = add_tx.objectStore('item_db');
|
||||
|
|
19
narrow.css
19
narrow.css
|
@ -6,6 +6,14 @@
|
|||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.equipment{
|
||||
padding: 0%;
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content auto;
|
||||
gap: 5px;
|
||||
grid-template-rows: min-content min-content auto;
|
||||
}
|
||||
.summary {
|
||||
padding: 2% 4% 4%;
|
||||
display: grid;
|
||||
|
@ -17,6 +25,9 @@
|
|||
|
||||
}
|
||||
|
||||
.ingredTable {
|
||||
table-layout: "fixed";
|
||||
}
|
||||
.build, .spells .misc {
|
||||
padding: 2%;
|
||||
display: grid;
|
||||
|
@ -33,6 +44,12 @@
|
|||
max-height: 50px;
|
||||
}
|
||||
|
||||
.powderinput {
|
||||
width: 25vw;
|
||||
height: 10vw;
|
||||
max-height: 50px;
|
||||
}
|
||||
|
||||
.skpInput, .idInput {
|
||||
width: 90%;
|
||||
height: 7vw;
|
||||
|
@ -44,7 +61,7 @@
|
|||
}
|
||||
|
||||
.idLabel, .skpLabel, .idDesc, .skpDesc {
|
||||
font-size: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.title{
|
||||
|
|
52
powders.js
Normal file
52
powders.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
let powderIDs = new Map();
|
||||
let powderNames = new Map();
|
||||
let _powderID = 0;
|
||||
for (const x of skp_elements) {
|
||||
for (let i = 1; i <= 6; ++i) {
|
||||
// Support both upper and lowercase, I guess.
|
||||
powderIDs.set(x.toUpperCase()+i, _powderID);
|
||||
powderIDs.set(x+i, _powderID);
|
||||
powderNames.set(_powderID, x+i);
|
||||
_powderID++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ordering: [dmgMin, dmgMax, convert, defPlus, defMinus (+6 mod 5)]
|
||||
class Powder {
|
||||
constructor(min, max, convert, defPlus, defMinus) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.convert = convert;
|
||||
this.defPlus = defPlus;
|
||||
this.defMinus = defMinus;
|
||||
}
|
||||
}
|
||||
function _p(a,b,c,d,e) { return new Powder(a,b,c,d,e); } //bruh moment
|
||||
|
||||
let powderStats = [
|
||||
_p(3,6,17,2,1), _p(6,9,21,4,2), _p(8,14,25,8,3), _p(11,16,31,14,5), _p(15,18,38,22,9), _p(18,22,46,30,13),
|
||||
_p(1,8,9,3,1), _p(1,13,11,5,1), _p(2,18,14,9,2), _p(3,24,17,14,4), _p(3,32,22,20,7), _p(5,40,28,28,10),
|
||||
_p(3,4,13,3,1), _p(4,7,15,6,1), _p(6,10,17,11,2), _p(8,12,21,18,4), _p(11,14,26,28,7), _p(13,17,32,40,10),
|
||||
_p(2,5,14,3,1), _p(4,8,16,5,2), _p(6,10,19,9,3), _p(9,13,24,16,5), _p(12,16,30,25,9), _p(15,19,37,36,13),
|
||||
_p(2,6,11,3,1), _p(4,9,14,6,2), _p(7,10,17,10,3), _p(9,13,22,16,5), _p(13,18,28,24,9), _p(16,18,35,34,13)
|
||||
];
|
||||
|
||||
//Ordering: [weapon special name, weapon special effects, armor special name, armor special effects]
|
||||
class PowderSpecial{
|
||||
constructor(wSpName, wSpEff, aSpName, aSpEff, cap){
|
||||
this.weaponSpecialName = wSpName;
|
||||
this.weaponSpecialEffects = wSpEff;
|
||||
this.armorSpecialName = aSpName;
|
||||
this.armorSpecialEffects = aSpEff;
|
||||
this.cap = cap;
|
||||
}
|
||||
}
|
||||
function _ps(a,b,c,d,e) { return new PowderSpecial(a,b,c,d,e); } //bruh moment
|
||||
|
||||
let powderSpecialStats = [
|
||||
_ps("Quake",new Map([["Radius",[5,5.5,6,6.5,7]], ["Damage",[155,220,285,350,415]] ]),"Rage",new Map([ ["Damage", [0.3,0.4,0.5,0.7,1.0]],["Description", "% " + "\u2764" + " Missing"] ]),400), //e
|
||||
_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
|
||||
];
|
582
query.js
Normal file
582
query.js
Normal file
|
@ -0,0 +1,582 @@
|
|||
/*
|
||||
* disj := conj "|" disj
|
||||
* | conj
|
||||
*
|
||||
* conj := cmp "&" conj
|
||||
* | cmpEq
|
||||
*
|
||||
* cmpEq := cmpRel "=" cmpEq
|
||||
* | cmpRel "!=" cmpEq
|
||||
*
|
||||
* cmpRel := sum "<=" cmpRel
|
||||
* | sum "<" cmpRel
|
||||
* | sum ">" cmpRel
|
||||
* | sum ">=" cmpRel
|
||||
* | sum
|
||||
*
|
||||
* sum := prod "+" sum
|
||||
* | prod "-" sum
|
||||
* | prod
|
||||
*
|
||||
* prod := exp "*" prod
|
||||
* | exp "/" prod
|
||||
* | exp
|
||||
*
|
||||
* exp := unary "^" exp
|
||||
* | unary
|
||||
*
|
||||
* unary := "-" unary
|
||||
* | "!" unary
|
||||
* | prim
|
||||
*
|
||||
* prim := nLit
|
||||
* | bLit
|
||||
* | sLit
|
||||
* | ident "(" [disj ["," disj...]] ")"
|
||||
* | ident
|
||||
* | "(" disj ")"
|
||||
*/
|
||||
|
||||
// a list of tokens indexed by a single pointer
|
||||
class TokenList {
|
||||
constructor(tokens) {
|
||||
this.tokens = tokens;
|
||||
this.ptr = 0;
|
||||
}
|
||||
|
||||
get here() {
|
||||
if (this.ptr >= this.tokens.length) throw new Error('Reached end of expression');
|
||||
return this.tokens[this.ptr];
|
||||
}
|
||||
|
||||
advance(steps = 1) {
|
||||
this.ptr = Math.min(this.ptr + steps, this.tokens.length);
|
||||
}
|
||||
}
|
||||
|
||||
// type casts
|
||||
function checkBool(v) {
|
||||
if (typeof v !== 'boolean') throw new Error(`Expected boolean, but got ${typeof v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
function checkNum(v) {
|
||||
if (typeof v !== 'number') throw new Error(`Expected number, but got ${typeof v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
function checkStr(v) {
|
||||
if (typeof v !== 'string') throw new Error(`Expected string, but got ${typeof v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
// properties of items that can be looked up
|
||||
const itemQueryProps = (function() {
|
||||
const props = {};
|
||||
function prop(names, getProp) {
|
||||
if (Array.isArray(names)) {
|
||||
for (name of names) {
|
||||
props[name] = getProp;
|
||||
}
|
||||
} else {
|
||||
props[names] = getProp;
|
||||
}
|
||||
}
|
||||
function maxId(names, idKey) {
|
||||
prop(names, (i, ie) => ie.get('maxRolls').get(idKey) || 0);
|
||||
}
|
||||
function minId(names, idKey) {
|
||||
prop(names, (i, ie) => ie.get('minRolls').get(idKey) || 0);
|
||||
}
|
||||
function rangeAvg(names, getProp) {
|
||||
prop(names, (i, ie) => {
|
||||
const range = getProp(i, ie);
|
||||
if (!range) return 0;
|
||||
const ndx = range.indexOf('-');
|
||||
return (parseInt(range.substring(0, ndx), 10) + parseInt(range.substring(ndx + 1), 10)) / 2;
|
||||
});
|
||||
}
|
||||
function map(names, comps, f) {
|
||||
return prop(names, (i, ie) => {
|
||||
const args = [];
|
||||
for (let k = 0; k < comps.length; k++) args.push(comps[k](i, ie));
|
||||
return f.apply(null, args);
|
||||
});
|
||||
}
|
||||
function sum(names, ...comps) {
|
||||
return map(names, comps, (...summands) => {
|
||||
let total = 0;
|
||||
for (let i = 0; i < summands.length; i++) total += summands[i];
|
||||
return total;
|
||||
});
|
||||
}
|
||||
|
||||
prop('name', (i, ie) => i.displayName || i.name);
|
||||
prop('type', (i, ie) => i.type);
|
||||
prop(['cat', 'category'], (i, ie) => i.category);
|
||||
const tierIndices = { Normal: 0, Unique: 1, Set: 2, Rare: 3, Legendary: 4, Fabled: 5, Mythic: 6 };
|
||||
prop(['rarityname', 'raritystr', 'tiername', 'tierstr'], (i, ie) => i.tier);
|
||||
prop(['rarity', 'tier'], (i, ie) => tierIndices[i.tier]);
|
||||
|
||||
prop(['level', 'lvl', 'combatlevel', 'combatlvl'], (i, ie) => i.lvl);
|
||||
prop(['strmin', 'strreq'], (i, ie) => i.strReq);
|
||||
prop(['dexmin', 'dexreq'], (i, ie) => i.dexReq);
|
||||
prop(['intmin', 'intreq'], (i, ie) => i.intReq);
|
||||
prop(['defmin', 'defreq'], (i, ie) => i.defReq);
|
||||
prop(['agimin', 'agireq'], (i, ie) => i.agiReq);
|
||||
sum(['summin', 'sumreq', 'totalmin', 'totalreq'], props.strmin, props.dexmin, props.intmin, props.defmin, props.agimin);
|
||||
|
||||
prop('str', (i, ie) => i.str);
|
||||
prop('dex', (i, ie) => i.dex);
|
||||
prop('int', (i, ie) => i.int);
|
||||
prop('def', (i, ie) => i.def);
|
||||
prop('agi', (i, ie) => i.agi);
|
||||
sum(['skillpoints', 'skillpts', 'attributes', 'attrs'], props.str, props.dex, props.int, props.def, props.agi);
|
||||
|
||||
rangeAvg(['neutraldmg', 'neutraldam', 'ndmg', 'ndam'], (i, ie) => i.nDam);
|
||||
rangeAvg(['earthdmg', 'earthdam', 'edmg', 'edam'], (i, ie) => i.eDam);
|
||||
rangeAvg(['thunderdmg', 'thunderdam', 'tdmg', 'tdam'], (i, ie) => i.tDam);
|
||||
rangeAvg(['waterdmg', 'waterdam', 'wdmg', 'wdam'], (i, ie) => i.wDam);
|
||||
rangeAvg(['firedmg', 'firedam', 'fdmg', 'fdam'], (i, ie) => i.fDam);
|
||||
rangeAvg(['airdmg', 'airdam', 'admg', 'adam'], (i, ie) => i.aDam);
|
||||
sum(['sumdmg', 'sumdam', 'totaldmg', 'totaldam'], props.ndam, props.edam, props.tdam, props.wdam, props.fdam, props.adam);
|
||||
|
||||
maxId(['earthdmg%', 'earthdam%', 'edmg%', 'edam%', 'edampct'], 'eDamPct');
|
||||
maxId(['thunderdmg%', 'thunderdam%', 'tdmg%', 'tdam%', 'tdampct'], 'tDamPct');
|
||||
maxId(['waterdmg%', 'waterdam%', 'wdmg%', 'wdam%', 'wdampct'], 'wDamPct');
|
||||
maxId(['firedmg%', 'firedam%', 'fdmg%', 'fdam%', 'fdampct'], 'fDamPct');
|
||||
maxId(['airdmg%', 'airdam%', 'admg%', 'adam%', 'adampct'], 'aDamPct');
|
||||
sum(['sumdmg%', 'sumdam%', 'totaldmg%', 'totaldam%', 'sumdampct', 'totaldampct'], props.edampct, props.tdampct, props.wdampct, props.fdampct, props.adampct);
|
||||
|
||||
maxId(['mainatkdmg', 'mainatkdam', 'mainatkdmg%', 'mainatkdam%', 'meleedmg', 'meleedam', 'meleedmg%', 'meleedam%', 'mdpct'], 'mdPct');
|
||||
maxId(['mainatkrawdmg', 'mainatkrawdam', 'mainatkneutraldmg', 'mainatkneutraldam', 'meleerawdmg', 'meleerawdam', 'meleeneutraldmg', 'meleeneutraldam', 'mdraw'], 'mdRaw');
|
||||
maxId(['spelldmg', 'spelldam', 'spelldmg%', 'spelldam%', 'sdpct'], 'sdPct');
|
||||
maxId(['spellrawdmg', 'spellrawdam', 'spellneutraldmg', 'spellneutraldam', 'sdraw'], 'sdRaw');
|
||||
|
||||
const atkSpdIndices = { SUPER_SLOW: -3, VERY_SLOW: -2, SLOW: -1, NORMAL: 0, FAST: 1, VERY_FAST: 2, SUPER_FAST: 3 };
|
||||
prop(['attackspeed', 'atkspd'], (i, ie) => i.atkSpd ? atkSpdIndices[i.atkSpd] : 0);
|
||||
maxId(['bonusattackspeed', 'bonusatkspd', 'attackspeedid', 'atkspdid', 'attackspeed+', 'atkspd+', 'atktier'], 'atkTier');
|
||||
sum(['sumattackspeed', 'totalattackspeed', 'sumatkspd', 'totalatkspd', 'sumatktier', 'totalatktier'], props.atkspd, props.atktier);
|
||||
|
||||
prop(['earthdef', 'edef'], (i, ie) => i.eDef || 0);
|
||||
prop(['thunderdef', 'tdef'], (i, ie) => i.tDef || 0);
|
||||
prop(['waterdef', 'wdef'], (i, ie) => i.wDef || 0);
|
||||
prop(['firedef', 'fdef'], (i, ie) => i.fDef || 0);
|
||||
prop(['airdef', 'adef'], (i, ie) => i.aDef || 0);
|
||||
sum(['sumdef', 'totaldef'], props.edef, props.tdef, props.wdef, props.fdef, props.adef);
|
||||
|
||||
maxId(['earthdef%', 'edef%', 'edefpct'], 'eDefPct');
|
||||
maxId(['thunderdef%', 'tdef%', 'tdefpct'], 'tDefPct');
|
||||
maxId(['waterdef%', 'wdef%', 'wdefpct'], 'wDefPct');
|
||||
maxId(['firedef%', 'fdef%', 'fdefpct'], 'fDefPct');
|
||||
maxId(['airdef%', 'adef%', 'adefpct'], 'aDefPct');
|
||||
sum(['sumdef%', 'totaldef%', 'sumdefpct', 'totaldefpct'], props.edefpct, props.tdefpct, props.wdefpct, props.fdefpct, props.adefpct);
|
||||
|
||||
prop(['health', 'hp'], (i, ie) => i.hp || 0);
|
||||
maxId(['bonushealth', 'healthid', 'bonushp', 'hpid', 'health+', 'hp+', 'hpbonus'], 'hpBonus');
|
||||
sum(['sumhealth', 'sumhp', 'totalhealth', 'totalhp'], props.hp, props.hpid);
|
||||
|
||||
maxId(['hpregen', 'hpr', 'hr', 'hprraw'], 'hprRaw');
|
||||
maxId(['hpregen%', 'hpr%', 'hr%', 'hprpct'], 'hprPct');
|
||||
maxId(['lifesteal', 'ls'], 'ls');
|
||||
maxId(['manaregen', 'mr'], 'mr');
|
||||
maxId(['manasteal', 'ms'], 'ms');
|
||||
|
||||
maxId(['walkspeed', 'movespeed', 'ws', 'spd'], 'spd');
|
||||
maxId('sprint', 'sprint');
|
||||
maxId(['sprintregen', 'sprintreg'], 'sprintReg');
|
||||
maxId(['jumpheight', 'jh'], 'jh');
|
||||
|
||||
minId(['spellcost1', 'rawspellcost1', 'spcost1', 'spraw1'], 'spRaw1');
|
||||
minId(['spellcost1%', 'spcost1%', 'sppct1'], 'spPct1');
|
||||
minId(['spellcost2', 'rawspellcost2', 'spcost2', 'spraw2'], 'spRaw2');
|
||||
minId(['spellcost2%', 'spcost2%', 'sppct2'], 'spPct2');
|
||||
minId(['spellcost3', 'rawspellcost3', 'spcost3', 'spraw3'], 'spRaw3');
|
||||
minId(['spellcost3%', 'spcost3%', 'sppct3'], 'spPct3');
|
||||
minId(['spellcost4', 'rawspellcost4', 'spcost4', 'spraw4'], 'spRaw4');
|
||||
minId(['spellcost4%', 'spcost4%', 'sppct4'], 'spPct4');
|
||||
sum(['sumspellcost', 'totalspellcost', 'sumrawspellcost', 'totalrawspellcost', 'sumspcost', 'totalspcost', 'sumspraw', 'totalspraw'], props.spraw1, props.spraw2, props.spraw3, props.spraw4);
|
||||
sum(['sumspellcost%', 'totalspellcost%', 'sumspcost%', 'totalspcost%', 'sumsppct', 'totalsppct'], props.sppct1, props.sppct2, props.sppct3, props.sppct4);
|
||||
|
||||
maxId(['exploding', 'expl', 'expd'], 'expd');
|
||||
maxId('poison', 'poison');
|
||||
maxId('thorns', 'thorns');
|
||||
maxId(['reflection', 'refl', 'ref'], 'ref');
|
||||
maxId(['soulpointregen', 'spr', 'spregen'], 'spRegen');
|
||||
maxId(['lootbonus', 'lb'], 'lb');
|
||||
maxId(['xpbonus', 'xpb', 'xb'], 'xpb');
|
||||
maxId(['stealing', 'esteal'], 'eSteal');
|
||||
prop(['powderslots', 'powders', 'slots', 'sockets'], (i, ie) => i.slots || 0);
|
||||
|
||||
return props;
|
||||
})();
|
||||
|
||||
// functions that can be called in query expressions
|
||||
const itemQueryFuncs = {
|
||||
max(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to max()');
|
||||
let runningMax = -Infinity;
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (checkNum(args[i]) > runningMax) runningMax = args[i];
|
||||
}
|
||||
return runningMax;
|
||||
},
|
||||
min(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to min()');
|
||||
let runningMin = Infinity;
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (checkNum(args[i]) < runningMin) runningMin = args[i];
|
||||
}
|
||||
return runningMin;
|
||||
},
|
||||
floor(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to floor()');
|
||||
return Math.floor(checkNum(args[0]));
|
||||
},
|
||||
ceil(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to ceil()');
|
||||
return Math.ceil(checkNum(args[0]));
|
||||
},
|
||||
round(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to ceil()');
|
||||
return Math.round(checkNum(args[0]));
|
||||
},
|
||||
sqrt(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to ceil()');
|
||||
return Math.sqrt(checkNum(args[0]));
|
||||
},
|
||||
abs(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to ceil()');
|
||||
return Math.abs(checkNum(args[0]));
|
||||
},
|
||||
contains(args) {
|
||||
if (args.length < 2) throw new Error('Not enough args to contains()');
|
||||
return checkStr(args[0]).toLowerCase().includes(checkStr(args[1]).toLowerCase());
|
||||
},
|
||||
atkspdmod(args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to atkSpdMod()');
|
||||
switch (checkNum(args[0])) {
|
||||
case 2: return 3.1;
|
||||
case 1: return 2.5;
|
||||
case 0: return 2.05;
|
||||
case -1: return 1.5;
|
||||
case -2: return 0.83;
|
||||
}
|
||||
if (args[0] <= -3) return 0.51;
|
||||
if (args[0] >= 3) return 4.3;
|
||||
throw new Error('Invalid argument to atkSpdMod()');
|
||||
}
|
||||
};
|
||||
|
||||
// the compiler itself
|
||||
const compileQueryExpr = (function() {
|
||||
// tokenize an expression string
|
||||
function tokenize(exprStr) {
|
||||
exprStr = exprStr.trim();
|
||||
const tokens = [];
|
||||
let col = 0;
|
||||
function pushSymbol(sym) {
|
||||
tokens.push({ type: 'sym', sym });
|
||||
col += sym.length;
|
||||
}
|
||||
while (col < exprStr.length) {
|
||||
// parse fixed symbols, like operators and stuff
|
||||
switch (exprStr[col]) {
|
||||
case '(':
|
||||
case ')':
|
||||
case ',':
|
||||
case '&':
|
||||
case '|':
|
||||
case '+':
|
||||
case '-':
|
||||
case '*':
|
||||
case '/':
|
||||
case '^':
|
||||
case '=':
|
||||
pushSymbol(exprStr[col]);
|
||||
continue;
|
||||
case '>':
|
||||
pushSymbol(exprStr[col + 1] === '=' ? '>=' : '>');
|
||||
continue;
|
||||
case '<':
|
||||
pushSymbol(exprStr[col + 1] === '=' ? '<=' : '<');
|
||||
continue;
|
||||
case '!':
|
||||
pushSymbol(exprStr[col + 1] === '=' ? '!=' : '!');
|
||||
continue;
|
||||
case ' ': // ignore extra whitespace
|
||||
++col;
|
||||
continue;
|
||||
}
|
||||
// parse a numeric literal
|
||||
let m;
|
||||
if ((m = /^\d+(?:\.\d*)?/.exec(exprStr.substring(col))) !== null) {
|
||||
tokens.push({ type: 'num', value: parseFloat(m[0]) });
|
||||
col += m[0].length;
|
||||
continue;
|
||||
}
|
||||
// parse a string literal
|
||||
if ((m = /^"([^"]+)"/.exec(exprStr.substring(col))) !== null) { // with double-quotes
|
||||
tokens.push({ type: 'str', value: m[1] });
|
||||
col += m[0].length;
|
||||
continue;
|
||||
}
|
||||
if ((m = /^'([^']+)'/.exec(exprStr.substring(col))) !== null) { // with single-quotes
|
||||
tokens.push({ type: 'str', value: m[1] });
|
||||
col += m[0].length;
|
||||
continue;
|
||||
}
|
||||
// parse an identifier or boolean literal
|
||||
if ((m = /^\w[\w\d+%]*/.exec(exprStr.substring(col))) !== null) {
|
||||
switch (m[0]) {
|
||||
case 'true':
|
||||
tokens.push({ type: 'bool', value: true });
|
||||
col += 4;
|
||||
continue;
|
||||
case 'false':
|
||||
tokens.push({ type: 'bool', value: false });
|
||||
col += 5;
|
||||
continue;
|
||||
}
|
||||
tokens.push({ type: 'id', id: m[0] });
|
||||
col += m[0].length;
|
||||
continue;
|
||||
}
|
||||
// if we reach here without successfully parsing a token, it's an error
|
||||
throw new Error(`Could not parse character "${exprStr[col]}" at position ${col}`);
|
||||
}
|
||||
tokens.push({ type: 'eof' });
|
||||
return new TokenList(tokens);
|
||||
}
|
||||
|
||||
// parse tokens into an ast
|
||||
function takeDisj(tokens) {
|
||||
const left = takeConj(tokens);
|
||||
if (tokens.here.type === 'sym' && tokens.here.sym === '|') {
|
||||
tokens.advance();
|
||||
const right = takeDisj(tokens);
|
||||
return (i, ie) => checkBool(left(i, ie)) || checkBool(right(i, ie));
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeConj(tokens) {
|
||||
const left = takeCmpEq(tokens);
|
||||
if (tokens.here.type === 'sym' && tokens.here.sym === '&') {
|
||||
tokens.advance();
|
||||
const right = takeConj(tokens);
|
||||
return (i, ie) => checkBool(left(i, ie)) && checkBool(right(i, ie));
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeCmpEq(tokens) {
|
||||
const left = takeCmpRel(tokens);
|
||||
if (tokens.here.type === 'sym') {
|
||||
switch (tokens.here.sym) {
|
||||
case '=': {
|
||||
tokens.advance();
|
||||
const right = takeCmpEq(tokens);
|
||||
return (i, ie) => {
|
||||
const a = left(i, ie), b = right(i, ie);
|
||||
if (typeof a !== typeof b) return false;
|
||||
switch (typeof a) {
|
||||
case 'number':
|
||||
return Math.abs(left(i, ie) - right(i, ie)) < 1e-4;
|
||||
case 'boolean':
|
||||
return a === b;
|
||||
case 'string':
|
||||
return a.toLowerCase() === b.toLowerCase();
|
||||
}
|
||||
throw new Error('???'); // wut
|
||||
};
|
||||
}
|
||||
case '!=': {
|
||||
tokens.advance();
|
||||
const right = takeCmpEq(tokens);
|
||||
return (i, ie) => {
|
||||
const a = left(i, ie), b = right(i, ie);
|
||||
if (typeof a !== typeof b) return false;
|
||||
switch (typeof a) {
|
||||
case 'number':
|
||||
return Math.abs(left(i, ie) - right(i, ie)) >= 1e-4;
|
||||
case 'boolean':
|
||||
return a !== b;
|
||||
case 'string':
|
||||
return a.toLowerCase() !== b.toLowerCase();
|
||||
}
|
||||
throw new Error('???'); // wtf
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeCmpRel(tokens) {
|
||||
const left = takeSum(tokens);
|
||||
if (tokens.here.type === 'sym') {
|
||||
switch (tokens.here.sym) {
|
||||
case '<=': {
|
||||
tokens.advance();
|
||||
const right = takeCmpRel(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) <= checkNum(right(i, ie));
|
||||
}
|
||||
case '<': {
|
||||
tokens.advance();
|
||||
const right = takeCmpRel(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) < checkNum(right(i, ie));
|
||||
}
|
||||
case '>': {
|
||||
tokens.advance();
|
||||
const right = takeCmpRel(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) > checkNum(right(i, ie));
|
||||
}
|
||||
case '>=': {
|
||||
tokens.advance();
|
||||
const right = takeCmpRel(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) >= checkNum(right(i, ie));
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeSum(tokens) {
|
||||
const left = takeProd(tokens);
|
||||
if (tokens.here.type === 'sym') {
|
||||
switch (tokens.here.sym) {
|
||||
case '+': {
|
||||
tokens.advance();
|
||||
const right = takeSum(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) + checkNum(right(i, ie));
|
||||
}
|
||||
case '-': {
|
||||
tokens.advance();
|
||||
const right = takeSum(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) - checkNum(right(i, ie));
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeProd(tokens) {
|
||||
const left = takeExp(tokens);
|
||||
if (tokens.here.type === 'sym') {
|
||||
switch (tokens.here.sym) {
|
||||
case '*': {
|
||||
tokens.advance();
|
||||
const right = takeProd(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) * checkNum(right(i, ie));
|
||||
}
|
||||
case '/': {
|
||||
tokens.advance();
|
||||
const right = takeProd(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) / checkNum(right(i, ie));
|
||||
}
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeExp(tokens) {
|
||||
const left = takeUnary(tokens);
|
||||
if (tokens.here.type === 'sym' && tokens.here.sym === '^') {
|
||||
tokens.advance();
|
||||
const right = takeExp(tokens);
|
||||
return (i, ie) => checkNum(left(i, ie)) ** checkNum(right(i, ie));
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
function takeUnary(tokens) {
|
||||
if (tokens.here.type === 'sym') {
|
||||
switch (tokens.here.sym) {
|
||||
case '-': {
|
||||
tokens.advance();
|
||||
const operand = takeUnary(tokens);
|
||||
return (i, ie) => -checkNum(operand(i, ie));
|
||||
}
|
||||
case '!': {
|
||||
tokens.advance();
|
||||
const operand = takeUnary(tokens);
|
||||
return (i, ie) => !checkBool(operand(i, ie));
|
||||
}
|
||||
}
|
||||
}
|
||||
return takePrim(tokens);
|
||||
}
|
||||
|
||||
function takePrim(tokens) {
|
||||
switch (tokens.here.type) {
|
||||
case 'num': {
|
||||
const lit = tokens.here.value;
|
||||
tokens.advance();
|
||||
return (i, ie) => lit;
|
||||
}
|
||||
case 'bool': {
|
||||
const lit = tokens.here.value;
|
||||
tokens.advance();
|
||||
return (i, ie) => lit;
|
||||
}
|
||||
case 'str': {
|
||||
const lit = tokens.here.value;
|
||||
tokens.advance();
|
||||
console.log(lit);
|
||||
return (i, ie) => lit;
|
||||
}
|
||||
case 'id':
|
||||
const id = tokens.here.id;
|
||||
tokens.advance();
|
||||
if (tokens.here.type === 'sym' && tokens.here.sym === '(') { // it's a function call
|
||||
tokens.advance();
|
||||
const argExprs = [];
|
||||
if (tokens.here.type !== 'sym' || tokens.here.sym !== ')') {
|
||||
arg_iter: // collect arg expressions, if there are any
|
||||
while (true) {
|
||||
argExprs.push(takeDisj(tokens));
|
||||
if (tokens.here.type === 'sym') {
|
||||
switch (tokens.here.sym) {
|
||||
case ')':
|
||||
tokens.advance();
|
||||
break arg_iter;
|
||||
case ',':
|
||||
tokens.advance();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw new Error(`Expected "," or ")", but got ${JSON.stringify(tokens.here)}`);
|
||||
}
|
||||
}
|
||||
const func = itemQueryFuncs[id.toLowerCase()];
|
||||
if (!func) throw new Error(`Unknown function: ${id}`);
|
||||
return (i, ie) => {
|
||||
const args = [];
|
||||
for (let k = 0; k < argExprs.length; k++) args.push(argExprs[k](i, ie));
|
||||
return func(args);
|
||||
};
|
||||
} else { // not a function call
|
||||
const prop = itemQueryProps[id.toLowerCase()];
|
||||
if (!prop) throw new Error(`Unknown property: ${id}`);
|
||||
return prop;
|
||||
}
|
||||
case 'sym':
|
||||
if (tokens.here.sym === '(') {
|
||||
tokens.advance();
|
||||
const expr = takeDisj(tokens);
|
||||
if (tokens.here.type !== 'sym' || tokens.here.sym !== ')') throw new Error('Bracket mismatch');
|
||||
tokens.advance();
|
||||
return expr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new Error(tokens.here.type === 'eof' ? 'Reached end of expression' : `Unexpected token: ${JSON.stringify(tokens.here)}`);
|
||||
}
|
||||
|
||||
// full compilation function, with extra safety for empty input strings
|
||||
return function(exprStr) {
|
||||
const tokens = tokenize(exprStr);
|
||||
return tokens.tokens.length <= 1 ? null : takeDisj(tokens);
|
||||
};
|
||||
})();
|
47
styles.css
47
styles.css
|
@ -24,14 +24,6 @@ div {
|
|||
padding: 0%;
|
||||
}
|
||||
|
||||
.equipment{
|
||||
padding: 0%;
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content min-content;
|
||||
gap: 5px;
|
||||
grid-template-rows: min-content min-content auto;
|
||||
}
|
||||
|
||||
.skillpoints{
|
||||
padding: 0% 4% 2%;
|
||||
display: grid;
|
||||
|
@ -92,13 +84,23 @@ table.center{
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.build-helmet, .build-chestplate, .build-leggings, .build-boots, .build-ring1, .build-ring2, .build-bracelet, .build-necklace, .build-weapon, .build-order, .build-overall, .build-melee-stats, .build-defense-stats, .spell-info, .set-info, .powder-special, .powder-special-stats, .int-info, .id-box, .box {
|
||||
.build-helmet, .build-chestplate, .build-leggings, .build-boots, .build-ring1, .build-ring2, .build-bracelet, .build-necklace, .build-weapon, .build-order, .build-overall, .build-melee-stats, .build-defense-stats, .spell-info, .set-info, .powder-special, .powder-special-stats, .int-info, .box {
|
||||
color: #aaa;
|
||||
background: #121516;
|
||||
border: 3px solid #BCBCBC;
|
||||
border-radius: 3px;
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
.id-box {
|
||||
color: #aaa;
|
||||
background: #121516;
|
||||
border: 3px solid #BCBCBC;
|
||||
border-radius: 3px;
|
||||
width: 96%;
|
||||
margin: 1em 0px 0px 0px;
|
||||
}
|
||||
|
||||
.crafter, .recipe-stats, .craft-stats, .ing-stats {
|
||||
color: #aaa;
|
||||
background: #121516;
|
||||
|
@ -131,6 +133,12 @@ table.center{
|
|||
margin: 2px 2%;
|
||||
padding: 0;
|
||||
}
|
||||
.powderLeft {
|
||||
margin-right: 0px;
|
||||
}
|
||||
.powderRight {
|
||||
margin-left: 0px;
|
||||
}
|
||||
.space {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
@ -387,3 +395,24 @@ button.toggleOn:hover {
|
|||
color: #ff0;
|
||||
background: #775;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
color: #aaa;
|
||||
background: #110110;
|
||||
width: min(200%, 75vw);
|
||||
text-align: center;
|
||||
border: 5px solid #BCBCBC;
|
||||
border-radius: 10px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
.recipe {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
|
80
update_merge.py
Normal file
80
update_merge.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
import json
|
||||
|
||||
with open("clean.json") as infile:
|
||||
olds = json.load(infile)
|
||||
|
||||
items = olds["items"]
|
||||
|
||||
item_oldnames_map = dict()
|
||||
item_newnames_map = dict()
|
||||
|
||||
VERSION_STR = " (1.20)"
|
||||
|
||||
max_old_id = 0
|
||||
|
||||
for item in items:
|
||||
item_id = item["id"]
|
||||
if "displayName" in item:
|
||||
displayName = item["displayName"]
|
||||
else:
|
||||
displayName = item["name"]
|
||||
item_name = displayName.replace(VERSION_STR, "")
|
||||
if item_id > 10000:
|
||||
map_name = item["name"].replace(VERSION_STR, "")
|
||||
item_newnames_map[map_name] = item
|
||||
item["displayName"] = item_name
|
||||
else:
|
||||
item_oldnames_map[item_name] = item
|
||||
if item_id > max_old_id:
|
||||
max_old_id = item_id
|
||||
|
||||
dummy_items = []
|
||||
|
||||
for (name, item) in item_newnames_map.items():
|
||||
if name in item_oldnames_map:
|
||||
old_item = item_oldnames_map[name]
|
||||
if "displayName" in item:
|
||||
displayName = item["displayName"].replace(VERSION_STR, "")
|
||||
else:
|
||||
displayName = name
|
||||
save_old = ["id","set","quest","drop","restrict", "name"]
|
||||
old_mappings = { k: old_item[k] for k in save_old if k in old_item }
|
||||
old_item.clear()
|
||||
|
||||
if "restrict" in item:
|
||||
del item["restrict"]
|
||||
|
||||
for k in item:
|
||||
old_item[k] = item[k]
|
||||
for k in old_mappings:
|
||||
old_item[k] = old_mappings[k]
|
||||
save_id = item["id"]
|
||||
item.clear()
|
||||
item["id"] = save_id
|
||||
item["name"] = str(save_id)
|
||||
item["remapID"] = old_item["id"]
|
||||
else:
|
||||
if "restrict" in item:
|
||||
in_str = input(name + " restriction: ").strip()
|
||||
if in_str:
|
||||
item["restrict"] = in_str
|
||||
else:
|
||||
del item["restrict"]
|
||||
item["name"] = name
|
||||
dummy_item = dict()
|
||||
dummy_item["id"] = item["id"]
|
||||
max_old_id += 1
|
||||
item["id"] = max_old_id
|
||||
dummy_item["remapID"] = item["id"]
|
||||
dummy_items.append(dummy_item)
|
||||
|
||||
items.extend(dummy_items)
|
||||
|
||||
sets = olds["sets"]
|
||||
|
||||
data = dict()
|
||||
data["items"] = items
|
||||
data["sets"] = sets
|
||||
|
||||
with open("updated.json", "w") as outfile:
|
||||
json.dump(data, outfile, indent=2)
|
260617
updated.json
Normal file
260617
updated.json
Normal file
File diff suppressed because it is too large
Load diff
13
wide.css
13
wide.css
|
@ -4,6 +4,15 @@
|
|||
.spell-info-container {
|
||||
grid-column:4;
|
||||
}
|
||||
|
||||
.equipment{
|
||||
padding: 0%;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
width: 50vw;
|
||||
gap: 5px;
|
||||
grid-template-rows: min-content min-content auto;
|
||||
}
|
||||
.sticky-box {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
@ -51,9 +60,9 @@
|
|||
}
|
||||
|
||||
.skpInput, .idInput {
|
||||
width: 90%;
|
||||
width: 95%;
|
||||
height: 7vw;
|
||||
max-height: 30px;
|
||||
max-height: 20px;
|
||||
}
|
||||
|
||||
.wide-space {
|
||||
|
|
Loading…
Reference in a new issue