226 lines
24 KiB
HTML
226 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html scroll-behavior="smooth">
|
|
<head>
|
|
<title>WynnAtlas Help</title>
|
|
<link rel="icon" href="../media/icons/new/searcher.png">
|
|
<link rel="manifest" href="manifest.json">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
|
|
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
|
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
|
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
|
|
|
|
|
<link rel="stylesheet" href="../css/article.css">
|
|
<link rel="stylesheet" href="../css/styles.css">
|
|
<link rel="stylesheet" href="../css/sidebar.css">
|
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
|
</head>
|
|
<body class="all" style="overflow-y: scroll">
|
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
|
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
|
|
<a href = "../map/"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
|
|
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
|
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
|
|
<hr/>
|
|
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
|
|
</div>
|
|
<main>
|
|
<div class="full-width text-center">
|
|
<img src="./help_photo.png"/>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>What the heck is “Advanced Item Search”?</h2>
|
|
<p>The WynnBuilder team has been hard at work giving you the latest and greatest tools for optimizing your most complex Wynncraft builds. Now, we're introducing <strong class="rb-text">WynnAtlas</strong>, the new, bigger, better, smarter, powerful-er item guide! Featuring an extremely flexible expression language for filtering and sorting items, WynnAtlas' advanced item search system gives build engineers the power to select items with a high degree of granularity. Picking components for your brand-new Divzer WFA build has never been easier!</p>
|
|
</div>
|
|
<div class="section">
|
|
<h2>So, how am I supposed to use this thing?</h2>
|
|
<p>The advanced item search interface uses two separate expressions: one for filtering items and the other for sorting them. The idea is that you can filter for items matching a set of criteria that are absolutely necessary for the item you want, then sort the remaining items to find the optimal build component. The expressions themselves are mathematical formulae that compute values from the properties of items, such as their damage values and identifications.</p>
|
|
<p>As an example, suppose you want a helmet granting at least 10 intelligence that maximizes spell damage gain for a build using the Nepta Floodbringer. We would start by writing a filter for helmets with our desired bound on intelligence:</p>
|
|
<pre class="hl-expr">{prop:type} {op:=} {str:"helmet"} {op:&} {prop:int} {op:>=} {num:10}</pre>
|
|
<p>Then, we would write an expression that computes the amount of damage we get from the helmet. To do this, we first check the damage numbers for the Nepta Floodbringer: assuming we aren't using any powders, we have 96–112 water damage scaled by the super-fast attack speed modifier of 4.3, giving us a total of <span class="math">4.3 × (96 + 112) ÷ 2 = 447.2</span>. We then multiply in the spell damage and water damage modifiers and add raw spell damage, giving us:</p>
|
|
<pre class="hl-expr">{prop:sdRaw} {op:+} {num:447.2} {op:*} {op:(}{prop:sdPct} {op:+} {prop:wDamPct}{op:)} {op:/} {num:100}</pre>
|
|
<p>And, voilà! In the blink of an eye, we've discovered that Caesura is the best helmet for our criteria, granting 448.14 spell damage.</p>
|
|
</div>
|
|
<div class="section">
|
|
<h2>Cool! What kinds of things can I write?</h2>
|
|
<p>Roughly speaking, the expression language just consists of mathematical expressions with simple operations and functions. Each value in the expression is either a number, a boolean (true or false), or a string (a chunk of text). Numbers are floating-point rather than integers, so any kind of fractional value can also be expressed. String literals can use either single- or double-quotes, so the following is true:
|
|
<pre class="hl-expr">{str:"Hello, world!"} {op:=} {str:'Hello, world!'}</pre>
|
|
<p>The filtering expression should produce a boolean in the end, which represents whether an item matches the filter or not, and the sorting expression should produce either a number or a string, which are used to order the items. The sorting expression can even contain multiple sub-expressions separated by semicolons, which are then used to sort the result lexicographically. For example, if I want to sort items by loot bonus, then XP bonus if items have equal loot bonus, I could write:</p>
|
|
<pre class="hl-expr">{prop:lb}; {prop:xpb}</pre>
|
|
<p>The supported mathematical operations include the basic arithmetic operations as well as exponentiation. These operators obey the usual order of operations, with exponentiation having the highest priority. Parentheses are also supported for grouping.</p>
|
|
<pre class="hl-expr">({num:1} {op:+} {num:2}) {op:-} {num:3} {op:*} ({num:4} {op:/} {num:5}) {op:^} {num:6}</pre>
|
|
<p>The supported boolean operations include conjunction (“AND”) and disjunction (“OR”) using the <code>&</code> and <code>|</code> symbols, as well as unary inversion (“NOT”) using the <code>!</code> symbol. Disjunction has higher priority than conjunction, so the following is true:</p>
|
|
<pre class="hl-expr">{bool:true} {op:|} {bool:false} {op:&} {bool:true} {op:|} {bool:false} {op:=} {op:(}{bool:true} {op:|} {bool:false}{op:)} {op:&} {op:(}{bool:true} {op:|} {bool:false}{op:)}</pre>
|
|
<p>For comparisons, equality and non-equality are represented by the <code>=</code> and <code>!=</code> symbols. The two operands can be of any type, and the result will always be a boolean describing whether the operands are equal. String comparisons are case-insensitive.</p>
|
|
<pre class="hl-expr">{prop:type} {op:=} {str:"wand"} {op:|} {prop:cat} {op:!=} {str:"weapon"}</pre>
|
|
<p>The relational comparisons are given by <code><</code>, <code><=</code>, <code>>=</code>, and <code>></code>. The two operands must be of some comparable type—booleans cannot be compared, so it's an error to pass them to a relational comparison.</p>
|
|
<pre class="hl-expr">{prop:str} {op:>=} {num:20} {op:&} {prop:lvl} {op:<} {num:80}</pre>
|
|
<p>In addition to these comparison operators, there's also a “contains” operator <code>?=</code>, which checks whether a string contains a given substring or not. This allows for matching more loosely against strings; for example, if I wanted to see all the Hive master weapons, I could use the filter:</p>
|
|
<pre class="hl-expr">{prop:name} {op:?=} {str:"hive infused"}</pre>
|
|
<p>Some handy functions are also available, and can be called using a C-like syntax:</p>
|
|
<pre class="hl-expr">{fn:min}{op:(}{prop:int}{op:,} {num:105} {op:-} {prop:intReq}{op:)} {op:+} {fn:min}{op:(}{prop:def}{op:,} {num:80} {op:-} {prop:defReq}{op:)}</pre>
|
|
<p>Try some of these examples out in the item guide! Experimenting with weird and unusual expressions is a great way to get used to the syntax. The more you practice, the faster and more effective you'll be at using WynnAtlas to optimize your powerful Wynncraft builds!</p>
|
|
</div>
|
|
<div class="section">
|
|
<h2>What properties and functions do I get?</h2>
|
|
<p>What follows are lists of existing built-in properties and functions. Note that some aliases are omitted for properties with way too many aliases. If you want, you can also check out <a class="link" href="https://github.com/wynnbuilder/wynnbuilder.github.io/blob/master/expr_parser.md" target="_blank" rel="noreferrer">the source code</a> to see the actual declarations for all the built-in objects.</p>
|
|
<div id="docs-props" class="docs">
|
|
<h3>Built-in Properties</h3>
|
|
</div>
|
|
<div id="docs-fns" class="docs">
|
|
<h3>Built-in Functions</h3>
|
|
</div>
|
|
</div>
|
|
<div class="section">
|
|
<h2>Is there a formal specification?</h2>
|
|
<p>You can check out the <a class="link" href="https://github.com/wynnbuilder/wynnbuilder.github.io/blob/master/expr_parser.md" target="_blank" rel="noreferrer">implementation notes</a> for the expression parser, as well as the <a class="link" href="https://github.com/wynnbuilder/wynnbuilder.github.io/blob/master/js/expr_parser.js" target="_blank" rel="noreferrer">parser code itself</a>, over at the WynnBuilder GitHub repository. You can also ask around on the Atlas Inc. Discord server if you want more details.</p>
|
|
</div>
|
|
<div class="section">
|
|
<div class="footer">Written by <strong class="heart">Phanta</strong>, the same person who designed the advanced search system!</div>
|
|
</div>
|
|
</main>
|
|
<script type="text/javascript">
|
|
for (const codeBlk of document.getElementsByClassName('hl-expr')) {
|
|
codeBlk.innerHTML = codeBlk.innerText.replace(/{(\w+):([^}]+)}/g, (_, k, v) => `<span class="${k}">${v}</span>`);
|
|
}
|
|
function genDocEntry(id, type, aliases, desc) {
|
|
const entryElem = document.createElement('div');
|
|
entryElem.classList.add('docs-entry');
|
|
const keyElem = document.createElement('div');
|
|
keyElem.classList.add('docs-entry-key');
|
|
const idElem = document.createElement('span');
|
|
idElem.classList.add('docs-entry-key-id');
|
|
idElem.innerText = id;
|
|
keyElem.append(idElem);
|
|
const typeElem = document.createElement('span');
|
|
typeElem.classList.add('docs-entry-key-type');
|
|
typeElem.innerText = type;
|
|
keyElem.append(typeElem);
|
|
if (aliases != null) {
|
|
const aliasesElem = document.createElement('span');
|
|
aliasesElem.classList.add('docs-entry-key-alias');
|
|
aliasesElem.innerHTML = `(aliases: ${aliases.map(a => `<span class="alias">${a}</span>`).join(', ')})`;
|
|
keyElem.append(aliasesElem);
|
|
}
|
|
entryElem.append(keyElem);
|
|
const descElem = document.createElement('p');
|
|
descElem.innerHTML = desc.replace(/`([^`]+)`/g, (_, c) => `<code>${c}</code>`);
|
|
entryElem.append(descElem);
|
|
return entryElem;
|
|
}
|
|
const docsProps = document.getElementById('docs-props');
|
|
for (const entry of [
|
|
['name', 'string', null, 'The name of an item.'],
|
|
['type', 'string', null, 'The type of an item. Value is one of `"wand"`, `"bow"`, `"dagger"`, `"spear"`, `"relik"`, `"helmet"`, `"chestplate"`, `"leggings"`, `"boots"`, `"ring"`, `"bracelet"`, or `"necklace"`.'],
|
|
['category', 'string', ['cat'], 'The category of an item. Value is one of `"weapon"`, `"armor"`, or `"accessory"`.'],
|
|
['tierName', 'string', ['tierStr', 'rarityName', 'rarityStr'], 'The name of a tier. Value is one of `"normal"`, `"unique"`, `"set"`, `"rare"`, `"legendary"`, `"fabled"`, or `"mythic"`. For a comparable version, use the `tier` property instead.'],
|
|
['tier', 'number', ['rarity'], 'An integer representing an item\'s tier, where normal items have tier 0 and mythic items have tier 6. For a string representation of the tier, use the `tierName` property instead.'],
|
|
['level', 'number', ['lvl', 'combatLevel', 'combatLvl'], 'The combat level requirement for an item.'],
|
|
['strReq', 'number', ['strMin'], 'The strength requirement for an item.'],
|
|
['dexReq', 'number', ['dexMin'], 'The dexterity requirement for an item.'],
|
|
['intReq', 'number', ['intMin'], 'The intelligence requirement for an item.'],
|
|
['defReq', 'number', ['defMin'], 'The defence requirement for an item.'],
|
|
['agiReq', 'number', ['agiMin'], 'The agility requirement for an item.'],
|
|
['sumReq', 'number', ['sumMin, totalReq, totalMin'], 'The total skill point requirement for an item.'],
|
|
['str', 'number', null, 'The bonus strength imparted by an item.'],
|
|
['dex', 'number', null, 'The bonus dexterity imparted by an item.'],
|
|
['int', 'number', null, 'The bonus intelligence imparted by an item.'],
|
|
['def', 'number', null, 'The bonus defence imparted by an item.'],
|
|
['agi', 'number', null, 'The bonus agility imparted by an item.'],
|
|
['skillPoints', 'number', ['skillPts', 'attributes', 'attrs'], 'The total bonus skill points imparted by an item.'],
|
|
['neutralDmg', 'number', ['neutralDam', 'nDmg', 'nDam'], 'The average neutral damage on a weapon.'],
|
|
['earthDmg', 'number', ['earthDam', 'eDmg', 'eDam'], 'The average earth damage on a weapon.'],
|
|
['thunderDmg', 'number', ['thunderDam', 'tDmg', 'tDam'], 'The average thunder damage on a weapon.'],
|
|
['waterDmg', 'number', ['waterDam', 'wDmg', 'wDam'], 'The average water damage on a weapon.'],
|
|
['fireDmg', 'number', ['fireDam', 'fDmg', 'fDam'], 'The average fire damage on a weapon.'],
|
|
['airDmg', 'number', ['airDam', 'aDmg', 'aDam'], 'The average air damage on a weapon.'],
|
|
['sumDmg', 'number', ['sumDam', 'totalDmg', 'totalDam'], 'The total average damages on a weapon.'],
|
|
['earthDmg%', 'number', ['earthDam%', 'eDmg%', 'eDam%', 'eDamPct'], 'The bonus earth damage modifier on an item.'],
|
|
['thunderDmg%', 'number', ['thunderDam%', 'tDmg%', 'tDam%', 'tDamPct'], 'The bonus thunder damage modifier on an item.'],
|
|
['waterDmg%', 'number', ['waterDam%', 'wDmg%', 'wDam%', 'wDamPct'], 'The bonus water damage modifier on an item.'],
|
|
['fireDmg%', 'number', ['fireDam%', 'fDmg%', 'fDam%', 'fDamPct'], 'The bonus fire damage modifier on an item.'],
|
|
['airDmg%', 'number', ['airDam%', 'aDmg%', 'aDam%', 'aDamPct'], 'The bonus air damage modifier on an item.'],
|
|
['sumDmg%', 'number', ['sumDam%', 'totalDmg%', 'totalDam%', 'sumDamPct', 'totalDamPct'], 'The sum of the bonus elemental damage modifiers on an item.'],
|
|
['meleeDmg', 'number', ['meleeDam', 'meleeDmg%', 'meleeDam%', 'mdPct'], 'The bonus main-attack damage modifier on an item.'],
|
|
['meleeNeutralDmg', 'number', ['meleeRawDam', 'meleeNeutralDmg', 'meleeNeutralDam', 'mdRaw'], 'The neutral main-attack damage on an item.'],
|
|
['spellDmg', 'number', ['spellDam', 'spellDmg%', 'spellDam%', 'sdPct'], 'The bonus spell damage modifier on an item.'],
|
|
['spellNeutralDmg', 'number', ['spellRawDam', 'spellNeutralDam', 'sdRaw'], 'The neutral spell damage on an item.'],
|
|
['attackSpeed', 'string', ['atkSpd'], 'The attack speed on a weapon. For the bonus attack speed tiers granted by the identification, use `bonusAttackSpeed` instead.'],
|
|
['bonusAttackSpeed', 'number', ['bonusAtkSpd', 'attackSpeedId', 'AtkSpdId', 'AtkTier'], 'The bonus attack speed tiers granted by the attack speed identification. For the base attack speed of a weapon, use `attackSpeed` instead.'],
|
|
['sumAttackSpeed', 'number', ['totalAttackSpeed', 'sumAtkSpd', 'totalAtkSpd'], 'The total attack speed on an item, including both base weapon attack speed and bonus attack speed. Useful for items like Skien\'s Madness.'],
|
|
['earthDef', 'number', ['eDef'], 'The base earth defence afforded by an armor piece or accessory.'],
|
|
['thunderDef', 'number', ['tDef'], 'The base thunder defence afforded by an armor piece or accessory.'],
|
|
['waterDef', 'number', ['wDef'], 'The base water defence afforded by an armor piece or accessory.'],
|
|
['fireDef', 'number', ['fDef'], 'The base fire defence afforded by an armor piece or accessory.'],
|
|
['airDef', 'number', ['aDef'], 'The base air defence afforded by an armor piece or accessory.'],
|
|
['sumDef', 'number', ['totalDef'], 'The total base elemental defences afforded by an armor piece or accessory.'],
|
|
['earthDef%', 'number', ['eDef%', 'eDefPct'], 'The bonus earth defence modifier on an item.'],
|
|
['thunderDef%', 'number', ['tDef%', 'tDefPct'], 'The bonus thunder defence modifier on an item.'],
|
|
['waterDef%', 'number', ['wDef%', 'wDefPct'], 'The bonus water defence modifier on an item.'],
|
|
['fireDef%', 'number', ['fDef%', 'fDefPct'], 'The bonus fire defence modifier on an item.'],
|
|
['airDef%', 'number', ['aDef%', 'aDefPct'], 'The bonus air defence modifier on an item.'],
|
|
['sumDef%', 'number', ['totalDef%', 'sumDefPct', 'totalDefPct'], 'The sum of the bonus elemental defence modifiers on an item.'],
|
|
['health', 'number', ['hp'], 'The base health points granted by an armor piece or accessory. For the health identification, use `bonusHealth` instead.'],
|
|
['bonusHealth', 'number', ['healthId', 'bonusHp', 'hpId', 'hpBonus'], 'The bonus health points granted by the identification. For the flat base health on an armor piece or accessory, use `health` instead.'],
|
|
['sumHealth', 'number', ['sumHp', 'totalHealth', 'totalHp'], 'The total health granted by an item, including both base health and the identification. Useful for items like Adamantite.'],
|
|
['hpRegen', 'number', ['hpr', 'hr', 'hprRaw'], 'The flat health regeneration granted by an item.'],
|
|
['hpRegen%', 'number', ['hpr%', 'hr%', 'hprPct'], 'The bonus health regeneration modifier on an item.'],
|
|
['lifeSteal', 'number', ['ls'], 'The life steal on an item.'],
|
|
['manaRegen', 'number', ['mr'], 'The mana regeneration on an item.'],
|
|
['manaSteal', 'number', ['ms'], 'The mana steal on an item.'],
|
|
['walkSpeed', 'number', ['moveSpeed', 'ws', 'spd'], 'The walk speed on an item.'],
|
|
['sprint', 'number', null, 'The bonus sprint capacity modifier on an item.'],
|
|
['sprintRegen', 'number', ['sprintReg'], 'The sprint regeneration modifier on an item.'],
|
|
['jumpHeight', 'number', ['jh'], 'The increased jump height granted by an item.'],
|
|
['spellCost1', 'number', ['rawSpellCost1', 'spCost1', 'spRaw1'], 'The flat spell cost modifier for the first spell.'],
|
|
['spellCost1%', 'number', ['spCost1%', 'spPct1'], 'The percent spell cost modifier for the first spell.'],
|
|
['spellCost2', 'number', ['rawSpellCost2', 'spCost2', 'spRaw2'], 'The flat spell cost modifier for the second spell.'],
|
|
['spellCost2%', 'number', ['spCost2%', 'spPct2'], 'The percent spell cost modifier for the second spell.'],
|
|
['spellCost3', 'number', ['rawSpellCost3', 'spCost3', 'spRaw3'], 'The flat spell cost modifier for the third spell.'],
|
|
['spellCost3%', 'number', ['spCost3%', 'spPct3'], 'The percent spell cost modifier for the third spell.'],
|
|
['spellCost4', 'number', ['rawSpellCost4', 'spCost4', 'spRaw4'], 'The flat spell cost modifier for the fourth spell.'],
|
|
['spellCost4%', 'number', ['spCost4%', 'spPct4'], 'The percent spell cost modifier for the fourth spell.'],
|
|
['sumSpellCost', 'number', ['sumRawSpellCost', 'sumSpCost', 'sumSpRaw'], 'The sum of the percent spell cost modifiers on an item.'],
|
|
['sumSpellCost%', 'number', ['sumSpCost%', 'sumSpPct'], 'The sum of the flat spell cost modifiers on an item.'],
|
|
['exploding', 'number', ['expl', 'expd'], 'The explosion proc chance granted by an item.'],
|
|
['poison', 'number', null, 'The poison damage granted by an item.'],
|
|
['thorns', 'number', null, 'The thorns proc chance granted by an item.'],
|
|
['reflection', 'number', ['refl', 'ref'], 'The reflection proc chance granted by an item.'],
|
|
['soulPointRegen', 'number', ['spr', 'spRegen'], 'The bonus soul point regeneration on an item.'],
|
|
['lootBonus', 'number', ['lb'], 'The bonus loot modifier on an item.'],
|
|
['xpBonus', 'number', ['xpb', 'xb'], 'The bonus experience modifier on an item.'],
|
|
['stealing', 'number', ['esteal'], 'The emerald-stealing proc change granted by an item.'],
|
|
['powderSlots', 'number', ['powders', 'slots', 'sockets'], 'The number of available powder slots on an item.']
|
|
]) {
|
|
docsProps.append(genDocEntry(...entry));
|
|
}
|
|
const docsFns = document.getElementById('docs-fns');
|
|
for (const entry of [
|
|
['max(x1, x2[, x3...])', 'number', 'Takes the maximum of a set of numbers. Accepts any number of arguments greater than or equal to two.'],
|
|
['min(x1, x2[, x3...])', 'number', 'Takes the minimum of a set of numbers. Accepts any number of arguments greater than or equal to two.'],
|
|
['floor(x)', 'number', 'Rounds a number down to the greatest integer less than or equal to it.'],
|
|
['ceil(x)', 'number', 'Rounds a number up to the least integer greater than or equal to it.'],
|
|
['round(x)', 'number', 'Rounds a number to the nearest integer.'],
|
|
['sqrt(x)', 'number', 'Computes the square root of a number.'],
|
|
['abs(x)', 'number', 'Computes the absolute value of a number.'],
|
|
['contains(xs, x)', 'boolean', 'Checks whether a string contains a particular substring or not. Equivalent to the `?=` operator.'],
|
|
['atkSpdMod(x)', 'number', 'Gets the attack speed damage modifier for a given attack speed value. Works well with the `sumAttackSpeed` property.'],
|
|
]) {
|
|
docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2]));
|
|
}
|
|
</script>
|
|
<script type="text/javascript" src="../js/sq2icons.js"></script>
|
|
</body>
|
|
</html>
|