Merge pull request #192 from hppeng-wynn/item-search-fix
Item search fix
This commit is contained in:
commit
2230a939f5
9 changed files with 661 additions and 980 deletions
|
@ -322,8 +322,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/query.js"></script>
|
||||
<script type="text/javascript" src="../js/query_2.js"></script>
|
||||
<script type="text/javascript" src="../js/utils.js"></script>
|
||||
<script type="text/javascript" src="../js/build_utils.js"></script>
|
||||
<script type="text/javascript" src="../js/icons.js"></script>
|
||||
|
@ -336,6 +334,5 @@
|
|||
<script type="text/javascript" src="../js/load.js"></script>
|
||||
<script type="text/javascript" src="../js/craft.js"></script>
|
||||
<script type="text/javascript" src="../js/crafter.js"></script>
|
||||
<script type="text/javascript" src="../js/expr_parser.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -158,12 +158,12 @@
|
|||
</div>
|
||||
<div class = "row">
|
||||
<div class = "col-auto">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "search-button" onclick = "doItemSearch()">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "search-button" onclick = "do_item_search()">
|
||||
Search!
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-auto">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "resetItemSearch()">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "reset_item_search()">
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
|
@ -184,10 +184,9 @@
|
|||
<script type="text/javascript" src="../js/display_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/display.js"></script>
|
||||
<script type="text/javascript" src="../js/query.js"></script>
|
||||
<script type="text/javascript" src="../js/query_2.js"></script>
|
||||
<script type="text/javascript" src="../js/expr_parser.js"></script>
|
||||
<script type="text/javascript" src="../js/load.js"></script>
|
||||
<script type="text/javascript" src="../js/sq2items.js"></script>
|
||||
<script type="text/javascript" src="../js/items.js"></script>
|
||||
<script type="text/javascript" src="../js/powders.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
<script type="text/javascript" src="/js/damage_calc.js"></script>
|
||||
<script type="text/javascript" src="/js/display_constants.js"></script>
|
||||
<script type="text/javascript" src="/js/display.js"></script>
|
||||
<script type="text/javascript" src="/js/query_2.js"></script>
|
||||
<script type="text/javascript" src="/js/query.js"></script>
|
||||
<script type="text/javascript" src="/js/expr_parser.js"></script>
|
||||
<script type="text/javascript" src="/js/load.js"></script>
|
||||
<script type="text/javascript" src="/js/items_2.js"></script>
|
||||
|
|
|
@ -70,7 +70,7 @@ const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set
|
|||
const all_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tome_types).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
|
||||
//weaponTypes.push("sword");
|
||||
//console.log(types)
|
||||
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tome_types);
|
||||
let item_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tome_types);
|
||||
|
||||
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
|
||||
let skpReqs = skp_order.map(x => x + "Req");
|
||||
|
|
158
js/items.js
158
js/items.js
|
@ -102,51 +102,53 @@ const special_mappings = {
|
|||
"No Defense Req": "f:defReq=0",
|
||||
};
|
||||
|
||||
let itemFilters = document.getElementById("filter-items");
|
||||
if (itemFilters) {
|
||||
for (let x in translate_mappings) {
|
||||
let el = document.createElement("option");
|
||||
el.value = x;
|
||||
itemFilters.appendChild(el);
|
||||
}
|
||||
for (let x in special_mappings) {
|
||||
let el = document.createElement("option");
|
||||
el.value = x;
|
||||
itemFilters.appendChild(el);
|
||||
}
|
||||
let item_filters = []
|
||||
for (let x in translate_mappings) {
|
||||
item_filters.push(x);
|
||||
}
|
||||
for (let x in special_mappings) {
|
||||
item_filters.push(x);
|
||||
}
|
||||
|
||||
let itemCategories = [ "armor", "accessory", "weapon" ];
|
||||
let item_categories = [ "armor", "accessory", "weapon" ];
|
||||
|
||||
function applyQuery(items, query) {
|
||||
return items.filter(query.filter, query).sort(query.compare);
|
||||
}
|
||||
|
||||
function displayItems(results) {
|
||||
let items_parent = document.getElementById("main");
|
||||
for (let i in results) {
|
||||
let item = results[i].itemExp;
|
||||
let box = document.createElement("div");
|
||||
box.classList.add("box");
|
||||
box.id = "item"+i;
|
||||
function displayItems(items_copy) {
|
||||
let items_parent = document.getElementById("search-results");
|
||||
for (let i in items_copy) {
|
||||
if (i > 200) {break;}
|
||||
let item = items_copy[i].itemExp;
|
||||
let box = make_elem('div', ['col-lg-3', 'col-sm-6', 'p-2'], {id: 'item'+i});
|
||||
//box.addEventListener("dblclick", function() {set_item(item);}); TODO: ??
|
||||
|
||||
let bckgrdbox = make_elem("div", ["dark-7", "rounded", "px-2", "col-auto"], {id: 'item'+i+'b'});
|
||||
box.append(bckgrdbox);
|
||||
items_parent.appendChild(box);
|
||||
displayExpandedItem(item, box.id);
|
||||
item.set("powders", []);
|
||||
if (item.get("category") == "weapon") {
|
||||
apply_weapon_powders(item);
|
||||
}
|
||||
displayExpandedItem(item, bckgrdbox.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
let searchDb;
|
||||
let search_db;
|
||||
let expr_parser;
|
||||
|
||||
function doItemSearch() {
|
||||
function do_item_search() {
|
||||
window.scrollTo(0, 0);
|
||||
let queries = [];
|
||||
queries.push('f:name?="'+document.getElementById("item-name-choice").value.trim()+'"');
|
||||
|
||||
let categoryOrType = document.getElementById("item-category-choice").value;
|
||||
if (itemTypes.includes(categoryOrType)) {
|
||||
queries.push('f:type="'+categoryOrType+'"');
|
||||
const cat_or_type = document.getElementById("item-category-choice").value;
|
||||
if (item_types.includes(cat_or_type)) {
|
||||
queries.push('f:type="'+cat_or_type+'"');
|
||||
}
|
||||
else if (itemCategories.includes(categoryOrType)) {
|
||||
queries.push('f:cat="'+categoryOrType+'"');
|
||||
else if (item_categories.includes(cat_or_type)) {
|
||||
queries.push('f:cat="'+cat_or_type+'"');
|
||||
}
|
||||
|
||||
let rarity = document.getElementById("item-rarity-choice").value;
|
||||
|
@ -162,7 +164,9 @@ function doItemSearch() {
|
|||
}
|
||||
}
|
||||
|
||||
let level_dat = document.getElementById("item-level-choice").value.split("-");
|
||||
let level_raw = document.getElementById("item-level-choice").value;
|
||||
if (!level_raw) { level_raw = '1-106'; };
|
||||
const level_dat = level_raw.split("-");
|
||||
queries.push('f:(lvl>='+parseInt(level_dat[0])+'&lvl<='+parseInt(level_dat[1])+')');
|
||||
|
||||
for (let i = 1; i <= 4; ++i) {
|
||||
|
@ -180,28 +184,27 @@ function doItemSearch() {
|
|||
}
|
||||
}
|
||||
|
||||
let filterQuery = "true";
|
||||
let sortQueries = [];
|
||||
let filter_query = "true";
|
||||
let sort_queries = [];
|
||||
console.log(queries);
|
||||
for (const query of queries) {
|
||||
if (query.startsWith("s:")) {
|
||||
sortQueries.push(query.slice(2));
|
||||
sort_queries.push(query.slice(2));
|
||||
}
|
||||
else if (query.startsWith("f:")) {
|
||||
filterQuery = filterQuery + "&" + query.slice(2);
|
||||
filter_query = filter_query + "&" + query.slice(2);
|
||||
}
|
||||
}
|
||||
console.log(filterQuery);
|
||||
console.log(sortQueries);
|
||||
document.getElementById("search-results").textContent = "";
|
||||
let results = [];
|
||||
try {
|
||||
const filterExpr = exprParser.parse(filterQuery);
|
||||
const sortExprs = sortQueries.map(q => exprParser.parse(q));
|
||||
for (let i = 0; i < searchDb.length; ++i) {
|
||||
const item = searchDb[i][0];
|
||||
const itemExp = searchDb[i][1];
|
||||
if (checkBool(filterExpr.resolve(item, itemExp))) {
|
||||
results.push({ item, itemExp, sortKeys: sortExprs.map(e => e.resolve(item, itemExp)) });
|
||||
const filter_expr = expr_parser.parse(filter_query);
|
||||
const sort_exprs = sort_queries.map(q => expr_parser.parse(q));
|
||||
for (let i = 0; i < search_db.length; ++i) {
|
||||
const item = search_db[i][0];
|
||||
const itemExp = search_db[i][1];
|
||||
if (checkBool(filter_expr.resolve(item, itemExp))) {
|
||||
results.push({ item, itemExp, sortKeys: sort_exprs.map(e => e.resolve(item, itemExp)) });
|
||||
}
|
||||
}
|
||||
results.sort((a, b) => {
|
||||
|
@ -211,13 +214,74 @@ function doItemSearch() {
|
|||
document.getElementById("summary").textContent = e.message;
|
||||
return;
|
||||
}
|
||||
document.getElementById("summary").textContent = results.length + " results."
|
||||
document.getElementById("summary").textContent = results.length + " results:"
|
||||
displayItems(results);
|
||||
}
|
||||
|
||||
function init_items() {
|
||||
searchDb = items.filter( i => ! i.remapID ).map( i => [i, expandItem(i, [])] );
|
||||
exprParser = new ExprParser(itemQueryProps, itemQueryFuncs);
|
||||
function reset_item_search() {
|
||||
const reset_fields = ["item-name-choice", "item-category-choice", "item-rarity-choice", "item-level-choice", "filter1-choice", "filter2-choice", "filter3-choice", "filter4-choice"]
|
||||
for (const field of reset_fields) {
|
||||
document.getElementById(field).value = "";
|
||||
}
|
||||
}
|
||||
|
||||
load_init(init_items);
|
||||
function init_items() {
|
||||
search_db = items.filter( i => ! i.remapID ).map( i => [i, expandItem(i, [])] );
|
||||
expr_parser = new ExprParser(itemQueryProps, itemQueryFuncs);
|
||||
//init dropdowns
|
||||
let filter_inputs = new Map([["item-category", ["ALL", "armor", "helmet", "chestplate", "leggings", "boots", "accessory", "ring", "bracelet", "necklace", "weapon", "wand", "spear", "bow", "dagger", "relik"]],
|
||||
["item-rarity", ["ANY", "Normal", "Unique", "Set", "Rare", "Legendary", "Fabled", "Mythic", "Sane"]],
|
||||
["filter1", item_filters],
|
||||
["filter2", item_filters],
|
||||
["filter3", item_filters],
|
||||
["filter4", item_filters]]);
|
||||
for (const [field, data] of filter_inputs) {
|
||||
let field_choice = document.getElementById(field+"-choice");
|
||||
// show dropdown on click
|
||||
field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));};
|
||||
filter_inputs.set(field, new autoComplete({
|
||||
data: {
|
||||
src: data,
|
||||
},
|
||||
threshold: 0,
|
||||
selector: "#"+ field +"-choice",
|
||||
wrapper: false,
|
||||
resultsList: {
|
||||
maxResults: 100,
|
||||
tabSelect: true,
|
||||
noResults: true,
|
||||
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
|
||||
element: (list, data) => {
|
||||
let position = document.getElementById(field+'-choice').getBoundingClientRect();
|
||||
list.style.top = position.bottom + window.scrollY +"px";
|
||||
list.style.left = position.x+"px";
|
||||
list.style.width = position.width+"px";
|
||||
list.style.maxHeight = position.height * 4 +"px";
|
||||
|
||||
if (!data.results.length) {
|
||||
const message = make_elem('li', ['scaled-font'], {textContent: "No results found!"});
|
||||
list.prepend(message);
|
||||
};
|
||||
},
|
||||
},
|
||||
resultItem: {
|
||||
class: "scaled-font search-item",
|
||||
selected: "dark-5",
|
||||
},
|
||||
events: {
|
||||
input: {
|
||||
selection: (event) => {
|
||||
if (event.detail.selection.value) {
|
||||
event.target.value = event.detail.selection.value;
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
(async function() {
|
||||
await Promise.resolve(load_init());
|
||||
init_items();
|
||||
})();
|
||||
|
|
|
@ -190,7 +190,7 @@ async function load_init() {
|
|||
}
|
||||
|
||||
// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon.
|
||||
for (const it of itemTypes) {
|
||||
for (const it of item_types) {
|
||||
itemLists.set(it, []);
|
||||
}
|
||||
|
||||
|
|
636
js/query.js
636
js/query.js
|
@ -1,115 +1,549 @@
|
|||
let queryTypeMap = new Map();
|
||||
|
||||
class NameQuery {
|
||||
constructor(string) { this.queryString = string.toLowerCase(); }
|
||||
|
||||
filter(item) {
|
||||
if (item.get("restrict") && item.get("restrict") === "DEPRECATED") {
|
||||
return false;
|
||||
}
|
||||
return (item.get("displayName").toLowerCase().includes(this.queryString));
|
||||
}
|
||||
|
||||
compare(a, b) { return a < b; }
|
||||
}
|
||||
queryTypeMap.set("name", function(s) { return new NameQuery(s); } );
|
||||
|
||||
class LevelRangeQuery {
|
||||
constructor(min, max) { this.min = min; this.max = max; }
|
||||
|
||||
filter(item) {
|
||||
if (item.get("remapID") === undefined) {
|
||||
return (item.get("lvl") <= this.max && item.get("lvl") >= this.min);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
compare(a, b) { return a > b; }
|
||||
// dynamic type casts
|
||||
function checkBool(v) {
|
||||
if (typeof v !== 'boolean') throw new Error(`Expected boolean, but got ${typeof v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
class NegateQuery {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
this.compare = function(a, b) { return 0; };
|
||||
}
|
||||
|
||||
filter(item) {
|
||||
return (!item.get(this.id)) || (item.get(this.id) == 0);
|
||||
}
|
||||
}
|
||||
queryTypeMap.set("null", function(s) { return new IdQuery(s); } );
|
||||
|
||||
class IdQuery {
|
||||
constructor(id) {
|
||||
this.id = id;
|
||||
if (nonRolledIDs.includes(id)) {
|
||||
this.compare = function(a, b) {
|
||||
return b.get(id) - a.get(id);
|
||||
};
|
||||
this.filter = function(a) {
|
||||
return a.get(this.id);
|
||||
}
|
||||
console.log("QUERY: ID, NONROLL");
|
||||
}
|
||||
else if (reversedIDs.includes(id)) {
|
||||
this.compare = function(a, b) {
|
||||
return a.get("maxRolls").get(id) - b.get("maxRolls").get(id);
|
||||
};
|
||||
this.filter = function(a) {
|
||||
return a.get("maxRolls").get(this.id);
|
||||
}
|
||||
console.log("QUERY: ID, REVERSE");
|
||||
}
|
||||
else {
|
||||
this.compare = function(a, b) {
|
||||
return b.get("maxRolls").get(id) - a.get("maxRolls").get(id);
|
||||
};
|
||||
this.filter = function(a) {
|
||||
return a.get("maxRolls").get(this.id);
|
||||
}
|
||||
console.log("QUERY: ID, ,,,");
|
||||
}
|
||||
}
|
||||
}
|
||||
queryTypeMap.set("stat", function(s) { return new IdQuery(s); } );
|
||||
|
||||
class IdMatchQuery {
|
||||
constructor(id, value) {
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
this.compare = function(a, b) {
|
||||
function checkNum(v) {
|
||||
if (typeof v === 'boolean') {
|
||||
if (v) return 1;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
filter(item) {
|
||||
return item.get(this.id) && (item.get(this.id) == this.value);
|
||||
}
|
||||
if (typeof v !== 'number') throw new Error(`Expected number, but got ${typeof v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
class SumQuery {
|
||||
constructor(ids) {
|
||||
let getters = [];
|
||||
for (const id of ids) {
|
||||
if (nonRolledIDs.includes(id)) {
|
||||
getters.push(a => a.get(id));
|
||||
function checkStr(v) {
|
||||
if (typeof v !== 'string') throw new Error(`Expected string, but got ${typeof v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
function checkComparable(v) {
|
||||
if (typeof v === 'boolean') throw new Error('Boolean is not comparable');
|
||||
return v;
|
||||
}
|
||||
|
||||
// properties of items that can be looked up
|
||||
// each entry is a function `(item, extended item) -> value`
|
||||
const itemQueryProps = (function() {
|
||||
const props = {};
|
||||
|
||||
function prop(names, type, resolve) {
|
||||
if (Array.isArray(names)) {
|
||||
for (name of names) {
|
||||
props[name] = { type, resolve };
|
||||
}
|
||||
} else {
|
||||
props[names] = { type, resolve };
|
||||
}
|
||||
else {
|
||||
getters.push(a => a.get("maxRolls").get(id));
|
||||
}
|
||||
|
||||
}
|
||||
this.compare = function(a, b) {
|
||||
let balance = 0;
|
||||
for (const getter of getters) {
|
||||
if (getter(a)) { balance -= getter(a); }
|
||||
if (getter(b)) { balance += getter(b); }
|
||||
}
|
||||
return balance;
|
||||
};
|
||||
function maxId(names, idKey) {
|
||||
prop(names, 'number', (i, ie) => ie.get('maxRolls').get(idKey) || 0);
|
||||
}
|
||||
|
||||
filter(item) {
|
||||
function minId(names, idKey) {
|
||||
prop(names, 'number', (i, ie) => ie.get('minRolls').get(idKey) || 0);
|
||||
}
|
||||
|
||||
function rangeAvg(names, getProp) {
|
||||
prop(names, 'number', (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, outType, f) {
|
||||
return prop(names, outType, (i, ie) => {
|
||||
const args = [];
|
||||
for (let k = 0; k < comps.length; k++) args.push(comps[k].resolve(i, ie));
|
||||
return f.apply(null, args);
|
||||
});
|
||||
}
|
||||
|
||||
function sum(names, ...comps) {
|
||||
return map(names, comps, 'number', (...summands) => {
|
||||
let total = 0;
|
||||
for (let i = 0; i < summands.length; i++) total += summands[i];
|
||||
return total;
|
||||
});
|
||||
}
|
||||
|
||||
prop('name', 'string', (i, ie) => i.displayName || i.name);
|
||||
prop('type', 'string', (i, ie) => i.type);
|
||||
prop(['cat', 'category'], 'string', (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'], 'string', (i, ie) => i.tier);
|
||||
prop(['rarity', 'tier'], 'number', (i, ie) => tierIndices[i.tier]);
|
||||
|
||||
prop(['level', 'lvl', 'combatlevel', 'combatlvl'], 'number', (i, ie) => i.lvl);
|
||||
prop(['strmin', 'strreq'], 'number', (i, ie) => i.strReq);
|
||||
prop(['dexmin', 'dexreq'], 'number', (i, ie) => i.dexReq);
|
||||
prop(['intmin', 'intreq'], 'number', (i, ie) => i.intReq);
|
||||
prop(['defmin', 'defreq'], 'number', (i, ie) => i.defReq);
|
||||
prop(['agimin', 'agireq'], 'number', (i, ie) => i.agiReq);
|
||||
sum(['summin', 'sumreq', 'totalmin', 'totalreq'], props.strmin, props.dexmin, props.intmin, props.defmin, props.agimin);
|
||||
|
||||
prop('str', 'number', (i, ie) => i.str);
|
||||
prop('dex', 'number', (i, ie) => i.dex);
|
||||
prop('int', 'number', (i, ie) => i.int);
|
||||
prop('def', 'number', (i, ie) => i.def);
|
||||
prop('agi', 'number', (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'], 'string', (i, ie) => i.atkSpd ? atkSpdIndices[i.atkSpd] : 0);
|
||||
maxId(['bonusattackspeed', 'bonusatkspd', 'attackspeedid', 'atkspdid', 'atktier'], 'atkTier');
|
||||
sum(['sumattackspeed', 'totalattackspeed', 'sumatkspd', 'totalatkspd', 'sumatktier', 'totalatktier'], props.atkspd, props.atktier);
|
||||
|
||||
prop(['earthdef', 'edef'], 'number', (i, ie) => i.eDef || 0);
|
||||
prop(['thunderdef', 'tdef'], 'number', (i, ie) => i.tDef || 0);
|
||||
prop(['waterdef', 'wdef'], 'number', (i, ie) => i.wDef || 0);
|
||||
prop(['firedef', 'fdef'], 'number', (i, ie) => i.fDef || 0);
|
||||
prop(['airdef', 'adef'], 'number', (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'], 'number', (i, ie) => i.hp || 0);
|
||||
maxId(['bonushealth', 'healthid', 'bonushp', 'hpid', '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');
|
||||
|
||||
maxId(['spellcost1', 'rawspellcost1', 'spcost1', 'spraw1'], 'spRaw1');
|
||||
maxId(['spellcost1%', 'spcost1%', 'sppct1'], 'spPct1');
|
||||
maxId(['spellcost2', 'rawspellcost2', 'spcost2', 'spraw2'], 'spRaw2');
|
||||
maxId(['spellcost2%', 'spcost2%', 'sppct2'], 'spPct2');
|
||||
maxId(['spellcost3', 'rawspellcost3', 'spcost3', 'spraw3'], 'spRaw3');
|
||||
maxId(['spellcost3%', 'spcost3%', 'sppct3'], 'spPct3');
|
||||
maxId(['spellcost4', 'rawspellcost4', 'spcost4', 'spraw4'], 'spRaw4');
|
||||
maxId(['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'], 'number', (i, ie) => i.slots || 0);
|
||||
|
||||
return props;
|
||||
})();
|
||||
|
||||
// functions that can be called in query expressions
|
||||
const itemQueryFuncs = {
|
||||
max: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, 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: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, 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: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to floor()');
|
||||
return Math.floor(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
ceil: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to ceil()');
|
||||
return Math.ceil(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
round: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to round()');
|
||||
return Math.round(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
sqrt: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to sqrt()');
|
||||
return Math.sqrt(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
abs: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to abs()');
|
||||
return Math.abs(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
contains: {
|
||||
type: 'boolean',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 2) throw new Error('Not enough args to contains()');
|
||||
return checkStr(args[0]).toLowerCase().includes(checkStr(args[1]).toLowerCase());
|
||||
}
|
||||
},
|
||||
atkspdmod: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, 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()');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// static type check
|
||||
function staticCheck(expType, term) {
|
||||
if (expType === 'any' || expType === term.type) {
|
||||
return true;
|
||||
}
|
||||
if (expType === 'number' && term.type === 'boolean') {
|
||||
return true;
|
||||
}
|
||||
throw new Error(`Expected ${expType}, but got ${term.type}`);
|
||||
}
|
||||
|
||||
// expression terms
|
||||
class Term {
|
||||
constructor(type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class LiteralTerm extends Term {
|
||||
constructor(type, value) {
|
||||
super(type);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
class BoolLitTerm extends LiteralTerm {
|
||||
constructor(value) {
|
||||
super('boolean', value);
|
||||
}
|
||||
}
|
||||
|
||||
class NumLitTerm extends LiteralTerm {
|
||||
constructor(value) {
|
||||
super('number', value);
|
||||
}
|
||||
}
|
||||
|
||||
class StrLitTerm extends LiteralTerm {
|
||||
constructor(value) {
|
||||
super('string', value);
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOpTerm extends Term {
|
||||
constructor(type, leftType, left, rightType, right) {
|
||||
super(type);
|
||||
staticCheck(leftType, left);
|
||||
staticCheck(rightType, right);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.apply(this.left.resolve(item, itemExt), this.right.resolve(item, itemExt));
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class LogicalTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'boolean', left, 'boolean', right);
|
||||
}
|
||||
}
|
||||
|
||||
class ConjTerm extends LogicalTerm {
|
||||
apply(a, b) {
|
||||
return a && b;
|
||||
}
|
||||
}
|
||||
|
||||
class DisjTerm extends LogicalTerm {
|
||||
apply(a, b) {
|
||||
return a || b;
|
||||
}
|
||||
}
|
||||
|
||||
class EqualityTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'any', left, 'any', right);
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
return (typeof a === 'string' && typeof b === 'string')
|
||||
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
|
||||
}
|
||||
|
||||
compare(a, b) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class EqTerm extends EqualityTerm {
|
||||
compare(a, b) {
|
||||
return a === b;
|
||||
}
|
||||
}
|
||||
|
||||
class NeqTerm extends EqualityTerm {
|
||||
compare(a, b) {
|
||||
return a !== b;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainsTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'string', left, 'string', right);
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
return a.toLowerCase().includes(b.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
class InequalityTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'any', left, 'any', right);
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
checkComparable(a);
|
||||
checkComparable(b);
|
||||
return (typeof a === 'string' && typeof b === 'string')
|
||||
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
|
||||
}
|
||||
|
||||
compare(a, b) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class LeqTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a <= b;
|
||||
}
|
||||
}
|
||||
|
||||
class LtTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a < b;
|
||||
}
|
||||
}
|
||||
|
||||
class GtTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a > b;
|
||||
}
|
||||
}
|
||||
|
||||
class GeqTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a >= b;
|
||||
}
|
||||
}
|
||||
|
||||
class ArithmeticTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('number', 'number', left, 'number', right);
|
||||
}
|
||||
}
|
||||
|
||||
class AddTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
class SubTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
}
|
||||
|
||||
class MulTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a * b;
|
||||
}
|
||||
}
|
||||
|
||||
class DivTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a / b;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a ** b;
|
||||
}
|
||||
}
|
||||
|
||||
class UnaryOpTerm extends Term {
|
||||
constructor(type, inType, inVal) {
|
||||
super(type);
|
||||
staticCheck(inType, inVal);
|
||||
this.inVal = inVal;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.apply(this.inVal.resolve(item, itemExt));
|
||||
}
|
||||
|
||||
apply(x) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class NegTerm extends UnaryOpTerm {
|
||||
constructor(inVal) {
|
||||
super('number', 'number', inVal);
|
||||
}
|
||||
|
||||
apply(x) {
|
||||
return -x;
|
||||
}
|
||||
}
|
||||
|
||||
class InvTerm extends UnaryOpTerm {
|
||||
constructor(inVal) {
|
||||
super('boolean', 'boolean', inVal);
|
||||
}
|
||||
|
||||
apply(x) {
|
||||
return !x;
|
||||
}
|
||||
}
|
||||
|
||||
class FnCallTerm extends Term {
|
||||
constructor(fn, argExprs) {
|
||||
super(fn.type);
|
||||
this.fn = fn;
|
||||
this.argExprs = argExprs;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
const argVals = [];
|
||||
for (const argExpr of this.argExprs) {
|
||||
argVals.push(argExpr.resolve(item, itemExt));
|
||||
}
|
||||
return this.fn.fn(item, itemExt, argVals);
|
||||
}
|
||||
}
|
||||
|
||||
class PropTerm extends Term {
|
||||
constructor(prop) {
|
||||
super(prop.type);
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.prop.resolve(item, itemExt);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
aKey = isNaN(aKey) ? 0 : aKey;
|
||||
bKey = isNaN(bKey) ? 0 : bKey;
|
||||
if (aKey < bKey) return 1;
|
||||
if (aKey > bKey) return -1;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Incomparable type ${typeof aKey}`);
|
||||
}
|
||||
}
|
||||
return ib.lvl - ia.lvl;
|
||||
}
|
||||
|
|
549
js/query_2.js
549
js/query_2.js
|
@ -1,549 +0,0 @@
|
|||
// dynamic 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 === 'boolean') {
|
||||
if (v) return 1;
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
function checkComparable(v) {
|
||||
if (typeof v === 'boolean') throw new Error('Boolean is not comparable');
|
||||
return v;
|
||||
}
|
||||
|
||||
// properties of items that can be looked up
|
||||
// each entry is a function `(item, extended item) -> value`
|
||||
const itemQueryProps = (function() {
|
||||
const props = {};
|
||||
|
||||
function prop(names, type, resolve) {
|
||||
if (Array.isArray(names)) {
|
||||
for (name of names) {
|
||||
props[name] = { type, resolve };
|
||||
}
|
||||
} else {
|
||||
props[names] = { type, resolve };
|
||||
}
|
||||
}
|
||||
|
||||
function maxId(names, idKey) {
|
||||
prop(names, 'number', (i, ie) => ie.get('maxRolls').get(idKey) || 0);
|
||||
}
|
||||
|
||||
function minId(names, idKey) {
|
||||
prop(names, 'number', (i, ie) => ie.get('minRolls').get(idKey) || 0);
|
||||
}
|
||||
|
||||
function rangeAvg(names, getProp) {
|
||||
prop(names, 'number', (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, outType, f) {
|
||||
return prop(names, outType, (i, ie) => {
|
||||
const args = [];
|
||||
for (let k = 0; k < comps.length; k++) args.push(comps[k].resolve(i, ie));
|
||||
return f.apply(null, args);
|
||||
});
|
||||
}
|
||||
|
||||
function sum(names, ...comps) {
|
||||
return map(names, comps, 'number', (...summands) => {
|
||||
let total = 0;
|
||||
for (let i = 0; i < summands.length; i++) total += summands[i];
|
||||
return total;
|
||||
});
|
||||
}
|
||||
|
||||
prop('name', 'string', (i, ie) => i.displayName || i.name);
|
||||
prop('type', 'string', (i, ie) => i.type);
|
||||
prop(['cat', 'category'], 'string', (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'], 'string', (i, ie) => i.tier);
|
||||
prop(['rarity', 'tier'], 'number', (i, ie) => tierIndices[i.tier]);
|
||||
|
||||
prop(['level', 'lvl', 'combatlevel', 'combatlvl'], 'number', (i, ie) => i.lvl);
|
||||
prop(['strmin', 'strreq'], 'number', (i, ie) => i.strReq);
|
||||
prop(['dexmin', 'dexreq'], 'number', (i, ie) => i.dexReq);
|
||||
prop(['intmin', 'intreq'], 'number', (i, ie) => i.intReq);
|
||||
prop(['defmin', 'defreq'], 'number', (i, ie) => i.defReq);
|
||||
prop(['agimin', 'agireq'], 'number', (i, ie) => i.agiReq);
|
||||
sum(['summin', 'sumreq', 'totalmin', 'totalreq'], props.strmin, props.dexmin, props.intmin, props.defmin, props.agimin);
|
||||
|
||||
prop('str', 'number', (i, ie) => i.str);
|
||||
prop('dex', 'number', (i, ie) => i.dex);
|
||||
prop('int', 'number', (i, ie) => i.int);
|
||||
prop('def', 'number', (i, ie) => i.def);
|
||||
prop('agi', 'number', (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'], 'string', (i, ie) => i.atkSpd ? atkSpdIndices[i.atkSpd] : 0);
|
||||
maxId(['bonusattackspeed', 'bonusatkspd', 'attackspeedid', 'atkspdid', 'atktier'], 'atkTier');
|
||||
sum(['sumattackspeed', 'totalattackspeed', 'sumatkspd', 'totalatkspd', 'sumatktier', 'totalatktier'], props.atkspd, props.atktier);
|
||||
|
||||
prop(['earthdef', 'edef'], 'number', (i, ie) => i.eDef || 0);
|
||||
prop(['thunderdef', 'tdef'], 'number', (i, ie) => i.tDef || 0);
|
||||
prop(['waterdef', 'wdef'], 'number', (i, ie) => i.wDef || 0);
|
||||
prop(['firedef', 'fdef'], 'number', (i, ie) => i.fDef || 0);
|
||||
prop(['airdef', 'adef'], 'number', (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'], 'number', (i, ie) => i.hp || 0);
|
||||
maxId(['bonushealth', 'healthid', 'bonushp', 'hpid', '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');
|
||||
|
||||
maxId(['spellcost1', 'rawspellcost1', 'spcost1', 'spraw1'], 'spRaw1');
|
||||
maxId(['spellcost1%', 'spcost1%', 'sppct1'], 'spPct1');
|
||||
maxId(['spellcost2', 'rawspellcost2', 'spcost2', 'spraw2'], 'spRaw2');
|
||||
maxId(['spellcost2%', 'spcost2%', 'sppct2'], 'spPct2');
|
||||
maxId(['spellcost3', 'rawspellcost3', 'spcost3', 'spraw3'], 'spRaw3');
|
||||
maxId(['spellcost3%', 'spcost3%', 'sppct3'], 'spPct3');
|
||||
maxId(['spellcost4', 'rawspellcost4', 'spcost4', 'spraw4'], 'spRaw4');
|
||||
maxId(['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'], 'number', (i, ie) => i.slots || 0);
|
||||
|
||||
return props;
|
||||
})();
|
||||
|
||||
// functions that can be called in query expressions
|
||||
const itemQueryFuncs = {
|
||||
max: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, 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: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, 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: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to floor()');
|
||||
return Math.floor(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
ceil: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to ceil()');
|
||||
return Math.ceil(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
round: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to round()');
|
||||
return Math.round(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
sqrt: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to sqrt()');
|
||||
return Math.sqrt(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
abs: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 1) throw new Error('Not enough args to abs()');
|
||||
return Math.abs(checkNum(args[0]));
|
||||
}
|
||||
},
|
||||
contains: {
|
||||
type: 'boolean',
|
||||
fn: function(item, itemExp, args) {
|
||||
if (args.length < 2) throw new Error('Not enough args to contains()');
|
||||
return checkStr(args[0]).toLowerCase().includes(checkStr(args[1]).toLowerCase());
|
||||
}
|
||||
},
|
||||
atkspdmod: {
|
||||
type: 'number',
|
||||
fn: function(item, itemExp, 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()');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// static type check
|
||||
function staticCheck(expType, term) {
|
||||
if (expType === 'any' || expType === term.type) {
|
||||
return true;
|
||||
}
|
||||
if (expType === 'number' && term.type === 'boolean') {
|
||||
return true;
|
||||
}
|
||||
throw new Error(`Expected ${expType}, but got ${term.type}`);
|
||||
}
|
||||
|
||||
// expression terms
|
||||
class Term {
|
||||
constructor(type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class LiteralTerm extends Term {
|
||||
constructor(type, value) {
|
||||
super(type);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
class BoolLitTerm extends LiteralTerm {
|
||||
constructor(value) {
|
||||
super('boolean', value);
|
||||
}
|
||||
}
|
||||
|
||||
class NumLitTerm extends LiteralTerm {
|
||||
constructor(value) {
|
||||
super('number', value);
|
||||
}
|
||||
}
|
||||
|
||||
class StrLitTerm extends LiteralTerm {
|
||||
constructor(value) {
|
||||
super('string', value);
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOpTerm extends Term {
|
||||
constructor(type, leftType, left, rightType, right) {
|
||||
super(type);
|
||||
staticCheck(leftType, left);
|
||||
staticCheck(rightType, right);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.apply(this.left.resolve(item, itemExt), this.right.resolve(item, itemExt));
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class LogicalTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'boolean', left, 'boolean', right);
|
||||
}
|
||||
}
|
||||
|
||||
class ConjTerm extends LogicalTerm {
|
||||
apply(a, b) {
|
||||
return a && b;
|
||||
}
|
||||
}
|
||||
|
||||
class DisjTerm extends LogicalTerm {
|
||||
apply(a, b) {
|
||||
return a || b;
|
||||
}
|
||||
}
|
||||
|
||||
class EqualityTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'any', left, 'any', right);
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
return (typeof a === 'string' && typeof b === 'string')
|
||||
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
|
||||
}
|
||||
|
||||
compare(a, b) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class EqTerm extends EqualityTerm {
|
||||
compare(a, b) {
|
||||
return a === b;
|
||||
}
|
||||
}
|
||||
|
||||
class NeqTerm extends EqualityTerm {
|
||||
compare(a, b) {
|
||||
return a !== b;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainsTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'string', left, 'string', right);
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
return a.toLowerCase().includes(b.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
class InequalityTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('boolean', 'any', left, 'any', right);
|
||||
}
|
||||
|
||||
apply(a, b) {
|
||||
checkComparable(a);
|
||||
checkComparable(b);
|
||||
return (typeof a === 'string' && typeof b === 'string')
|
||||
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
|
||||
}
|
||||
|
||||
compare(a, b) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class LeqTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a <= b;
|
||||
}
|
||||
}
|
||||
|
||||
class LtTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a < b;
|
||||
}
|
||||
}
|
||||
|
||||
class GtTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a > b;
|
||||
}
|
||||
}
|
||||
|
||||
class GeqTerm extends InequalityTerm {
|
||||
compare(a, b) {
|
||||
return a >= b;
|
||||
}
|
||||
}
|
||||
|
||||
class ArithmeticTerm extends BinaryOpTerm {
|
||||
constructor(left, right) {
|
||||
super('number', 'number', left, 'number', right);
|
||||
}
|
||||
}
|
||||
|
||||
class AddTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
class SubTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
}
|
||||
|
||||
class MulTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a * b;
|
||||
}
|
||||
}
|
||||
|
||||
class DivTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a / b;
|
||||
}
|
||||
}
|
||||
|
||||
class ExpTerm extends ArithmeticTerm {
|
||||
apply(a, b) {
|
||||
return a ** b;
|
||||
}
|
||||
}
|
||||
|
||||
class UnaryOpTerm extends Term {
|
||||
constructor(type, inType, inVal) {
|
||||
super(type);
|
||||
staticCheck(inType, inVal);
|
||||
this.inVal = inVal;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.apply(this.inVal.resolve(item, itemExt));
|
||||
}
|
||||
|
||||
apply(x) {
|
||||
throw new Error('Abstract method!');
|
||||
}
|
||||
}
|
||||
|
||||
class NegTerm extends UnaryOpTerm {
|
||||
constructor(inVal) {
|
||||
super('number', 'number', inVal);
|
||||
}
|
||||
|
||||
apply(x) {
|
||||
return -x;
|
||||
}
|
||||
}
|
||||
|
||||
class InvTerm extends UnaryOpTerm {
|
||||
constructor(inVal) {
|
||||
super('boolean', 'boolean', inVal);
|
||||
}
|
||||
|
||||
apply(x) {
|
||||
return !x;
|
||||
}
|
||||
}
|
||||
|
||||
class FnCallTerm extends Term {
|
||||
constructor(fn, argExprs) {
|
||||
super(fn.type);
|
||||
this.fn = fn;
|
||||
this.argExprs = argExprs;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
const argVals = [];
|
||||
for (const argExpr of this.argExprs) {
|
||||
argVals.push(argExpr.resolve(item, itemExt));
|
||||
}
|
||||
return this.fn.fn(item, itemExt, argVals);
|
||||
}
|
||||
}
|
||||
|
||||
class PropTerm extends Term {
|
||||
constructor(prop) {
|
||||
super(prop.type);
|
||||
this.prop = prop;
|
||||
}
|
||||
|
||||
resolve(item, itemExt) {
|
||||
return this.prop.resolve(item, itemExt);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
aKey = isNaN(aKey) ? 0 : aKey;
|
||||
bKey = isNaN(bKey) ? 0 : bKey;
|
||||
if (aKey < bKey) return 1;
|
||||
if (aKey > bKey) return -1;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Incomparable type ${typeof aKey}`);
|
||||
}
|
||||
}
|
||||
return ib.lvl - ia.lvl;
|
||||
}
|
264
js/sq2items.js
264
js/sq2items.js
|
@ -1,264 +0,0 @@
|
|||
let itemCategories = [ "armor", "accessory", "weapon" ];
|
||||
|
||||
const sq2_translate_mappings = {
|
||||
//"Name": "name",
|
||||
//"Display Name": "displayName",
|
||||
//"tier"Tier": ",
|
||||
//"Set": "set",
|
||||
"Powder Slots": "slots",
|
||||
//"Type": "type",
|
||||
//"armorType", (deleted)
|
||||
//"color", (deleted)
|
||||
//"lore", (deleted)
|
||||
//"material", (deleted)
|
||||
"Drop type": "drop",
|
||||
"Quest requirement": "quest",
|
||||
"Restriction": "restrict",
|
||||
//"Base Neutral Damage": "nDam",
|
||||
//"Base Fire Damage": "fDam",
|
||||
//"Base Water Damage": "wDam",
|
||||
//"Base Air Damage": "aDam",
|
||||
//"Base Thunder Damage": "tDam",
|
||||
//"Base Earth Damage": "eDam",
|
||||
//"Base Attack Speed": "atkSpd",
|
||||
"Health": "hp",
|
||||
"Raw Fire Defense": "fDef",
|
||||
"Raw Water Defense": "wDef",
|
||||
"Raw Air Defense": "aDef",
|
||||
"Raw Thunder Defense": "tDef",
|
||||
"Raw Earth Defense": "eDef",
|
||||
"Combat Level": "lvl",
|
||||
//"Class Requirement": "classReq",
|
||||
"Req Strength": "strReq",
|
||||
"Req Dexterity": "dexReq",
|
||||
"Req Intelligence": "intReq",
|
||||
"Req Agility": "agiReq",
|
||||
"Req Defense": "defReq",
|
||||
"% Health Regen": "hprPct",
|
||||
"Mana Regen": "mr",
|
||||
"% Spell Damage": "sdPct",
|
||||
"% Melee Damage": "mdPct",
|
||||
"Life Steal": "ls",
|
||||
"Mana Steal": "ms",
|
||||
"XP Bonus": "xpb",
|
||||
"Loot Bonus": "lb",
|
||||
"Reflection": "ref",
|
||||
"Strength": "str",
|
||||
"Dexterity": "dex",
|
||||
"Intelligence": "int",
|
||||
"Agility": "agi",
|
||||
"Defense": "def",
|
||||
"Thorns": "thorns",
|
||||
"Exploding": "expd",
|
||||
"Walk Speed": "spd",
|
||||
"Attack Speed Bonus": "atkTier",
|
||||
"Poison": "poison",
|
||||
"Health Bonus": "hpBonus",
|
||||
"Soul Point Regen": "spRegen",
|
||||
"Stealing": "eSteal",
|
||||
"Raw Health Regen": "hprRaw",
|
||||
"Raw Spell": "sdRaw",
|
||||
"Raw Melee": "mdRaw",
|
||||
"% Fire Damage": "fDamPct",
|
||||
"% Water Damage": "wDamPct",
|
||||
"% Air Damage": "aDamPct",
|
||||
"% Thunder Damage": "tDamPct",
|
||||
"% Earth Damage": "eDamPct",
|
||||
"% Fire Defense": "fDefPct",
|
||||
"% Water Defense": "wDefPct",
|
||||
"% Air Defense": "aDefPct",
|
||||
"% Thunder Defense": "tDefPct",
|
||||
"% Earth Defense": "eDefPct",
|
||||
"Fixed IDs": "fixID",
|
||||
"Custom Skin": "skin",
|
||||
//"Item Category": "category",
|
||||
|
||||
"1st Spell Cost %": "spPct1",
|
||||
"1st Spell Cost Raw": "spRaw1",
|
||||
"2nd Spell Cost %": "spPct2",
|
||||
"2nd Spell Cost Raw": "spRaw2",
|
||||
"3rd Spell Cost %": "spPct3",
|
||||
"3rd Spell Cost Raw": "spRaw3",
|
||||
"4th Spell Cost %": "spPct4",
|
||||
"4th Spell Cost Raw": "spRaw4",
|
||||
|
||||
"Rainbow Spell Damage": "rainbowRaw",
|
||||
"Sprint": "sprint",
|
||||
"Sprint Regen": "sprintReg",
|
||||
"Jump Height": "jh",
|
||||
"Loot Quality": "lq",
|
||||
|
||||
"Gather XP Bonus": "gXp",
|
||||
"Gather Speed Bonus": "gSpd",
|
||||
};
|
||||
|
||||
const sq2_special_mappings = {
|
||||
"Sum (skill points)": new SumQuery(["str", "dex", "int", "def", "agi"]),
|
||||
"Sum (Mana Sustain)": new SumQuery(["mr", "ms"]),
|
||||
"Sum (Life Sustain)": new SumQuery(["hpr", "ls"]),
|
||||
"Sum (Health + Health Bonus)": new SumQuery(["hp", "hpBonus"]),
|
||||
"No Strength Req": new NegateQuery("strReq"),
|
||||
"No Dexterity Req": new NegateQuery("dexReq"),
|
||||
"No Intelligence Req": new NegateQuery("intReq"),
|
||||
"No Agility Req": new NegateQuery("agiReq"),
|
||||
"No Defense Req": new NegateQuery("defReq"),
|
||||
};
|
||||
|
||||
let sq2ItemFilters = []
|
||||
for (let x in sq2_translate_mappings) {
|
||||
sq2ItemFilters.push(x);
|
||||
}
|
||||
for (let x in sq2_special_mappings) {
|
||||
sq2ItemFilters.push(x);
|
||||
}
|
||||
|
||||
function applyQuery(items, query) {
|
||||
return items.filter(query.filter, query).sort(query.compare);
|
||||
}
|
||||
|
||||
function displayItems(items_copy) {
|
||||
let items_parent = document.getElementById("search-results");
|
||||
for (let i in items_copy) {
|
||||
if (i > 200) {break;}
|
||||
let item = items_copy[i];
|
||||
let box = document.createElement("div");
|
||||
box.classList.add("col-lg-3", "col-sm-6", "p-2");
|
||||
box.id = "item"+i;
|
||||
box.addEventListener("dblclick", function() {set_item(item);});
|
||||
|
||||
let bckgrdbox = document.createElement("div");
|
||||
bckgrdbox.classList.add("dark-7", "rounded", "px-2", "col-auto");
|
||||
box.appendChild(bckgrdbox);
|
||||
bckgrdbox.id = "item"+i+"b";
|
||||
items_parent.appendChild(box);
|
||||
item.set("powders", []);
|
||||
if (item.get("category") == "weapon") {
|
||||
apply_weapon_powders(item);
|
||||
}
|
||||
displayExpandedItem(item, bckgrdbox.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
let items_expanded;
|
||||
|
||||
function doItemSearch() {
|
||||
// window.scrollTo(0, 0);
|
||||
let queries = [];
|
||||
queries.push(new NameQuery(document.getElementById("item-name-choice").value.trim()));
|
||||
|
||||
let categoryOrType = document.getElementById("item-category-choice").value;
|
||||
if (itemTypes.includes(categoryOrType)) {
|
||||
queries.push(new IdMatchQuery("type", categoryOrType));
|
||||
}
|
||||
else if (itemCategories.includes(categoryOrType)) {
|
||||
queries.push(new IdMatchQuery("category", categoryOrType));
|
||||
}
|
||||
|
||||
let rarity = document.getElementById("item-rarity-choice").value;
|
||||
if (rarity) {
|
||||
if (rarity === "ANY") {
|
||||
|
||||
}
|
||||
else {
|
||||
queries.push(new IdMatchQuery("tier", rarity));
|
||||
}
|
||||
}
|
||||
|
||||
let level_dat = document.getElementById("item-level-choice").value ? document.getElementById("item-level-choice").value.split("-") : [1, 106];
|
||||
queries.push(new LevelRangeQuery(parseInt(level_dat[0]), parseInt(level_dat[1])));
|
||||
|
||||
for (let i = 1; i <= 4; ++i) {
|
||||
let raw_dat = document.getElementById("filter"+i+"-choice").value;
|
||||
let filter_dat = sq2_translate_mappings[raw_dat];
|
||||
if (filter_dat !== undefined) {
|
||||
queries.push(new IdQuery(filter_dat));
|
||||
continue;
|
||||
}
|
||||
filter_dat = sq2_special_mappings[raw_dat];
|
||||
if (filter_dat !== undefined) {
|
||||
queries.push(filter_dat);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let items_copy = items_expanded.slice();
|
||||
document.getElementById("search-results").textContent = "";
|
||||
for (const query of queries) {
|
||||
console.log(items_copy.length);
|
||||
console.log(query, query.filter);
|
||||
items_copy = applyQuery(items_copy, query);
|
||||
console.log(items_copy.length);
|
||||
}
|
||||
document.getElementById("summary").textContent = items_copy.length + " results:"
|
||||
displayItems(items_copy);
|
||||
}
|
||||
|
||||
function resetItemSearch() {
|
||||
resetFields = ["item-name-choice", "item-category-choice", "item-rarity-choice", "item-level-choice", "filter1-choice", "filter2-choice", "filter3-choice", "filter4-choice"]
|
||||
for (const field of resetFields) {
|
||||
document.getElementById(field).value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function init_items() {
|
||||
items_expanded = items.filter( (i) => !("remapID" in i) ).map( (i) => expandItem(i) );
|
||||
|
||||
//init dropdowns
|
||||
let filterInputs = new Map([["item-category", ["ALL", "armor", "helmet", "chestplate", "leggings", "boots", "accessory", "ring", "bracelet", "necklace", "weapon", "wand", "spear", "bow", "dagger", "relik"]],
|
||||
["item-rarity", ["ANY", "Normal", "Unique", "Set", "Rare", "Legendary", "Fabled", "Mythic", "Sane"]],
|
||||
["filter1", sq2ItemFilters],
|
||||
["filter2", sq2ItemFilters],
|
||||
["filter3", sq2ItemFilters],
|
||||
["filter4", sq2ItemFilters]]);
|
||||
for (const [field, data] of filterInputs) {
|
||||
let field_choice = document.getElementById(field+"-choice");
|
||||
// show dropdown on click
|
||||
field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));};
|
||||
filterInputs.set(field, new autoComplete({
|
||||
data: {
|
||||
src: data,
|
||||
},
|
||||
threshold: 0,
|
||||
selector: "#"+ field +"-choice",
|
||||
wrapper: false,
|
||||
resultsList: {
|
||||
maxResults: 100,
|
||||
tabSelect: true,
|
||||
noResults: true,
|
||||
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
|
||||
element: (list, data) => {
|
||||
let position = document.getElementById(field+'-choice').getBoundingClientRect();
|
||||
list.style.top = position.bottom + window.scrollY +"px";
|
||||
list.style.left = position.x+"px";
|
||||
list.style.width = position.width+"px";
|
||||
list.style.maxHeight = position.height * 4 +"px";
|
||||
|
||||
if (!data.results.length) {
|
||||
message = document.createElement('li');
|
||||
message.classList.add('scaled-font');
|
||||
message.textContent = "No results found!";
|
||||
list.prepend(message);
|
||||
};
|
||||
},
|
||||
},
|
||||
resultItem: {
|
||||
class: "scaled-font search-item",
|
||||
selected: "dark-5",
|
||||
},
|
||||
events: {
|
||||
input: {
|
||||
selection: (event) => {
|
||||
if (event.detail.selection.value) {
|
||||
event.target.value = event.detail.selection.value;
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
(async function() {
|
||||
await Promise.resolve(load_init());
|
||||
init_items();
|
||||
})();
|
Loading…
Reference in a new issue