fix merge conflicts

This commit is contained in:
ferricles 2022-07-20 14:53:35 -07:00
commit 99d2b8273a
176 changed files with 13898 additions and 6637 deletions

19
LICENSE.txt Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2022 Wynnbuilder team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -18,7 +18,7 @@
-->
<title>WynnBuilder</title>
<link rel="icon" href="./media/memes/agony.png">
<link rel="icon" href="../media/icons/new/builder.png" type="image/icon type">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
@ -45,12 +45,15 @@
<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 = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "document.querySelector('#search-container').style.display = '';"><img src="../media/icons/new/searcher.png" alt="" title="Item Search"><b>WynnAtlas Mini</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>
<div class="container-fluid me-4" style="max-width: 95%; display: none">
<!-- REMOVE THIS DIV AT SOME POINT. -->
<div class = "row scaled-font mx-auto" id = "discord-banner-dev">
<div class = "col text-center item-title">Join the <a class = "link" href = "https://discord.gg/CGavnAnerv" target = "_blank">discord</a> today to suggest new features, submit bug reports, and hangout/talk to devs!</div>
</div>
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3">
<div class="col-xl-6">
<div class="row row-cols-1 mb-3 gy-4">
@ -407,6 +410,8 @@
</div>
<div class="col text-center">
<div id="summary-box"></div>
<div id="err-box"></div>
<div id="stack-box"></div>
<div id="str-warnings"></div>
<div id="dex-warnings"></div>
<div id="int-warnings"></div>
@ -428,7 +433,7 @@
</div>
<div class = "col-3 py-2">
<button class = "col-auto button rounded scaled-font fw-bold text-light dark-5" id = "toggle-atree" onclick = "toggle_tab('atree-dropdown'); toggleButton('toggle-atree')">
Show Ability Tree
Edit Abilities
</button>
</div>
<div class = "col-3 py-2">
@ -617,10 +622,14 @@
</div>
<div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;">
<div class="row row-cols-1 row-cols-xl-2">
<div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 500px; overflow-y: auto;">
<div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 90vh; overflow-y: auto; overflow-x: hidden;">
</div>
<div class="col mx-auto" id="atree-active">
<div class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs">
<div class="col mx-auto" style="height: 2em; overflow-y: auto;" id="atree-header">
</div>
<div class="col mx-auto" style="overflow-y: auto;" id="atree-active">
</div>
</div>
</div>
</div>
@ -965,7 +974,12 @@
</div>
<div class="col-auto p-1">
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
War Scream (+10%)
War Scream
</button>
</div>
<div class="col-auto p-1">
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+30%)
</button>
</div>
<div class="col-auto p-1">
@ -979,8 +993,8 @@
</button>
</div>
<div class="col-auto p-1">
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="bash-boost" onclick="update_boosts('bash-boost')">
Bash (+50%)
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="fortitude-boost" onclick="update_boosts('fortitude-boost')">
Fortitude (+60%)
</button>
</div>
</div>
@ -1045,11 +1059,6 @@
<div class="col eDam">
Rage (Passive)
</div>
<div class="col">
<input type = "range" class = "e_slider" id = "str_boost_armor" name = "str-boost-armor" autocomplete = "off" min = '0' max = '400' value = '0' step = '1' onchange = "update_armor_powder_specials('str_boost_armor')">
<input type="text" id="str_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
<label id = "str_boost_armor_label" for="str-boost-armor">% Earth Dmg Boost: 0</label>
</div>
</div>
</div>
<div class="col" id="dex-boost" style="display: none;">
@ -1089,11 +1098,6 @@
<div class="col tDam">
Kill Streak (Passive)
</div>
<div class="col">
<input type = "range" class = "t_slider" id = "dex_boost_armor" name = "dex-boost-armor" autocomplete = "off" min = '0' max = '200' value = '0' step = '1' onchange = "update_armor_powder_specials('dex_boost_armor')">
<input type="text" id="dex_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
<label id = "dex_boost_armor_label" for="dex-boost-armor">% Thunder Dmg Boost: 0</label>
</div>
</div>
</div>
<div class="col" id="int-boost">
@ -1133,11 +1137,6 @@
<div class="col wDam">
Concentration (Passive)
</div>
<div class="col">
<input type = "range" class = "w_slider" id = "int_boost_armor" name = "dex-boost-armor" autocomplete = "off" min = '0' max = '150' value = '0' step = '1' onchange = "update_armor_powder_specials('int_boost_armor')">
<input type="text" id="int_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
<label id = "int_boost_armor_label" for="dex-boost-armor">% Water Dmg Boost: 0</label>
</div>
</div>
</div>
<div class="col" id="def-boost" style="display: none;">
@ -1177,11 +1176,6 @@
<div class="col fDam">
Endurance (Passive)
</div>
<div class="col">
<input type = "range" class = "f_slider" id = "def_boost_armor" name = "def-boost-armor" autocomplete = "off" min = '0' max = '200' value = '0' step = '1' onchange = "update_armor_powder_specials('def_boost_armor')">
<input type="text" id="def_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
<label id = "def_boost_armor_label" for="def-boost-armor">% Fire Dmg Boost: 0</label>
</div>
</div>
</div>
<div class="col" id="agi-boost" style="display: none;">
@ -1221,11 +1215,6 @@
<div class="col aDam">
Dodge (Passive)
</div>
<div class="col">
<input type = "range" class = "a_slider" id = "agi_boost_armor" name = "agi-boost-armor" autocomplete = "off" min = '0' max = '150' value = '0' step = '1' onchange = "update_armor_powder_specials('agi_boost_armor')">
<input type="text" id="agi_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
<label id = "agi_boost_armor_label" for="agi-boost-armor">% Air Dmg Boost: 0</label>
</div>
</div>
</div>
</div>
@ -1263,21 +1252,10 @@
<div class = "col">
<div class = "col spell-display dark-5 rounded dark-shadow py-2 border border-dark" id="build-poison-stats">poison</div>
</div>
<div class = "col">
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell0-infoAvg">spell1</div>
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell0-info" style="display: none;">Spell 1</div>
<div id="all-spells-display" class="row row-cols-1 gy-3 text-center scaled-font pe-0">
<div class = "col pe-0">
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell0-infoAvg">Input a weapon to see abilities!</div>
</div>
<div class = "col">
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell1-infoAvg">spell2</div>
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell1-info" style="display: none;">Spell 2</div>
</div>
<div class = "col">
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell2-infoAvg">spell3</div>
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell2-info" style="display: none;">Spell 3</div>
</div>
<div class = "col">
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell3-infoAvg">spell4</div>
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell3-info" style="display: none;">Spell 4</div>
</div>
<div class = "col">
<div class = "spell-display dark-5 rounded dark-shadow py-2 border border-dark" id = "powder-special-stats"></div>
@ -1329,11 +1307,11 @@
<div class="col-12 dark-5 scaled-font">
<footer class="text-center">
<div id="header2">
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
<p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, and <b>reschan</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
</div>
<div id="credits">
<a href="credits.txt" class="link">Additional credits</a>
<a href="../credits.txt" class="link">Additional credits</a>
</div>
</footer>
</div>
@ -1393,7 +1371,6 @@
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/macy@2"></script>
<script type="text/javascript" src="../js/utils.js"></script>
@ -1404,15 +1381,9 @@
<script type="text/javascript" src="../js/powders.js"></script>
<script type="text/javascript" src="../js/skillpoints.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script>
<script type="text/javascript" src="../js/atree_constants_min.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.js"></script>
<script type="text/javascript" src="../js/query_2.js"></script>
<script type="text/javascript" src="../js/load.js"></script>
<script type="text/javascript" src="../js/load_ing.js"></script>
<script type="text/javascript" src="../js/load_tome.js"></script>
@ -1424,9 +1395,6 @@
<script type="text/javascript" src="../js/atree.js"></script>
<script type="text/javascript" src="../js/builder.js"></script>
<script type="text/javascript" src="../js/builder_graph.js"></script>
<script type="text/javascript" src="../js/expr_parser.js"></script>
<script type="text/javascript" src="../js/items.js"></script>
<script type="text/javascript" src="../js/sq2items.js"></script>
<script type="text/javascript" src="../js/optimize.js"></script>
<div id="graph_body" style="max-width: 100%; height: 100vh">
@ -1434,6 +1402,7 @@
<a id="saveLink">savelink</a>
</div>
<script src="https://d3js.org/d3.v7.js"></script>
<script type="text/javascript" src="../js/render_compute_graph.js"></script>
<script type="text/javascript" src="../js/debug/render_compute_graph.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

1279
builder/index_full.html Normal file

File diff suppressed because it is too large Load diff

4293
clean.json

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -31,32 +31,30 @@
<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>
<div class = "container py-5 vh-100 mx-0 mx-lg-auto">
<div class = "col">
<div class = "row g-3">
<div class = "col-lg-5 col-sm-12 text-center">
<div class = "row gx-5 mb-2">
<div class = "col-lg-6 col-sm-12">
<div id = "recipe-dropdown" class = "row h-100 dark-shadow dark-6 rounded">
<div class="container mt-5">
<div class="row row-cols-1 row-cols-lg-3 gy-5">
<div class="col col-lg-5">
<!--crafter ui-->
<div class="row row-cols-1 row-cols-lg-2 gx-5 gy-4 gy-lg-2">
<div class="col" id="recipe-dropdown">
<div class="row dark-shadow dark-5 rounded">
<div id = "recipe-img-loc" class = "col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon">
<img id = "recipe-img" class = "img-fluid rounded Crafted-shadow" src = "../media/items/new/generic-potion.png">
</div>
<div class = "col px-0">
<div class = "row align-items-center">
<div class = "col ps-3">
<div class = "row row-cols-2 align-items-center">
<div class = "col-4 px-0">
<p class = "text-right mb-0 scaled-font fw-bold">Type:</p>
<p class = "mb-0 scaled-font fw-bold">Type:</p>
</div>
<div class = "col-7 px-0">
<div class = "col-8 px-0">
<input class="recipeinput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="recipe-choices" id="recipe-choice" name="recipe-choice" placeholder="Potion"/>
<datalist id="recipe-choices">
</datalist>
</div>
</div>
<div class = "row align-items-center">
<div class = "col-4 px-0">
<p class = "text-right mb-0 scaled-font fw-bold">Lv:</p>
<p class = "mb-0 scaled-font fw-bold">Lv:</p>
</div>
<div class = "col-7 px-0">
<div class = "col-8 px-0">
<input class="levelinput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="level-choices" id="level-choice" name="level-choice" placeholder="103-105" />
<datalist id="level-choices">
</datalist>
@ -65,24 +63,24 @@
</div>
</div>
</div>
<div class = "col-lg-6 col-sm-12">
<div id = "atkSpdChoices" class = "row h-100 dark-shadow dark-6 rounded">
<div class = "col py-2">
<div class = "row h-50 align-items-center">
<p class = "text-right mb-0 scaled-font fw-bold">Attack Speed</p>
<div class="col">
<div id = "atkSpdChoices" class = "row row-cols-1 dark-shadow dark-5 rounded">
<div class="col pt-1">
<p class = "text-center scaled-font fw-bold mb-1">Attack Speed</p>
</div>
<div class = "row h-50">
<div class = "col-4 pl-1">
<div class="col mb-2">
<div class="row justify-content-center">
<div class="col-auto px-2">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "slow-atk-button" onclick = "toggleAtkSpd('slow-atk-button')">
Slow
</button>
</div>
<div class = "col-4 px-0">
<div class="col-auto px-2">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "normal-atk-button" onclick = "toggleAtkSpd('normal-atk-button')">
Normal
</button>
</div>
<div class = "col-4 pr-1">
<div class="col-auto px-2">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "fast-atk-button" onclick = "toggleAtkSpd('fast-atk-button')">
Fast
</button>
@ -91,12 +89,10 @@
</div>
</div>
</div>
</div>
<div class = "row gx-5 mb-2">
<div class = "col-lg-6 col-sm-12 justify-content-center">
<div class="col">
<div class = "row dark-shadow dark-6 rounded py-1 align-items-center">
<div class = "col-6 px-0">
<p class = "mb-0 scaled-font fw-bold" id = "mat-1">Mat 1 Tier:</p>
<p class = "mb-0 scaled-font fw-bold text-center" id = "mat-1">Mat 1 Tier:</p>
</div>
<div class = "col px-0">
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-1" onclick = "toggleMaterial('mat-1-1')">1</button>
@ -109,10 +105,10 @@
</div>
</div>
</div>
<div class = "col-lg-6 col-sm-12 justify-content-center">
<div class="col">
<div class = "row dark-shadow dark-6 rounded py-1 align-items-center">
<div class = "col-6 px-0">
<p class = "mb-0 scaled-font fw-bold" id = "mat-2">Mat 2 Tier:</p>
<p class = "mb-0 scaled-font fw-bold text-center" id = "mat-2">Mat 2 Tier:</p>
</div>
<div class = "col px-0">
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-1" onclick = "toggleMaterial('mat-2-1')">1</button>
@ -125,11 +121,9 @@
</div>
</div>
</div>
</div>
<div class = "row gx-5 mb-1">
<div class="col">
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0">
<div class = "row dark-shadow dark-6 rounded align-items-center just">
<div class = "col-3 px-0 ps-2">
<p class = "mb-0 scaled-font fw-bold">Ing 1:</p>
</div>
<div class = "col-9 px-0">
@ -140,8 +134,8 @@
</div>
</div>
<div class="col">
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0">
<div class = "row dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0 ps-2">
<p class = "mb-0 scaled-font fw-bold">Ing 2:</p>
</div>
<div class = "col-9 px-0">
@ -151,11 +145,9 @@
</div>
</div>
</div>
</div>
<div class = "row gx-5 mb-1">
<div class="col">
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0">
<div class = "row dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0 ps-2">
<p class = "mb-0 scaled-font fw-bold">Ing 3:</p>
</div>
<div class = "col-9 px-0">
@ -166,8 +158,8 @@
</div>
</div>
<div class="col">
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0">
<div class = "row dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0 ps-2">
<p class = "mb-0 scaled-font fw-bold">Ing 4:</p>
</div>
<div class = "col-9 px-0">
@ -177,11 +169,9 @@
</div>
</div>
</div>
</div>
<div class = "row gx-5 mb-1">
<div class="col">
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0">
<div class = "row dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0 ps-2">
<p class = "mb-0 scaled-font fw-bold">Ing 5:</p>
</div>
<div class = "col-9 px-0">
@ -192,8 +182,8 @@
</div>
</div>
<div class="col">
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0">
<div class = "row dark-shadow dark-6 rounded align-items-center">
<div class = "col-3 px-0 ps-2">
<p class = "mb-0 scaled-font fw-bold">Ing 6:</p>
</div>
<div class = "col-9 px-0">
@ -204,41 +194,39 @@
</div>
</div>
</div>
<div class = "row rounded dark-shadow dark-6 py-2 gy-3">
<div class = "col-lg-2 col-sm-6">
<div class = "row rounded dark-shadow dark-6 mt-3 p-2 align-items-center justify-content-center">
<div class = "col-auto mx-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "resetFields()">
Reset
</button>
</div>
<div class = "col-lg-3 col-sm-6">
<div class = "col-auto mx-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "copy-hash-button" onclick = "copyRecipeHash()">
Copy Hash
</button>
</div>
<div class = "col-lg-4 col-sm-6">
<div class = "col-auto mx-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "copy-button" onclick = "copyRecipe()">
Copy Short
</button>
</div>
<div class = "col-lg-3 col-sm-6">
<div class = "col-auto mx-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "share-button" onclick = "shareRecipe()">
Copy Long
</button>
</div>
</div>
</div>
<div class = "col-lg-4">
<div class="col col-lg-4">
<div class = "recipe hide-container-block px-3 col rounded dark-6 text-light scaled-font p-3 border-dark dark-shadow g-0" style = "display:none">
<div class = "row recipe-stats">
<div class = "col" id = "recipe-stats"></div>
<div class = "row row-cols-1 recipe-stats " id = "recipe-stats">
</div>
<div class = "row craft-warnings">
<div class = "" id = "craft-warnings"></div>
</div>
</div>
</div>
<div class = "col-lg-3">
<div class="col col-lg-3">
<div class = "crafted row hide-container-block" style = "display:none">
<div class = "craft-stats">
<div class = "col rounded dark-6 text-light scaled-font p-3 border-dark dark-shadow g-0" id = "craft-stats"></div>
@ -246,40 +234,38 @@
</div>
</div>
</div>
</div>
<div class="col ingredients-container hide-container-grid" id = "ingreds" style = "display:none">
<div class = "col-lg-6 col-sm-12 hide-container-grid" id = "ingreds">
<div class="row my-3">
<div class="col col-lg-6 ingredients-container hide-container-grid" id = "ingreds" style = "display:none">
<div class = "row mb-3">
<div class="col">
<p class="box-title hide-container-block">
Ingredients
</p>
</div>
<div class = "row mb-3">
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-1">
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-1-stats"></div>
</div>
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-2">
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-2-stats"></div>
<div class="row row-cols-1 row-cols-lg-2 g-3">
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-1">
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-1-stats"></div>
</div>
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-2">
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-2-stats"></div>
</div>
<div class = "row mb-3">
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-3">
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-3-stats"></div>
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-3">
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-3-stats"></div>
</div>
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-4">
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-4-stats"></div>
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-4">
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-4-stats"></div>
</div>
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-5">
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-5-stats"></div>
</div>
<div class = "row mb-3">
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-5">
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-5-stats"></div>
</div>
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-6">
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-6-stats"></div>
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-6">
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-6-stats"></div>
</div>
</div>
</div>
</div>
<div class="row my-2">
<div class="col dark-5 scaled-font">
<footer class="text-center">
<div id="header2">
@ -287,11 +273,12 @@
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
</div>
<div id="credits">
<a href="credits.txt" class="link">Additional credits</a>
<a href="../credits.txt" class="link">Additional credits</a>
</div>
</footer>
</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>
@ -307,7 +294,5 @@
<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>
<script type="text/javascript" src="../js/items.js"></script>
<!-- <script type="text/javascript" src="../js/sq2items.js"></script> -->
</body>
</html>

View file

@ -7,14 +7,16 @@ The game, of course
Additional Contributors, in no particular order:
- Kiocifer (Icons!)
- IncinerateMe (helping transition to 1.20.3 / CI helper)
- puppy (wynn2 ability tree help)
- IncinerateMe (helping transition to 1.20.3 / atree / CI helper)
- puppy (dog)
- SockMower (ability tree encode/decode optimization)
- ITechnically (coding emotional support / misc)
- touhoku (best IM)
- HeyZeer0 (huge help in getting our damage formulas right)
- blankman (fin444 github) (beautifying atree visuals)
- lemonalade (ability tree pdf for us to copy from :) )
- Lennon (Skill point formula reversing)
- Phanta (WynnAtlas custom expression parser / item search)
- nbcss (Crafted Item mechanics reverse engineering)
- nbcss (and WIM team) (Crafted Item mechanics reverse engineering, testing)
- dr_carlos (Hiding UI elements properly, fade animations, proper error handling)
- Atlas Inc discord (feedback, ideas, damage calc, etc)

View file

@ -8,7 +8,7 @@
/* builder containers */
.e_slider, .t_slider, .w_slider, .f_slider, .a_slider {
.slider {
-webkit-appearance: none;
background: #AAAAAA;
border-radius: 30px;
@ -16,15 +16,14 @@
}
/***** Chrome, Safari, Opera, and Edge Chromium *****/
.e_slider::-webkit-slider-runnable-track, .t_slider::-webkit-slider-runnable-track, .w_slider::-webkit-slider-runnable-track, .f_slider::-webkit-slider-runnable-track, .a_slider::-webkit-slider-runnable-track {
.slider::-webkit-slider-runnable-track{
-webkit-appeareance: none;
background:transparent;
height: 0.5rem;
}
/******** Firefox **** **/
.e_slider::-moz-range-track, .t_slider::-moz-range-track, .w_slider::-moz-range-track, .f_slider::-moz-range-track, .a_slider::-moz-range-track {
.slider::-moz-range-track {
-webkit-appearance: none;
background-color: transparent;
border-radius: 30px;
@ -32,7 +31,7 @@
}
.e_slider::-webkit-slider-thumb, .t_slider::-webkit-slider-thumb, .w_slider::-webkit-slider-thumb, .f_slider::-webkit-slider-thumb, .a_slider::-webkit-slider-thumb {
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
height: 0.75rem;
@ -133,10 +132,6 @@ input.equipment-input {
font-weight: bold;
}
.spell-expand {
cursor: pointer;
}
:root {
--scaled-fontsize: 2.5rem;
}
@ -449,36 +444,6 @@ a:hover {
border-color: #fff;
}
/* atree connector rotations */
.rotate-90 {
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-ms-transform: rotate(90deg);
-o-transform: rotate(90deg);
transform: rotate(90deg);
}
.rotate-180 {
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-ms-transform: rotate(180deg);
-o-transform: rotate(180deg);
transform: rotate(180deg);
}
.rotate-270 {
-webkit-transform: rotate(270deg);
-moz-transform: rotate(270deg);
-ms-transform: rotate(270deg);
-o-transform: rotate(270deg);
transform: rotate(270deg);
}
.rotate-flip {
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.hide-scroll {
@ -489,16 +454,6 @@ a:hover {
display: none; /* Safari and Chrome */
}
.atree-selected {
outline: 5px solid rgba(95, 214, 223, 0.8);
}
.atree-circle {
border-radius:50%;
-moz-border-radius:50%;
-webkit-border-radius:50%;
}
.hppeng{
color: #20c2b6;
}

View file

@ -182,3 +182,22 @@ Wynn-Related CSS
.restrict {
color: #ff8180;
}
/* Every text color in Minecraft */
.mc-black { color: #000000 !important; }
.mc-dark-blue { color: #0000AA !important; }
.mc-dark-green { color: #00AA00 !important; }
.mc-dark-aqua { color: #00AAAA !important; }
.mc-dark-red { color: #AA0000 !important; }
.mc-dark-purple { color: #AA00AA !important; }
.mc-gold { color: #FFAA00 !important; }
.mc-gray { color: #AAAAAA !important; }
.mc-dark-gray { color: #555555 !important; }
.mc-blue { color: #5555FF !important; }
.mc-green { color: #55FF55 !important; }
.mc-aqua { color: #55FFFF !important; }
.mc-red { color: #FF5555 !important; }
.mc-light-purple { color: #FF55FF !important; }
.mc-yellow { color: #FFFF55 !important; }
.mc-white { color: #FFFFFF !important; }

View file

@ -1889,7 +1889,7 @@
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
</div>
<div id="credits">
<a href="credits.txt" class="link">Additional credits</a>
<a href="../credits.txt" class="link">Additional credits</a>
</div>
</footer>
</div>
@ -1906,8 +1906,6 @@
<script type="text/javascript" src="/js/craft.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/sq2display_constants.js"></script>
<script type="text/javascript" src="/js/sq2display.js"></script>
<script type="text/javascript" src="/js/custom.js"></script>
<script type="text/javascript" src="/js/customizer.js"></script>
</body>

View file

@ -67,6 +67,6 @@
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/loadheader.js"></script>
<script type="text/javascript" src="/js/icons.js"></script>
<script type="text/javascript" src="/js/dps_vis.js"></script>
<script type="text/javascript" src="/js/debug/dps_vis.js"></script>
</body>
</html>

View file

@ -200,8 +200,8 @@
"lvl": 75,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdPct": {
"minimum": -22,
@ -9834,7 +9834,7 @@
"ids": {
"mr": {
"minimum": 0,
"maximum": 1
"maximum": 5
}
},
"itemIDs": {
@ -10533,7 +10533,7 @@
"ids": {
"ms": {
"minimum": 0,
"maximum": 1
"maximum": 5
}
},
"itemIDs": {
@ -10882,8 +10882,8 @@
"maximum": 15
},
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
}
},
"itemIDs": {
@ -11906,8 +11906,8 @@
"maximum": -5
},
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"gXp": {
"minimum": 4,
@ -14594,8 +14594,8 @@
"lvl": 76,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"spd": {
"minimum": -12,
@ -15404,8 +15404,8 @@
"lvl": 28,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"xpb": {
"minimum": -10,
@ -15569,8 +15569,8 @@
"lvl": 102,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"hpBonus": {
"minimum": -260,
@ -15684,8 +15684,8 @@
"lvl": 87,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdRaw": {
"minimum": 65,
@ -15762,8 +15762,8 @@
"lvl": 65,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
}
},
"itemIDs": {
@ -16037,8 +16037,8 @@
"lvl": 77,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"hpBonus": {
"minimum": -775,
@ -16270,8 +16270,8 @@
"lvl": 50,
"ids": {
"mr": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
},
"sdPct": {
"minimum": 8,
@ -16355,8 +16355,8 @@
"lvl": 90,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdPct": {
"minimum": 6,
@ -16396,8 +16396,8 @@
"lvl": 90,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"mdRaw": {
"minimum": 50,
@ -16734,8 +16734,8 @@
"maximum": 20
},
"ms": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
},
"fDamPct": {
"minimum": 10,
@ -16777,8 +16777,8 @@
"lvl": 99,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdPct": {
"minimum": 10,
@ -16944,8 +16944,8 @@
"maximum": 10
},
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
}
},
"itemIDs": {
@ -17488,12 +17488,12 @@
"lvl": 23,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"ms": {
"minimum": 0,
"maximum": 1
"maximum": 5
}
},
"itemIDs": {
@ -18623,8 +18623,8 @@
"maximum": -100
},
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"wDamPct": {
"minimum": 10,
@ -18908,8 +18908,8 @@
"lvl": 29,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdPct": {
"minimum": -8,
@ -18947,8 +18947,8 @@
"lvl": 105,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"agi": {
"minimum": 8,
@ -19535,8 +19535,8 @@
"lvl": 104,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"gXp": {
"minimum": 3,
@ -21060,8 +21060,8 @@
"lvl": 75,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"int": {
"minimum": 3,
@ -21387,12 +21387,12 @@
"lvl": 78,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"ms": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
},
"wDamPct": {
"minimum": 5,
@ -21550,8 +21550,8 @@
"lvl": 93,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdRaw": {
"minimum": 45,
@ -21595,8 +21595,8 @@
"lvl": 20,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdRaw": {
"minimum": 8,
@ -21958,8 +21958,8 @@
"lvl": 43,
"ids": {
"mr": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
},
"sdPct": {
"minimum": 11,
@ -22216,8 +22216,8 @@
"lvl": 75,
"ids": {
"mr": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
},
"sdPct": {
"minimum": 15,
@ -22714,8 +22714,8 @@
"lvl": 80,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdPct": {
"minimum": -20,
@ -22911,8 +22911,8 @@
"lvl": 8,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"ref": {
"minimum": 2,
@ -22988,8 +22988,8 @@
"lvl": 45,
"ids": {
"mr": {
"minimum": 2,
"maximum": 2
"minimum": 10,
"maximum": 10
}
},
"itemIDs": {
@ -23583,8 +23583,8 @@
"lvl": 42,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
}
},
"itemIDs": {
@ -23878,8 +23878,8 @@
"lvl": 50,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdPct": {
"minimum": -12,
@ -23957,8 +23957,8 @@
"lvl": 55,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"spd": {
"minimum": 8,
@ -24035,8 +24035,8 @@
"lvl": 43,
"ids": {
"mr": {
"minimum": -2,
"maximum": -2
"minimum": -10,
"maximum": -10
},
"sdPct": {
"minimum": 24,
@ -24120,8 +24120,8 @@
"maximum": 14
},
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"hprRaw": {
"minimum": 24,
@ -24160,8 +24160,8 @@
"lvl": 92,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"sdRaw": {
"minimum": 110,
@ -24373,8 +24373,8 @@
"lvl": 90,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"spd": {
"minimum": 16,
@ -25193,8 +25193,8 @@
"maximum": 20
},
"mr": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
}
},
"itemIDs": {
@ -25249,8 +25249,8 @@
"maximum": 20
},
"mr": {
"minimum": -1,
"maximum": -1
"minimum": -5,
"maximum": -5
}
},
"itemIDs": {
@ -25285,8 +25285,8 @@
"lvl": 83,
"ids": {
"mr": {
"minimum": 2,
"maximum": 2
"minimum": 10,
"maximum": 10
}
},
"itemIDs": {
@ -25544,8 +25544,8 @@
"lvl": 10,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
}
},
"itemIDs": {
@ -26028,8 +26028,8 @@
"lvl": 47,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"tDamPct": {
"minimum": 6,
@ -26200,8 +26200,8 @@
"lvl": 87,
"ids": {
"mr": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
},
"eDamPct": {
"minimum": 10,
@ -26769,8 +26769,8 @@
"maximum": 12
},
"ms": {
"minimum": 2,
"maximum": 2
"minimum": 10,
"maximum": 10
},
"atkTier": {
"minimum": -1,
@ -26971,8 +26971,8 @@
"lvl": 68,
"ids": {
"ms": {
"minimum": 2,
"maximum": 2
"minimum": 10,
"maximum": 10
}
},
"itemIDs": {
@ -27344,8 +27344,8 @@
"lvl": 25,
"ids": {
"ms": {
"minimum": 1,
"maximum": 1
"minimum": 5,
"maximum": 5
}
},
"itemIDs": {

File diff suppressed because one or more lines are too long

View file

@ -58,6 +58,7 @@
<script type="text/javascript" src="/js/build_utils.js"></script>
<script type="text/javascript" src="/js/icons.js"></script>
<script type="text/javascript" src="/js/damage_calc.js"></script>
<script type="text/javascript" src="/js/powders.js"></script>
<!-- <script type="text/javascript" src="/js/powders.js"></script> -->
<script type="text/javascript" src="/js/load.js"></script>
<script type="text/javascript" src="/js/load_ing.js"></script>

View file

@ -140,13 +140,11 @@
<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/sq2display_constants.js"></script>
<script type="text/javascript" src="../js/sq2display.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/items.js"></script>
<script type="text/javascript" src="../js/sq2items.js"></script>
<script type="text/javascript" src="../js/powders.js"></script>
</body>
</html>

View file

@ -36,7 +36,7 @@
<div class = "col">
<div class = "row">
<div class = "col text-start" id = "credits">
<a href="credits.txt" class="link">Additional credits</a>
<a href="../credits.txt" class="link">Additional credits</a>
</div>
<div class = "col text-center" id = "help">
<a href="items_2_help.html" class="link" target="_blank">Search Guide</a>

View file

@ -1,5 +1,6 @@
How to convert:
0. start in the `js` directory
1. edit `atree_constants.js`
2. run `python3 ../py_script/atree-generateID.py
3. check that the site still works

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,146 +0,0 @@
{
"Archer": {
"Arrow Shield": 0,
"Escape": 1,
"Arrow Bomb": 2,
"Heart Shatter": 3,
"Fire Creep": 4,
"Bryophyte Roots": 5,
"Nimble String": 6,
"Arrow Storm": 7,
"Guardian Angels": 8,
"Windy Feet": 9,
"Basaltic Trap": 10,
"Windstorm": 11,
"Grappling Hook": 12,
"Implosion": 13,
"Twain's Arc": 14,
"Fierce Stomp": 15,
"Scorched Earth": 16,
"Leap": 17,
"Shocking Bomb": 18,
"Mana Trap": 19,
"Escape Artist": 20,
"Initiator": 21,
"Call of the Hound": 22,
"Arrow Hurricane": 23,
"Geyser Stomp": 24,
"Crepuscular Ray": 25,
"Grape Bomb": 26,
"Tangled Traps": 27,
"Snow Storm": 28,
"All-Seeing Panoptes": 29,
"Minefield": 30,
"Bow Proficiency I": 31,
"Cheaper Arrow Bomb": 32,
"Cheaper Arrow Storm": 33,
"Cheaper Escape": 34,
"Earth Mastery": 35,
"Thunder Mastery": 36,
"Water Mastery": 37,
"Air Mastery": 38,
"Fire Mastery": 39,
"More Shields": 40,
"Stormy Feet": 41,
"Refined Gunpowder": 42,
"More Traps": 43,
"Better Arrow Shield": 44,
"Better Leap": 45,
"Better Guardian Angels": 46,
"Cheaper Arrow Storm (2)": 47,
"Precise Shot": 48,
"Cheaper Arrow Shield": 49,
"Rocket Jump": 50,
"Cheaper Escape (2)": 51,
"Stronger Hook": 52,
"Cheaper Arrow Bomb (2)": 53,
"Bouncing Bomb": 54,
"Homing Shots": 55,
"Shrapnel Bomb": 56,
"Elusive": 57,
"Double Shots": 58,
"Triple Shots": 59,
"Power Shots": 60,
"Focus": 61,
"More Focus": 62,
"More Focus (2)": 63,
"Traveler": 64,
"Patient Hunter": 65,
"Stronger Patient Hunter": 66,
"Frenzy": 67,
"Phantom Ray": 68,
"Arrow Rain": 69,
"Decimator": 70
},
"Warrior": {
"Bash": 0,
"Spear Proficiency 1": 1,
"Cheaper Bash": 2,
"Double Bash": 3,
"Charge": 4,
"Heavy Impact": 5,
"Vehement": 6,
"Tougher Skin": 7,
"Uppercut": 8,
"Cheaper Charge": 9,
"War Scream": 10,
"Earth Mastery": 11,
"Thunder Mastery": 12,
"Water Mastery": 13,
"Air Mastery": 14,
"Fire Mastery": 15,
"Quadruple Bash": 16,
"Fireworks": 17,
"Half-Moon Swipe": 18,
"Flyby Jab": 19,
"Flaming Uppercut": 20,
"Iron Lungs": 21,
"Generalist": 22,
"Counter": 23,
"Mantle of the Bovemists": 24,
"Bak'al's Grasp": 25,
"Spear Proficiency 2": 26,
"Cheaper Uppercut": 27,
"Aerodynamics": 28,
"Provoke": 29,
"Precise Strikes": 30,
"Air Shout": 31,
"Enraged Blow": 32,
"Flying Kick": 33,
"Stronger Mantle": 34,
"Manachism": 35,
"Boiling Blood": 36,
"Ragnarokkr": 37,
"Ambidextrous": 38,
"Burning Heart": 39,
"Stronger Bash": 40,
"Intoxicating Blood": 41,
"Comet": 42,
"Collide": 43,
"Rejuvenating Skin": 44,
"Uncontainable Corruption": 45,
"Radiant Devotee": 46,
"Whirlwind Strike": 47,
"Mythril Skin": 48,
"Armour Breaker": 49,
"Shield Strike": 50,
"Sparkling Hope": 51,
"Massive Bash": 52,
"Tempest": 53,
"Spirit of the Rabbit": 54,
"Massacre": 55,
"Axe Kick": 56,
"Radiance": 57,
"Cheaper Bash 2": 58,
"Cheaper War Scream": 59,
"Discombobulate": 60,
"Thunderclap": 61,
"Cyclone": 62,
"Second Chance": 63,
"Blood Pact": 64,
"Haemorrhage": 65,
"Brink of Madness": 66,
"Cheaper Uppercut 2": 67,
"Martyr": 68
}
}

View file

@ -1,94 +1,9 @@
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20], ["sword", 1.10]]);
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.0], ["sword", 1.10]]);
/**
* @description Error to catch items that don't exist.
* @module ItemNotFound
*/
class ItemNotFound {
/**
* @class
* @param {String} item the item name entered
* @param {String} type the type of item
* @param {Boolean} genElement whether to generate an element from inputs
* @param {String} override override for item type
*/
constructor(item, type, genElement, override) {
/**
* @public
* @type {String}
*/
this.message = `Cannot find ${override||type} named ${item}`;
if (genElement)
/**
* @public
* @type {Element}
*/
this.element = document.getElementById(`${type}-choice`).parentElement.querySelectorAll("p.error")[0];
else
this.element = document.createElement("div");
}
}
/**
* @description Error to catch incorrect input.
* @module IncorrectInput
*/
class IncorrectInput {
/**
* @class
* @param {String} input the inputted text
* @param {String} format the correct format
* @param {String} sibling the id of the error node's sibling
*/
constructor(input, format, sibling) {
/**
* @public
* @type {String}
*/
this.message = `${input} is incorrect. Example: ${format}`;
/**
* @public
* @type {String}
*/
this.id = sibling;
}
}
/**
* @description Error that inputs an array of items to generate errors of.
* @module ListError
* @extends Error
*/
class ListError extends Error {
/**
* @class
* @param {Array} errors array of errors
*/
constructor(errors) {
let ret = [];
if (typeof errors[0] == "string") {
super(errors[0]);
} else {
super(errors[0].message);
}
for (let i of errors) {
if (typeof i == "string") {
ret.push(new Error(i));
} else {
ret.push(i);
}
}
/**
* @public
* @type {Object[]}
*/
this.errors = ret;
}
}
/*Class that represents a wynn player's build.
/*
* Class that represents a wynn player's build.
*/
class Build{
@ -115,7 +30,6 @@ class Build{
this.level = level;
} else if (typeof level === "string") {
this.level = level;
errors.push(new IncorrectInput(level, "a number", "level-choice"));
} else {
errors.push("Level is not a string or number.");
}
@ -168,7 +82,6 @@ class Build{
//Create a map of this build's stats
let statMap = new Map();
statMap.set("defMultiplier", 1);
for (const staticID of staticIDs) {
statMap.set(staticID, 0);
@ -198,8 +111,10 @@ class Build{
}
}
}
statMap.set('damageMultiplier', 1 + (statMap.get('damMobs') / 100));
statMap.set('defMultiplier', 1 - (statMap.get('defMobs') / 100));
statMap.set('damMult', new Map());
statMap.set('defMult', new Map());
statMap.get('damMult').set('tome', statMap.get('damMobs'))
statMap.get('defMult').set('tome', statMap.get('defMobs'))
statMap.set("activeMajorIDs", major_ids);
for (const [setName, count] of this.activeSetCounts) {
const bonus = sets.get(setName).bonuses[count-1];
@ -213,6 +128,8 @@ class Build{
}
}
statMap.set("poisonPct", 100);
statMap.set("critDamPct", 100);
statMap.set("healPct", 100);
// The stuff relevant for damage calculation!!! @ferricles
statMap.set("atkSpd", this.weapon.statMap.get("atkSpd"));

View file

@ -7,8 +7,6 @@ const url_tag = location.hash.slice(1);
const BUILD_VERSION = "7.0.19";
let player_build;
// THIS IS SUPER DANGEROUS, WE SHOULD NOT BE KEEPING THIS IN SO MANY PLACES
let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison",

View file

@ -1,3 +1,9 @@
let player_build;
let build_powders;
function getItemNameFromID(id) { return idMap.get(id); }
function getTomeNameFromID(id) { return tomeIDMap.get(id); }
function parsePowdering(powder_info) {
// TODO: Make this run in linear instead of quadratic time... ew
let powdering = [];
@ -124,7 +130,6 @@ function decodeBuild(url_tag) {
for (let i in tomes) {
let tome_str = info[1].charAt(i);
let tome_name = getTomeNameFromID(Base64.toInt(tome_str));
console.log(tome_name);
setValue(tomeInputs[i], tome_name);
}
info[1] = info[1].slice(7);
@ -200,7 +205,7 @@ function encodeBuild(build, powders, skillpoints, atree, atree_state) {
}
build_string += tome_string;
if (atree_state.get(atree[0].ability.id).active) {
if (atree.length > 0 && atree_state.get(atree[0].ability.id).active) {
build_version = Math.max(build_version, 7);
const bitvec = encode_atree(atree, atree_state);
build_string += bitvec.toB64();

View file

@ -15,6 +15,11 @@ function skillPointsToPercentage(skp){
//return clamp((-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771), 0.00, 0.808);
}
// WYNN2: Skillpoint max scaling. Intel is cost reduction
const skillpoint_final_mult = [1, 1, 0.5/skillPointsToPercentage(150), 0.867, 0.951];
// intel water%
const skillpoint_damage_mult = [1, 1, 1, 0.867, 0.951];
/*Turns the input amount of levels into skillpoints available.
*
* @param level - the integer level count to be converted
@ -69,17 +74,17 @@ let skpReqs = skp_order.map(x => x + "Req");
let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rSdRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id", "majorIds", "damMobs", "defMobs",
// wynn2 damages.
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct"*/,"eDamRaw","eDamAddMin","eDamAddMax",
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct"*/,"tDamRaw","tDamAddMin","tDamAddMax",
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct"*/,"wDamRaw","wDamAddMin","wDamAddMax",
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct"*/,"fDamRaw","fDamAddMin","fDamAddMax",
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct"*/,"aDamRaw","aDamAddMin","aDamAddMax",
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw","eDamAddMin","eDamAddMax",
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct,"*/"tDamRaw","tDamAddMin","tDamAddMax",
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct,"*/"wDamRaw","wDamAddMin","wDamAddMax",
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct,"*/"fDamRaw","fDamAddMin","fDamAddMax",
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw","aDamAddMin","aDamAddMax",
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
"critDamPct"
];
// Extra fake IDs (reserved for use in spell damage calculation) : damageMultiplier, defMultiplier, poisonPct, activeMajorIDs
// Extra fake IDs (reserved for use in spell damage calculation) : damMult, defMult, poisonPct, activeMajorIDs
let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ]
//File reading for ID translations for JSON purposes
@ -164,11 +169,11 @@ let rolledIDs = [
"gXp",
"gSpd",
// wynn2 damages.
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct"*/,"eDamRaw","eDamAddMin","eDamAddMax",
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct"*/,"tDamRaw","tDamAddMin","tDamAddMax",
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct"*/,"wDamRaw","wDamAddMin","wDamAddMax",
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct"*/,"fDamRaw","fDamAddMin","fDamAddMax",
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct"*/,"aDamRaw","aDamAddMin","aDamAddMax",
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw","eDamAddMin","eDamAddMax",
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct,"*/"tDamRaw","tDamAddMin","tDamAddMax",
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct,"*/"wDamRaw","wDamAddMin","wDamAddMax",
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct,"*/"fDamRaw","fDamAddMin","fDamAddMax",
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw","aDamAddMin","aDamAddMax",
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
@ -295,3 +300,28 @@ function idRound(id){
return rounded;
}
}
/**
* stupid stupid multiplicative stats
*/
function merge_stat(stats, name, value) {
const start = name.slice(0, 7);
if (start === 'damMult' || start === 'defMult') {
if (!stats.has(start)) {
stats.set(start, new Map());
}
const map = stats.get(start);
if (value instanceof Map) {
for (const [k, v] of value.entries()) {
merge_stat(map, k, v);
}
return;
}
merge_stat(map, name.slice(8), value);
return;
}
if (stats.has(name)) {
stats.set(name, stats.get(name) + value);
}
else { stats.set(name, value); }
}

View file

@ -1,18 +1,3 @@
let build_powders;
function getItemNameFromID(id) {
if (redirectMap.has(id)) {
return getItemNameFromID(redirectMap.get(id));
}
return idMap.get(id);
}
function getTomeNameFromID(id) {
if (tomeRedirectMap.has(id)) {
return getTomeNameFromID(tomeRedirectMap.get(id));
}
return tomeIDMap.get(id);
}
function populateBuildList() {
const buildList = document.getElementById("build-choice");
@ -98,7 +83,6 @@ function resetFields(){
for (const elem of skp_order) {
console.log(document.getElementById(elem + "_boost_armor").value);
document.getElementById(elem + "_boost_armor").value = 0;
document.getElementById(elem + "_boost_armor_prev").value = 0;
document.getElementById(elem + "_boost_armor").style.background = `linear-gradient(to right, #AAAAAA, #AAAAAA 0%, #AAAAAA 100%)`;
document.getElementById(elem + "_boost_armor_label").textContent = `% ${damageClasses[skp_order.indexOf(elem)+1]} Damage Boost: 0`;
}
@ -149,7 +133,6 @@ function toggle_tab(tab) {
}
}
function toggle_boost_tab(tab) {
for (const i of skp_order) {
document.querySelector("#"+i+"-boost").style.display = "none";
@ -157,13 +140,10 @@ function toggle_boost_tab(tab) {
}
document.querySelector("#"+tab+"-boost").style.display = "";
document.getElementById(tab + "-boost-tab").classList.add("selected-btn");
}
let tabs = ['overall-stats', 'offensive-stats', 'defensive-stats'];
function show_tab(tab) {
//console.log(itemFilters)
//hide all tabs, then show the tab of the div clicked and highlight the correct button
for (const i in tabs) {
document.querySelector("#" + tabs[i]).style.display = "none";
@ -320,51 +300,6 @@ function init_autocomplete() {
}));
}
let filter_loc = ["filter1", "filter2", "filter3", "filter4"];
for (const i of filter_loc) {
dropdowns.set(i+"-choice", new autoComplete({
data: {
src: sq2ItemFilters,
},
selector: "#"+i+"-choice",
wrapper: false,
resultsList: {
tabSelect: true,
noResults: true,
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
element: (list, data) => {
// dynamic result loc
console.log(i);
list.style.zIndex = "100";
let position = document.getElementById(i+"-dropdown").getBoundingClientRect();
window_pos = document.getElementById("search-container").getBoundingClientRect();
list.style.top = position.bottom - window_pos.top + 5 +"px";
list.style.left = position.x - window_pos.x +"px";
list.style.width = position.width+"px";
if (!data.results.length) {
message = document.createElement('li');
message.classList.add('scaled-font');
message.textContent = "No filters 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;
}
},
},
}
}));
}
}
function collapse_element(elmnt) {
@ -394,6 +329,19 @@ function init() {
for (const eq of equipment_keys) {
document.querySelector("#"+eq+"-tooltip").addEventListener("click", () => collapse_element('#'+eq+'-tooltip'));
}
// Armor Specials
for (let i = 0; i < 5; ++i) {
const powder_special = powderSpecialStats[i];
const elem_name = damageClasses[i+1]; // skip neutral
const elem_char = skp_elements[i]; // TODO: merge?
const skp_name = skp_order[i]; // TODO: merge?
const boost_parent = document.getElementById(skp_name+'-boost');
const slider_id = skp_name+'_boost_armor';
const label_name = "% " + elem_name + " Dmg Boost";
const slider_container = gen_slider_labeled({label_name: label_name, max: powder_special.cap, id: slider_id, color: elem_colors[i]});
boost_parent.appendChild(slider_container);
document.getElementById(slider_id).addEventListener("change", (_) => armor_powder_node.mark_dirty().update() );
}
// Masonry setup
let masonry = Macy({
@ -425,6 +373,15 @@ function init() {
});
decodeBuild(url_tag);
builder_graph_init();
for (const item_node of item_nodes) {
if (item_node.get_value() === null) {
// likely DB load failure...
if (confirm('One or more items failed to load correctly. This could be due to a corrupted build link, or (more likely) a database load failure. Would you like to reload?')) {
hardReload();
}
break;
}
}
}
window.onerror = function(message, source, lineno, colno, error) {

View file

@ -11,29 +11,7 @@ let armor_powder_node = new (class extends ComputeNode {
}
return statMap;
}
})().update();
/* Updates PASSIVE powder special boosts (armors)
*/
function update_armor_powder_specials(elem_id) {
//we only update the powder special + external stats if the player has a build
let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi
//update the label associated w/ the slider
let elem = document.getElementById(elem_id);
let label = document.getElementById(elem_id + "_label");
let value = elem.value;
label.textContent = label.textContent.split(":")[0] + ": " + value
//update the slider's graphics
let bg_color = elem_colors[skp_order.indexOf(wynn_elem)];
let pct = Math.round(100 * value / powderSpecialStats[skp_order.indexOf(wynn_elem)].cap);
elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
armor_powder_node.mark_dirty().update();
}
})();
let boosts_node = new (class extends ComputeNode {
constructor() { super('builder-boost-input'); }
@ -45,13 +23,13 @@ let boosts_node = new (class extends ComputeNode {
let elem = document.getElementById(key + "-boost")
if (elem.classList.contains("toggleOn")) {
damage_boost += value;
if (key === "warscream") { def_boost += .20 }
if (key === "warscream") { def_boost += .10 }
if (key === "vanish") { def_boost += .15 }
}
}
let res = new Map();
res.set('damageMultiplier', 1+damage_boost);
res.set('defMultiplier', 1-def_boost);
res.set('damMult.Potion', 100*damage_boost);
res.set('defMult.Potion', 100*def_boost);
return res;
}
})().update();
@ -181,7 +159,8 @@ class ItemInputNode extends InputNode {
if (item) {
if (powdering !== undefined) {
item.statMap.set('powders', powdering);
const max_slots = item.statMap.get('slots');
item.statMap.set('powders', powdering.slice(0, max_slots));
}
let type_match;
if (this.category == 'weapon') {
@ -213,7 +192,13 @@ class ItemInputNode extends InputNode {
for (const [i, x] of zip2(equipment_inputs, replace_items)) { setValue(i, x); }
for (const node of item_nodes) { calcSchedule(node, 10); }
for (const node of item_nodes) {
if (node !== this) {
// save a tiny bit of compute
calcSchedule(node, 10);
}
}
// Needed to push the weapon node's updates forward
return this.compute_func(input_map);
}
return null;
@ -257,7 +242,7 @@ class ItemInputDisplayNode extends ComputeNode {
this.input_field.classList.add("is-invalid");
return null;
}
if (item.statMap.has('powders')) {
if (this.powder_field && item.statMap.has('powders')) {
this.powder_field.placeholder = "powders";
}
@ -265,7 +250,7 @@ class ItemInputDisplayNode extends ComputeNode {
return null;
}
if (item.statMap.has('powders')) {
if (this.powder_field && item.statMap.has('powders')) {
this.powder_field.placeholder = item.statMap.get('slots') + ' slots';
}
@ -422,7 +407,10 @@ class BuildAssembleNode extends ComputeNode {
input_map.get('guildTome1-input')
];
let weapon = input_map.get('weapon-input');
let level = input_map.get('level-input');
let level = parseInt(input_map.get('level-input'));
if (isNaN(level)) {
level = 106;
}
let all_none = weapon.statMap.has('NONE');
for (const item of equipments) {
@ -502,16 +490,22 @@ class SpellSelectNode extends ComputeNode {
*/
function getDefenseStats(stats) {
let defenseStats = [];
let def_pct = skillPointsToPercentage(stats.get('def'));
let agi_pct = skillPointsToPercentage(stats.get('agi'));
let def_pct = skillPointsToPercentage(stats.get('def')) * skillpoint_final_mult[3];
let agi_pct = skillPointsToPercentage(stats.get('agi')) * skillpoint_final_mult[4];
//total hp
let totalHp = stats.get("hp") + stats.get("hpBonus");
if (totalHp < 5) totalHp = 5;
defenseStats.push(totalHp);
//EHP
let ehp = [totalHp, totalHp];
let defMult = (2 - stats.get("classDef")) * stats.get("defMultiplier");
ehp[0] /= (1-def_pct)*(1-agi_pct)*defMult;
let defMult = (2 - stats.get("classDef"));
for (const [k, v] of stats.get("defMult").entries()) {
defMult *= (1 - v/100);
}
// newehp = oldehp / [0.1 * A(x) + (1 - A(x)) * (1 - D(x))]
ehp[0] = ehp[0] / (0.1*agi_pct + (1-agi_pct) * (1-def_pct));
ehp[0] /= defMult;
// ehp[0] /= (1-def_pct)*(1-agi_pct)*defMult;
ehp[1] /= (1-def_pct)*defMult;
defenseStats.push(ehp);
//HPR
@ -554,7 +548,6 @@ class SpellDamageCalcNode extends ComputeNode {
const spell = spell_info[0];
const spell_parts = spell_info[1];
const stats = input_map.get('stats');
const damage_mult = stats.get('damageMultiplier');
const skillpoints = [
stats.get('str'),
stats.get('dex'),
@ -571,7 +564,7 @@ class SpellDamageCalcNode extends ComputeNode {
for (const part of spell_parts) {
let spell_result;
if ('multipliers' in part) { // damage type spell
let results = calculateSpellDamage(stats, weapon, part.multipliers, use_spell, !use_speed);
let results = calculateSpellDamage(stats, weapon, part.multipliers, use_spell, !use_speed, spell.base_spell + '.' + part.name);
spell_result = {
type: "damage",
normal_min: results[2].map(x => x[0]),
@ -583,7 +576,7 @@ class SpellDamageCalcNode extends ComputeNode {
}
} else if ('power' in part) {
// TODO: wynn2 formula
let _heal_amount = (part.power * getDefenseStats(stats)[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100)));
let _heal_amount = (part.power * getDefenseStats(stats)[0] * (stats.get('healPct')/100));
spell_result = {
type: "heal",
heal_amount: _heal_amount
@ -663,59 +656,10 @@ class SpellDisplayNode extends ComputeNode {
const i = this.spell_idx;
let parent_elem = document.getElementById("spell"+i+"-info");
let overallparent_elem = document.getElementById("spell"+i+"-infoAvg");
displaySpellDamage(parent_elem, overallparent_elem, stats, spell, i+1, damages);
displaySpellDamage(parent_elem, overallparent_elem, stats, spell, i, damages);
}
}
/* Get melee stats for build.
Returns an array in the order:
*/
function getMeleeStats(stats, weapon) {
stats = new Map(stats); // Shallow copy
const weapon_stats = weapon.statMap;
const skillpoints = [
stats.get('str'),
stats.get('dex'),
stats.get('int'),
stats.get('def'),
stats.get('agi')
];
if (weapon_stats.get("tier") === "Crafted") {
stats.set("damageBases", [weapon_stats.get("nDamBaseHigh"),weapon_stats.get("eDamBaseHigh"),weapon_stats.get("tDamBaseHigh"),weapon_stats.get("wDamBaseHigh"),weapon_stats.get("fDamBaseHigh"),weapon_stats.get("aDamBaseHigh")]);
}
let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier");
if(adjAtkSpd > 6){
adjAtkSpd = 6;
}else if(adjAtkSpd < 0){
adjAtkSpd = 0;
}
if (weapon_stats.get("type") === "relik") {
stats.set('damageMultiplier', 0.99); // CURSE YOU WYNNCRAFT
//One day we will create WynnWynn and no longer have shaman 99% melee injustice.
//In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams.
}
let results = calculateSpellDamage(stats, weapon_stats, [100, 0, 0, 0, 0, 0], false, true);
let dex = skillpoints[1];
let totalDamNorm = results[0];
let totalDamCrit = results[1];
totalDamNorm.push(1-skillPointsToPercentage(dex));
totalDamCrit.push(skillPointsToPercentage(dex));
let damages_results = results[2];
let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2])
+(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2;
//Now do math
let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd];
let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd];
let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex)));
//[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit]
return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]);
}
/**
* Display build stats.
*
@ -730,10 +674,7 @@ class BuildDisplayNode extends ComputeNode {
displayBuildStats('overall-stats', build, build_all_display_commands, stats);
displayBuildStats("offensive-stats", build, build_offensive_display_commands, stats);
displaySetBonuses("set-info", build);
let meleeStats = getMeleeStats(stats, build.weapon);
// TODO: move weapon out?
displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats);
displayDefenseStats(document.getElementById("defensive-stats"), stats);
displayPoisonDamage(document.getElementById("build-poison-stats"), build);
@ -770,7 +711,7 @@ class DisplayBuildWarningsNode extends ComputeNode {
setValue(skp_order[i] + "-skp", skillpoints[i]);
let linebreak = document.createElement("br");
linebreak.classList.add("itemp");
setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i]));
setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100*skillpoint_final_mult[i]).toFixed(1).concat(skp_effects[i]));
document.getElementById(skp_order[i]+"-warnings").textContent = ''
if (assigned > 100) {
let skp_warning = document.createElement("p");
@ -859,18 +800,7 @@ class AggregateStatsNode extends ComputeNode {
const output_stats = new Map();
for (const [k, v] of input_map.entries()) {
for (const [k2, v2] of v.entries()) {
if (output_stats.has(k2)) {
// TODO: ugly AF
if (k2 === 'damageMultiplier' || k2 === 'defMultiplier') {
output_stats.set(k2, v2 * output_stats.get(k2));
}
else {
output_stats.set(k2, v2 + output_stats.get(k2));
}
}
else {
output_stats.set(k2, v2);
}
merge_stat(output_stats, k2, v2);
}
}
return output_stats;
@ -972,7 +902,7 @@ class SkillPointSetterNode extends ComputeNode {
class SumNumberInputNode extends InputNode {
compute_func(input_map) {
let value = this.input_field.value;
if (value === "") { value = 0; }
if (value === "") { value = "0"; }
let input_num = 0;
if (value.includes("+")) {
@ -1035,6 +965,9 @@ function builder_graph_init() {
// Level input node.
let level_input = new InputNode('level-input', document.getElementById('level-choice'));
// linking to atree verification
atree_validate.link_to(level_input, 'level');
// "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display.
build_node = new BuildAssembleNode();
for (const input of item_nodes) {
@ -1063,9 +996,6 @@ function builder_graph_init() {
// Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
let build_disp_node = new BuildDisplayNode()
build_disp_node.link_to(build_node, 'build');
// Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap)
stat_agg_node = new AggregateStatsNode();
edit_agg_node = new AggregateEditableIDNode();
@ -1092,16 +1022,15 @@ function builder_graph_init() {
skp_inputs.push(node);
}
stat_agg_node.link_to(edit_agg_node);
build_disp_node.link_to(stat_agg_node, 'stats');
// Phase 3/3: Set up atree stuff.
let class_node = new PlayerClassNode('builder-class').link_to(build_node);
// These two are defined in `atree.js`
atree_node.link_to(class_node, 'player-class');
atree_merge.link_to(build_node, 'build');
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
.link_to(atree_collect_spells, 'spells');
atree_merge.link_to(class_node, 'player-class');
atree_stats.link_to(build_node, 'build');
stat_agg_node.link_to(atree_stats, 'atree-stats');
build_encode_node.link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
@ -1111,8 +1040,12 @@ function builder_graph_init() {
for (const input_node of item_nodes.concat(powder_nodes)) {
input_node.update();
}
armor_powder_node.update();
level_input.update();
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
.link_to(atree_collect_spells, 'spells');
// kinda janky, manually set atree and update. Some wasted compute here
if (atree_data !== null && atree_node.value !== null) { // janky check if atree is valid
const atree_state = atree_state_node.value;
@ -1138,6 +1071,10 @@ function builder_graph_init() {
// Also do something similar for skill points
let build_disp_node = new BuildDisplayNode()
build_disp_node.link_to(build_node, 'build');
build_disp_node.link_to(stat_agg_node, 'stats');
for (const node of edit_input_nodes) {
node.update();
}
@ -1157,5 +1094,6 @@ function builder_graph_init() {
// this will propagate the update to the `stat_agg_node`, and then to damage calc
console.log("Set up graph");
graph_live_update = true;
}

View file

@ -1,4 +1,5 @@
let all_nodes = [];
let node_debug_stack = [];
class ComputeNode {
/**
* Make a generic compute node.
@ -33,6 +34,7 @@ class ComputeNode {
if (this.dirty === 0) {
return;
}
node_debug_stack.push(this.name);
if (this.dirty == 2) {
let calc_inputs = new Map();
for (const input of this.inputs) {
@ -44,6 +46,7 @@ class ComputeNode {
for (const child of this.children) {
child.mark_input_clean(this.name, this.value);
}
node_debug_stack.pop();
return this;
}
@ -174,17 +177,20 @@ class ValueCheckComputeNode extends ComputeNode {
}
let graph_live_update = false;
/**
* Schedule a ComputeNode to be updated.
*
* @param node : ComputeNode to schedule an update for.
*/
function calcSchedule(node, timeout) {
if (!graph_live_update) return;
if (node.update_task !== null) {
clearTimeout(node.update_task);
}
node.mark_dirty();
node.update_task = setTimeout(function() {
node_debug_stack = [];
node.update();
node.update_task = null;
}, timeout);

View file

@ -44,14 +44,16 @@ function init_crafter() {
try {
document.getElementById("recipe-choice").addEventListener("change", (event) => {
updateMaterials();
updateCraftedImage();
calculateCraftSchedule();
});
document.getElementById("recipe-choice").addEventListener("oninput", (event) => {
updateCraftedImage();
});
document.getElementById("level-choice").addEventListener("change", (event) => {
updateMaterials();
calculateCraftSchedule();
});
document.getElementById("recipe-choice").setAttribute("oninput", "updateCraftedImage()");
document.getElementById("recipe-choice").setAttribute("change", "updateCraftedImage()");
for (let i = 1; i < 4; ++i) {
document.getElementById("mat-1-"+i).setAttribute("onclick", document.getElementById("mat-1-"+i).getAttribute("onclick") + "; calculateCraftSchedule();");
@ -177,7 +179,7 @@ function calculateCraft() {
//Display Craft Stats
// displayCraftStats(player_craft, "craft-stats");
let mock_item = player_craft.statMap;
apply_weapon_powders(mock_item);
if (mock_item.get('category') === 'weapon') { apply_weapon_powders(mock_item) };
displayExpandedItem(mock_item, "craft-stats");
//Display Ingredients' Stats

View file

@ -1,4 +1,4 @@
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.10], ["bash", 0.50] ]);
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.00], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]);
function get_base_dps(item) {
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];
@ -26,7 +26,7 @@ function get_base_dps(item) {
}
function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, ignore_speed=false) {
function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ignore_speed=false, part_filter=undefined) {
// TODO: Roll all the loops together maybe
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
@ -38,9 +38,30 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
else {
weapon_damages = damage_keys.map(x => weapon.get(x));
}
let present = weapon.get(damage_present_key);
let present = deepcopy(weapon.get(damage_present_key));
// Also theres prop and rainbow!!
const damage_elements = ['n'].concat(skp_elements); // netwfa
// 2. Conversions.
// 2.0: First, modify conversions.
let conversions = deepcopy(_conversions);
if (part_filter !== undefined) {
const conv_postfix = ':'+part_filter;
for (let i in damage_elements) {
const stat_name = damage_elements[i]+'ConvBase'+conv_postfix;
if (stats.has(stat_name)) {
conversions[i] += stats.get(stat_name);
}
}
}
for (let i in damage_elements) {
const stat_name = damage_elements[i]+'ConvBase';
if (stats.has(stat_name)) {
conversions[i] += stats.get(stat_name);
}
}
// 2.1. First, apply neutral conversion (scale weapon damage). Keep track of total weapon damage here.
let damages = [];
const neutral_convert = conversions[0] / 100;
@ -56,7 +77,7 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
// 2.2. Next, apply elemental conversions using damage computed in step 1.1.
// Also, track which elements are present. (Add onto those present in the weapon itself.)
let total_convert = 0; //TODO get confirmation that this is how raw works.
let total_convert = 0;
for (let i = 1; i <= 5; ++i) {
if (conversions[i] > 0) {
const conv_frac = conversions[i]/100;
@ -66,9 +87,7 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
total_convert += conv_frac
}
}
// Also theres prop and rainbow!!
const damage_elements = ['n'].concat(skp_elements); // netwfa
total_convert += conversions[0]/100;
if (!ignore_speed) {
// 3. Apply attack speed multiplier. Ignored for melee single hit
@ -80,7 +99,7 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
}
// 4. Add additive damage. TODO: Is there separate additive damage?
for (let i = 0; i < 6; ++i) {
for (let i in damage_elements) {
if (present[i]) {
damages[i][0] += stats.get(damage_elements[i]+'DamAddMin');
damages[i][1] += stats.get(damage_elements[i]+'DamAddMax');
@ -94,18 +113,19 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
}
// 5.1: %boost application
let skill_boost = [0]; // no neutral skillpoint booster
for (const skp of skp_order) {
skill_boost.push(skillPointsToPercentage(stats.get(skp)));
for (let i in skp_order) {
const skp = skp_order[i];
skill_boost.push(skillPointsToPercentage(stats.get(skp)) * skillpoint_damage_mult[i]);
}
let static_boost = (stats.get(specific_boost_str.toLowerCase()+'Pct') + stats.get('damPct')) / 100;
// These do not count raw damage. I think. Easy enough to change
let total_min = 0;
let total_max = 0;
for (let i in damages) {
let damage_prefix = damage_elements[i] + specific_boost_str;
for (let i in damage_elements) {
let damage_specific = damage_elements[i] + specific_boost_str + 'Pct';
let damageBoost = 1 + skill_boost[i] + static_boost
+ ((stats.get(damage_prefix+'Pct') + stats.get(damage_elements[i]+'DamPct')) /100);
+ ((stats.get(damage_specific) + stats.get(damage_elements[i]+'DamPct')) /100);
damages[i][0] *= Math.max(damageBoost, 0);
damages[i][1] *= Math.max(damageBoost, 0);
// Collect total damage post %boost
@ -131,13 +151,21 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
let min_boost = raw_boost;
let max_boost = raw_boost;
if (total_max > 0) { // TODO: what about total negative all raw?
if (total_elem_min > 0) {
// TODO: compute actual chance of 0 damage. For now we just copy max ratio
if (total_min === 0) {
min_boost += (damages_obj[1] / total_max) * prop_raw;
}
else {
min_boost += (damages_obj[0] / total_min) * prop_raw;
}
max_boost += (damages_obj[1] / total_max) * prop_raw;
}
if (i != 0 && total_elem_max > 0) { // rainraw TODO above
if (total_elem_min > 0) {
// TODO: compute actual chance of 0 damage. For now we just copy max ratio
if (total_elem_min === 0) {
min_boost += (damages_obj[1] / total_elem_max) * rainbow_raw;
}
else {
min_boost += (damages_obj[0] / total_elem_min) * rainbow_raw;
}
max_boost += (damages_obj[1] / total_elem_max) * rainbow_raw;
@ -152,14 +180,27 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
let total_dam_norm = [0, 0];
let total_dam_crit = [0, 0];
let damages_results = [];
const damage_mult = stats.get("damageMultiplier");
const mult_map = stats.get("damMult");
let damage_mult = 1;
for (const [k, v] of mult_map.entries()) {
if (k.includes(':')) {
// TODO: fragile... checking for specific part multipliers.
const spell_match = k.split(':')[1];
if (spell_match !== part_filter) {
continue;
}
}
damage_mult *= (1 + v/100);
}
const crit_mult = stats.get("critDamPct")/100;
for (const damage of damages) {
const res = [
damage[0] * strBoost * damage_mult, // Normal min
damage[1] * strBoost * damage_mult, // Normal max
damage[0] * (strBoost + 1) * damage_mult, // Crit min
damage[1] * (strBoost + 1) * damage_mult, // Crit max
damage[0] * (strBoost + crit_mult) * damage_mult, // Crit min
damage[1] * (strBoost + crit_mult) * damage_mult, // Crit max
];
damages_results.push(res);
total_dam_norm[0] += res[0];
@ -245,15 +286,6 @@ const default_spells = {
scaling: "melee", use_atkspd: false,
display: "Melee",
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}, {
name: "Heal", // TODO: name for melee attacks? // JUST FOR TESTING...
base_spell: 1,
display: "Total Heal",
parts: [
{ name: "First Pulse", power: 0.12 },
{ name: "Second and Third Pulses", power: 0.06 },
{ name: "Total Heal", hits: { "First Pulse": 1, "Second and Third Pulses": 2 } }
]
}],
spear: [{
type: "replace_spell", // not needed but makes this usable as an "abil part"
@ -294,130 +326,6 @@ const default_spells = {
};
const spell_table = {
"wand": [
{ title: "Heal", cost: 6, parts: [
{ subtitle: "First Pulse", type: "heal", strength: 0.12 },
{ subtitle: "Second and Third Pulses", type: "heal", strength: 0.06 },
{ subtitle: "Total Heal", type: "heal", strength: 0.24, summary: true },
{ subtitle: "First Pulse (Ally)", type: "heal", strength: 0.20 },
{ subtitle: "Second and Third Pulses (Ally)", type: "heal", strength: 0.1 },
{ subtitle: "Total Heal (Ally)", type: "heal", strength: 0.4 }
] },
{ title: "Teleport", cost: 4, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: 150, conversion: [60, 0, 40, 0, 0, 0], summary: true },
] },
{ title: "Meteor", cost: 8, parts: [
{ subtitle: "Blast Damage", type: "damage", multiplier: 500, conversion: [40, 30, 0, 0, 30, 0], summary: true },
{ subtitle: "Burn Damage", type: "damage", multiplier: 125, conversion: [100, 0, 0, 0, 0, 0] },
] },
{ title: "Ice Snake", cost: 4, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: 70, conversion: [50, 0, 0, 50, 0, 0], summary: true },
] },
],
"spear": [
{ title: "Bash", cost: 6, parts: [
{ subtitle: "First Damage", type: "damage", multiplier: 130, conversion: [60, 40, 0, 0, 0, 0]},
{ subtitle: "Explosion Damage", type: "damage", multiplier: 130, conversion: [100, 0, 0, 0, 0, 0]},
{ subtitle: "Total Damage", type: "total", factors: [1, 1], summary: true },
] },
{ title: "Charge", cost: 4, variants: {
DEFAULT: [
{ subtitle: "Total Damage", type: "damage", multiplier: 150, conversion: [60, 0, 0, 0, 40, 0], summary: true }
],
RALLY: [
{ subtitle: "Self Heal", type: "heal", strength: 0.07, summary: true },
{ subtitle: "Ally Heal", type: "heal", strength: 0.15 }
]
} },
{ 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] },
{ subtitle: "Total Damage", type: "total", factors: [1, 1, 1], summary: true },
] },
{ title: "War Scream", cost: 6, parts: [
{ subtitle: "Area Damage", type: "damage", multiplier: 50, conversion: [0, 0, 0, 0, 75, 25], summary: true },
{ subtitle: "Air Shout (Per Hit)", type: "damage", multiplier: 30, conversion: [0, 0, 0, 0, 75, 25] },
] },
],
"bow": [
{ title: "Arrow Storm", cost: 6, variants: {
DEFAULT: [
{ subtitle: "Total Damage", type: "damage", multiplier: 600, conversion: [60, 0, 25, 0, 15, 0], summary: true },
{ subtitle: "Per Arrow (60)", type: "damage", multiplier: 10, conversion: [60, 0, 25, 0, 15, 0]}
],
HAWKEYE: [
{ subtitle: "Total Damage (Hawkeye)", type: "damage", multiplier: 400, conversion: [60, 0, 25, 0, 15, 0], summary: true },
{ subtitle: "Per Arrow (5)", type: "damage", multiplier: 80, conversion: [60, 0, 25, 0, 15, 0]}
],
} },
{ title: "Escape", cost: 3, parts: [
{ subtitle: "Landing Damage", type: "damage", multiplier: 100, conversion: [50, 0, 0, 0, 0, 50], summary: true },
] },
{ title: "Bomb Arrow", cost: 8, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: 250, conversion: [60, 25, 0, 0, 15, 0], summary: true },
] },
{ title: "Arrow Shield", cost: 10, parts: [
{ subtitle: "Shield Damage", type: "damage", multiplier: 100, conversion: [70, 0, 0, 0, 0, 30], summary: true },
{ subtitle: "Arrow Rain Damage", type: "damage", multiplier: 200, conversion: [70, 0, 0, 0, 0, 30] },
] },
],
"dagger": [
{ 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: 2, parts: [
{ subtitle: "No Damage", type: "none", summary: true }
] },
{ title: "Multihit", cost: 8, parts: [
{ subtitle: "1st to 10th Hit", type: "damage", multiplier: 27, conversion: [100, 0, 0, 0, 0, 0] },
{ subtitle: "Fatality", type: "damage", multiplier: 120, conversion: [20, 0, 30, 50, 0, 0] },
{ subtitle: "Total Damage", type: "total", factors: [10, 1], summary: true },
] },
{ title: "Smoke Bomb", cost: 8, variants: {
DEFAULT: [
{ subtitle: "Tick Damage (10 max)", type: "damage", multiplier: 60, conversion: [50, 25, 0, 0, 0, 25] },
{ subtitle: "Total Damage", type: "damage", multiplier: 600, conversion: [50, 25, 0, 0, 0, 25], summary: true },
],
CHERRY_BOMBS: [
{ subtitle: "Total Damage (Cherry Bombs)", type: "damage", multiplier: 330, conversion: [50, 25, 0, 0, 0, 25], summary: true },
{ subtitle: "Per Bomb", type: "damage", multiplier: 110, conversion: [50, 25, 0, 0, 0, 25] }
]
} },
],
"relik": [
{ 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.03, summary: true },
] },
{ title: "Haul", cost: 1, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: 100, conversion: [80, 0, 20, 0, 0, 0], summary: true },
] },
{ title: "Aura", cost: 8, parts: [
{ 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: 100, conversion: [70, 30, 0, 0, 0, 0], summary: true },
] },
],
"sword": [
{ title: "Successive Strikes", cost: 5, parts: [
{ subtitle: "Damage", type: "damage", multiplier: 65, conversion: [70, 0, 15, 0, 0, 15]},
{ subtitle: "Final Strike", type: "damage", multiplier: 120, conversion: [70, 0, 15, 0, 0, 15]},
{ subtitle: "Total Damage (Normal)", type: "total", factors: [2, 0], summary: true },
] },
{ title: "Dash", cost: 3, parts: [
{ subtitle: "Damage", type: "damage", multiplier: 120, conversion: [60, 0, 0, 0, 0, 40], summary: true },
] },
{ title: "Execute", cost: 8, parts: [
{ subtitle: "Minimum Damage", type: "damage", multiplier: 100, conversion: [60, 0, 20, 0, 20, 0]},
{ subtitle: "Maximum Damage", type: "damage", multiplier: 1200, conversion: [60, 0, 20, 0, 20, 0], summary: true },
] },
{ title: "Blade Echo", cost: 4, parts: [
{ subtitle: "Damage", type: "damage", multiplier: 125, conversion: [60, 0, 0, 20, 0, 20], summary: true },
] },
],
"powder": [ //This is how instant-damage powder specials are implemented.
{ title: "Quake", cost: 0, parts:[
{ subtitle: "Total Damage", type: "damage", multiplier: [155, 220, 285, 350, 415], conversion: [0,100,0,0,0,0], summary: true},

View file

@ -5,13 +5,10 @@ function apply_elemental_format(p_elem, id, suffix) {
let parts = idPrefixes[id].split(/ (.*)/);
let element_prefix = parts[0];
let desc = parts[1];
let i_elem = document.createElement('span');
i_elem.classList.add(element_prefix);
i_elem.textContent = element_prefix;
let i_elem = make_elem('span', [element_prefix], {textContent: element_prefix});
p_elem.appendChild(i_elem);
let i_elem2 = document.createElement('span');
i_elem2.textContent = " " + desc + suffix;
let i_elem2 = make_elem('span', [], {textContent: " "+desc+suffix});
p_elem.appendChild(i_elem2);
}
@ -19,17 +16,14 @@ function displaySetBonuses(parent_id,build) {
setHTML(parent_id, "");
let parent_div = document.getElementById(parent_id);
let set_summary_elem = document.createElement('p');
set_summary_elem.classList.add('text-center');
set_summary_elem.textContent = "Set Bonuses";
let set_summary_elem = make_elem('p', ['text-center'], {textContent: "Set Bonuses"});
parent_div.append(set_summary_elem);
for (const [setName, count] of build.activeSetCounts) {
const active_set = sets.get(setName);
if (active_set["hidden"]) { continue; }
let set_elem = document.createElement('p');
set_elem.id = "set-"+setName;
let set_elem = make_elem('p', [], {id: "set-"+setName});
set_summary_elem.append(set_elem);
const bonus = active_set.bonuses[count-1];
@ -111,36 +105,28 @@ function displayBuildStats(parent_id,build,command_group,stats){
}
displayFixedID(parent_div, id, id_val, elemental_format, style);
if (id === "poison" && id_val > 0) {
let row = document.createElement('div');
row.classList.add("row")
let value_elem = document.createElement('div');
value_elem.classList.add('col');
value_elem.classList.add('text-end');
let row = make_elem('div', ['row']);
let value_elem = make_elem('div', ['col', 'text-end']);
let prefix_elem = document.createElement('b');
prefix_elem.textContent = "\u279C With Strength: ";
let number_elem = document.createElement('b');
number_elem.classList.add(style);
number_elem.textContent = (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id];
let prefix_elem = make_elem('b', [], {textContent: "\u279C With Strength: "});
let number_elem = make_elem('b', [style], {
textContent: (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id]
});
value_elem.append(prefix_elem);
value_elem.append(number_elem);
row.appendChild(value_elem);
parent_div.appendChild(row);
}
else if (id === "ls" && id_val != 0) {
let row = document.createElement('div');
row.classList.add("row")
let value_elem = document.createElement('div');
value_elem.classList.add('col');
value_elem.classList.add('text-end');
let row = make_elem('div', ['row']);
let value_elem = make_elem('div', ['col', 'text-end']);
let prefix_elem = document.createElement('b');
prefix_elem.textContent = "\u279C Effective LS: ";
let prefix_elem = make_elem('b', [], {textContent: "\u279C Effective LS: "});
let defStats = getDefenseStats(stats);
let number_elem = document.createElement('b');
number_elem.classList.add(style);
number_elem.textContent = Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s";
let number_elem = make_elem('b', [style], {
textContent: Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s"
});
value_elem.append(prefix_elem);
value_elem.append(number_elem);
row.appendChild(value_elem);
@ -195,8 +181,7 @@ function displayExpandedItem(item, parent_id){
elemental_format = !elemental_format;
}
else if (command === "!spacer") {
let spacer = document.createElement('div');
spacer.classList.add("row", "my-2");
let spacer = make_elem('div', ["row", "my-2"], {});
parent_div.appendChild(spacer);
continue;
}
@ -211,43 +196,35 @@ function displayExpandedItem(item, parent_id){
}
}
if (id === "slots") {
let p_elem = document.createElement("div");
p_elem.classList.add("col");
let p_elem = make_elem("div", ["col"]);
// PROPER POWDER DISPLAYING
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
let powderPrefix = document.createElement("b");
powderPrefix.textContent = "Powder Slots: " + item.get(id) + " [";
p_elem.appendChild(powderPrefix);
p_elem.appendChild(make_elem("b", [], {
textContent: "Powder Slots: " + item.get(id) + " ["
}));
let powders = item.get("powders");
for (let i = 0; i < powders.length; i++) {
let powder = document.createElement("b");
powder.textContent = numerals.get((powders[i]%6)+1)+" ";
powder.classList.add(damageClasses[Math.floor(powders[i]/6)+1]+"_powder");
p_elem.appendChild(powder);
p_elem.appendChild(make_elem("b", [damageClasses[Math.floor(powders[i]/6)+1]+"_powder"], {
textContent: numerals.get((powders[i]%6)+1)+" "
}));
}
let powderSuffix = document.createElement("b");
powderSuffix.textContent = "]";
p_elem.appendChild(powderSuffix);
p_elem.appendChild(make_elem("b", [], { textContent: "]" }));
parent_div.appendChild(p_elem);
} else if (id === "set") {
if (item.get("hideSet")) { continue; }
let p_elem = document.createElement("div");
p_elem.classList.add("col");
p_elem.textContent = "Set: " + item.get(id).toString();
parent_div.appendChild(p_elem);
parent_div.appendChild(make_elem("div", ["col"], { textContent: "Set: " + item.get(id).toString() }));
} else if (id === "majorIds") {
//console.log(item.get(id));
for (let majorID of item.get(id)) {
let p_elem = document.createElement("div");
p_elem.classList.add("col");
let p_elem = make_elem("div", ['col']);
let title_elem = document.createElement("b");
let b_elem = document.createElement("b");
let title_elem = make_elem("b");
let b_elem = make_elem("b");
if (majorID.includes(":")) {
let name = majorID.substring(0, majorID.indexOf(":")+1);
let mid = majorID.substring(majorID.indexOf(":")+1);
@ -268,20 +245,30 @@ function displayExpandedItem(item, parent_id){
parent_div.appendChild(p_elem);
}
} else if (id === "lvl" && item.get("tier") === "Crafted") {
let p_elem = document.createElement("div");
p_elem.classList.add("col");
p_elem.textContent = "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id);
parent_div.appendChild(p_elem);
parent_div.appendChild(make_elem("div", ["col"], {
textContent: "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id)
}));
} else if (id === "displayName") {
let row = document.createElement("div");
let row = make_elem("div", ["row", "justify-content-center"]);
let a_elem = document.createElement("a");
row.classList.add("row", "justify-content-center");
a_elem.classList.add("col-auto", "text-center", "item-title", "p-0");
a_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal");
// a_elem.style.textGrow = 1;
let nolink_row = make_elem("div", ["row", "justify-content-center"]);
nolink_row.style.display = "none";
row.appendChild(a_elem);
const tier_class = item.has("tier") ? item.get("tier").replace(" ","") : "Normal";
let item_link;
if (item.get("custom")) {
item_link = "../custom/#" + item.get("hash");
} else if (item.get("crafted")) {
a_item_link = "../crafter/#" + item.get("hash");
} else {
item_link = "../item/#" + item.get("displayName");
}
const item_name_elem = make_elem("a", ["col-auto", "text-center", "item-title", "p-0", tier_class], {
textContent: item.get('displayName')
});
nolink_row.appendChild(item_name_elem.cloneNode(true));
item_name_elem.href = item_link;
row.appendChild(item_name_elem);
/*
FUNCTIONALITY FOR THIS FEATURE HAS SINCE BEEN REMOVED (WITH SQ2).
@ -295,50 +282,23 @@ function displayExpandedItem(item, parent_id){
//plusminus.style.flexGrow = 0;
//plusminus.textContent = "\u2795";
//row.appendChild(plusminus);
if (item.get("custom")) {
a_elem.href = "../custom/#" + item.get("hash");
a_elem.textContent = item.get("displayName");
} else if (item.get("crafted")) {
a_elem.href = "../crafter/#" + item.get("hash");
a_elem.textContent = item.get(id);
} else {
a_elem.href = "../item/#" + item.get("displayName");
a_elem.textContent = item.get("displayName");
}
parent_div.appendChild(row);
let nolink_row = document.createElement("div");
let p_elem = document.createElement("p");
nolink_row.classList.add("row", "justify-content-center");
nolink_row.style.display = "none";
p_elem.classList.add("col-auto", "text-center", "item-title", "p-0");
p_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal");
if (item.get("custom")) {
p_elem.textContent = item.get("displayName");
} else if (item.get("crafted")) {
p_elem.textContent = item.get(id);
} else {
p_elem.textContent = item.get("displayName");
}
nolink_row.appendChild(p_elem);
parent_div.appendChild(nolink_row);
let img = document.createElement("img");
if (item && item.has("type")) {
img.src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png";
img.alt = item.get("type");
img.style = " z=index: 1; position: relative;";
let container = document.createElement("div");
if (item.has("type")) {
let img = make_elem("img", [], {
src: "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png",
alt: item.get("type"),
style: " z=index: 1; position: relative;"
});
let container = make_elem("div");
let bckgrd = document.createElement("div");
bckgrd.classList.add("col", "px-0", "d-flex", "align-items-center", "justify-content-center");// , "no-collapse");
bckgrd.style = "border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "hsl(0, 0%, 16%) 80%); margin-left: auto; margin-right: auto;"
bckgrd.classList.add("scaled-bckgrd");
parent_div.appendChild(container);
container.appendChild(bckgrd);
let bckgrd = make_elem("div", ["col", "px-0", "d-flex", "align-items-center", "justify-content-center", 'scaled-bckgrd'], { // , "no-collapse"
style: "border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "hsl(0, 0%, 16%) 80%); margin-left: auto; margin-right: auto;"
});
bckgrd.appendChild(img);
container.appendChild(bckgrd);
parent_div.appendChild(container);
}
} else {
if (id.endsWith('Dam_')) {
@ -365,8 +325,7 @@ function displayExpandedItem(item, parent_id){
p_elem.style = "font-style: italic";
} else if (skp_order.includes(id)) { //id = str, dex, int, def, or agi
if ( item.get("tier") !== "Crafted") {
row = document.createElement("div");
row.classList.add("col");
row = make_elem("div", ["col"]);
let title = document.createElement("b");
title.textContent = idPrefixes[id] + " ";
@ -550,9 +509,6 @@ function displayExpandedItem(item, parent_id){
*/
function displayRecipeStats(craft, parent_id) {
let elem = document.getElementById(parent_id);
if (!elem.classList.contains("col")) {
elem.classList.add("col");
}
//local vars
elem.textContent = "";
@ -565,91 +521,85 @@ function displayRecipeStats(craft, parent_id) {
let effectiveness = craft["statMap"].get("ingredEffectiveness");
let title = document.createElement("div");
title.classList.add("row", "box-title", "fw-bold", "justify-content-center");
title.classList.add("col", "box-title", "fw-bold", "justify-content-center", "scaled-font");
title.textContent = "Recipe Stats";
elem.appendChild(title);
let mats = document.createElement("div");
mats.classList.add("row");
mats.classList.add("col");
mats.textContent = "Crafting Materials: ";
elem.appendChild(mats);
for (let i = 0; i < 2; i++) {
let tier = mat_tiers[i];
let row = document.createElement("div");
row.classList.add("row", "px-0", "mx-0");
let b = document.createElement("div");
let col = document.createElement("div");
col.classList.add("col", "ps-4");
let b = document.createElement("span");
let mat = recipe.get("materials")[i];
b.textContent = "- " + mat.get("amount") + "x " + mat.get("item").split(" ").slice(1).join(" ");
b.classList.add("col");
row.appendChild(b);
col.appendChild(b);
let starsB = document.createElement("div");
starsB.classList.add("T1-bracket", "col-auto", "px-0");
let starsContainer = document.createElement("span");
let starsB = document.createElement("span");
starsB.classList.add("T1-bracket", "px-0");
starsB.textContent = "[";
row.appendChild(starsB);
starsContainer.appendChild(starsB);
for(let j = 0; j < 3; j ++) {
let star = document.createElement("div");
star.classList.add("col-auto", "px-0");
let star = document.createElement("span");
star.classList.add("px-0");
star.textContent = "\u272B";
if(j < tier) {
star.classList.add("T1");
} else {
star.classList.add("T0");
}
row.append(star);
starsContainer.append(star);
}
let starsE = document.createElement("div");
starsE.classList.add("T1-bracket", "col-auto", "px-0");
let starsE = document.createElement("span");
starsE.classList.add("T1-bracket", "px-0");
starsE.textContent = "]";
row.appendChild(starsE);
starsContainer.appendChild(starsE);
elem.appendChild(row);
col.appendChild(starsContainer);
elem.appendChild(col);
}
let ingredTable = document.createElement("div");
ingredTable.classList.add("row");
ingredTable.classList.add("col", "mt-2");
for (let i = 0; i < 3; i++) {
let row = document.createElement("div");
row.classList.add("row", "g-1", "justify-content-center");
let ingredContainer = document.createElement("div");
ingredContainer.classList.add("row", "row-cols-2", "g-3");
for (let i = 0; i < 6; i++) {
let ingredCell = document.createElement("div");
ingredCell.classList.add("col");
let ingredTextContainer = document.createElement("div");
ingredTextContainer.classList.add("border", "border-3", "rounded")
for (let j = 0; j < 2; j++) {
if (j == 1) {
let spacer = document.createElement("div");
spacer.classList.add("col-1");
row.appendChild(spacer);
}
let ingredName = ingreds[2 * i + j];
let col = document.createElement("div");
col.classList.add("col-5", "rounded", "dark-6", "border", "border-3", "dark-shadow");
let ingredName = ingreds[i];
let ingred_text = document.createElement("p");
ingred_text.classList.add("mb-2", "ps-2");
ingred_text.textContent = ingredName;
ingredTextContainer.appendChild(ingred_text);
let temp_row = document.createElement("div");
temp_row.classList.add("row");
col.appendChild(temp_row);
let ingred_div = document.createElement("div");
ingred_div.classList.add("col");
ingred_div.textContent = ingredName;
temp_row.appendChild(ingred_div);
let eff_div = document.createElement("div");
eff_div.classList.add("col-auto");
let e = effectiveness[2 * i + j];
let eff_div = document.createElement("p");
eff_div.classList.add("mb-2", "ps-2");
let e = effectiveness[i];
if (e > 0) {
eff_div.classList.add("positive");
} else if (e < 0) {
eff_div.classList.add("negative");
}
eff_div.textContent = "[" + e + "%]";
ingredTextContainer.appendChild(eff_div);
temp_row.appendChild(eff_div);
ingredCell.appendChild(ingredTextContainer);
row.appendChild(col);
}
ingredTable.appendChild(row);
ingredContainer.appendChild(ingredCell);
}
ingredTable.appendChild(ingredContainer);
elem.appendChild(ingredTable);
}
@ -877,7 +827,7 @@ function displayExpandedIngredient(ingred, parent_id) {
row.appendChild(title);
for(const skill of ingred.get("skills")) {
let skill_div = document.createElement("div");
skill_div.classList.add("row");
skill_div.classList.add("row", "ps-4");
skill_div.textContent = skill.charAt(0) + skill.substring(1).toLowerCase();
row.appendChild(skill_div);
}
@ -1039,23 +989,30 @@ function displayFixedID(active, id, value, elemental_format, style) {
function displayPoisonDamage(overallparent_elem, build) {
overallparent_elem.textContent = "";
if (build.statMap.get('poison') <= 0) {
overallparent_elem.style = "display: none";
return;
}
overallparent_elem.style = "";
let container = make_elem('div', ['col', 'pe-0']);
let spell_summary = make_elem('div', ["col", "spell-display", "dark-5", "rounded", "dark-shadow", "py-2", "border", "border-dark"]);
//Title
let title_elemavg = document.createElement("b");
title_elemavg.textContent = "Poison Stats";
overallparent_elem.append(title_elemavg);
let title_elemavg = make_elem("b");
title_elemavg.append(make_elem('span', [], { textContent: "Poison Stats" }));
spell_summary.append(title_elemavg);
let overallpoisonDamage = document.createElement("p");
let overallpoisonDamageFirst = document.createElement("span");
let overallpoisonDamageSecond = document.createElement("span");
let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct"))/100 /3);
overallpoisonDamageFirst.textContent = "Poison Tick: ";
overallpoisonDamageSecond.textContent = Math.max(poison_tick,0);
overallpoisonDamageSecond.classList.add("Damage");
overallpoisonDamage.appendChild(overallpoisonDamageFirst);
overallpoisonDamage.appendChild(overallpoisonDamageSecond);
overallparent_elem.append(overallpoisonDamage);
let overallpoisonDamage = make_elem("p");
overallpoisonDamage.append(
make_elem("span", [], { textContent: "Poison Tick: " }),
make_elem("span", ["Damage"], { textContent: Math.max(poison_tick,0) })
);
spell_summary.append(overallpoisonDamage);
container.append(spell_summary);
overallparent_elem.append(container);
}
function displayEquipOrder(parent_elem, buildOrder){
@ -1072,152 +1029,6 @@ function displayEquipOrder(parent_elem, buildOrder){
}
}
function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) {
let attackSpeeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"];
//let damagePrefixes = ["Neutral Damage: ","Earth Damage: ","Thunder Damage: ","Water Damage: ","Fire Damage: ","Air Damage: "];
parent_elem.textContent = "";
overallparent_elem.textContent = "";
const stats = meleeStats.slice();
for (let i = 0; i < 6; ++i) {
for (let j in stats[i]) {
stats[i][j] = stats[i][j].toFixed(2);
}
}
for (let i = 6; i < 8; ++i) {
for (let j = 0; j < 2; j++) {
stats[i][j] = stats[i][j].toFixed(2);
}
}
for (let i = 8; i < 11; ++i) {
stats[i] = stats[i].toFixed(2);
}
//title
let title_elem = document.createElement("p");
title_elem.classList.add("title");
title_elem.textContent = "Melee Stats";
parent_elem.append(title_elem);
parent_elem.append(document.createElement("br"));
//overall title
let title_elemavg = document.createElement("b");
title_elemavg.textContent = "Melee Stats";
overallparent_elem.append(title_elemavg);
//average DPS
let averageDamage = document.createElement("p");
averageDamage.classList.add("left");
averageDamage.textContent = "Average DPS: " + stats[10];
parent_elem.append(averageDamage);
//overall average DPS
let overallaverageDamage = document.createElement("p");
let overallaverageDamageFirst = document.createElement("span");
overallaverageDamageFirst.textContent = "Average DPS: "
let overallaverageDamageSecond = document.createElement("span");
overallaverageDamageSecond.classList.add("Damage");
overallaverageDamageSecond.textContent = stats[10];
overallaverageDamage.appendChild(overallaverageDamageFirst);
overallaverageDamage.appendChild(overallaverageDamageSecond);
overallparent_elem.append(overallaverageDamage);
//overallparent_elem.append(document.createElement("br"));
//attack speed
let atkSpd = document.createElement("p");
atkSpd.classList.add("left");
atkSpd.textContent = "Attack Speed: " + attackSpeeds[stats[11]];
parent_elem.append(atkSpd);
parent_elem.append(document.createElement("br"));
//overall attack speed
let overallatkSpd = document.createElement("p");
let overallatkSpdFirst = document.createElement("span");
overallatkSpdFirst.textContent = "Attack Speed: ";
let overallatkSpdSecond = document.createElement("span");
overallatkSpdSecond.classList.add("Damage");
overallatkSpdSecond.textContent = attackSpeeds[stats[11]];
overallatkSpd.appendChild(overallatkSpdFirst);
overallatkSpd.appendChild(overallatkSpdSecond);
overallparent_elem.append(overallatkSpd);
//Non-Crit: n->elem, total dmg, DPS
let nonCritStats = document.createElement("p");
nonCritStats.classList.add("left");
nonCritStats.textContent = "Non-Crit Stats: ";
nonCritStats.append(document.createElement("br"));
for (let i = 0; i < 6; i++) {
if (stats[i][1] != 0) {
let dmg = document.createElement("p");
dmg.textContent = stats[i][0] + " \u2013 " + stats[i][1];
dmg.classList.add(damageClasses[i]);
dmg.classList.add("itemp");
nonCritStats.append(dmg);
}
}
let normalDamage = document.createElement("p");
normalDamage.textContent = "Total: " + stats[6][0] + " \u2013 " + stats[6][1];
nonCritStats.append(normalDamage);
let normalDPS = document.createElement("p");
normalDPS.textContent = "Normal DPS: " + stats[8];
nonCritStats.append(normalDPS);
//overall average DPS
let singleHitDamage = document.createElement("p");
let singleHitDamageFirst = document.createElement("span");
singleHitDamageFirst.textContent = "Single Hit Average: ";
let singleHitDamageSecond = document.createElement("span");
singleHitDamageSecond.classList.add("Damage");
singleHitDamageSecond.textContent = stats[12].toFixed(2);
singleHitDamage.appendChild(singleHitDamageFirst);
singleHitDamage.appendChild(singleHitDamageSecond);
overallparent_elem.append(singleHitDamage);
let normalChance = document.createElement("p");
normalChance.textContent = "Non-Crit Chance: " + (stats[6][2]*100).toFixed(2) + "%";
normalChance.append(document.createElement("br"));
normalChance.append(document.createElement("br"));
nonCritStats.append(normalChance);
parent_elem.append(nonCritStats);
parent_elem.append(document.createElement("br"));
//Crit: n->elem, total dmg, DPS
let critStats = document.createElement("p");
critStats.classList.add("left");
critStats.textContent = "Crit Stats: ";
critStats.append(document.createElement("br"));
for (let i = 0; i < 6; i++){
if(stats[i][3] != 0) {
dmg = document.createElement("p");
dmg.textContent = stats[i][2] + " \u2013 " + stats[i][3];
dmg.classList.add(damageClasses[i]);
dmg.classList.add("itemp");
critStats.append(dmg);
}
}
let critDamage = document.createElement("p");
critDamage.textContent = "Total: " + stats[7][0] + " \u2013 " + stats[7][1];
critStats.append(critDamage);
let critDPS = document.createElement("p");
critDPS.textContent = "Crit DPS: " + stats[9];
critStats.append(critDPS);
let critChance = document.createElement("p");
critChance.textContent = "Crit Chance: " + (stats[7][2]*100).toFixed(2) + "%";
critChance.append(document.createElement("br"));
critChance.append(document.createElement("br"));
critStats.append(critChance);
parent_elem.append(critStats);
addClickableArrow(overallparent_elem, parent_elem);
}
function displayDefenseStats(parent_elem, statMap, insertSummary){
let defenseStats = getDefenseStats(statMap);
insertSummary = (typeof insertSummary !== 'undefined') ? insertSummary : false;
@ -1424,6 +1235,13 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){
}
function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overall=false) {
parent_elem.textContent = "";
if (powderSpecials.length === 0) {
parent_elem.style = "display: none";
return;
}
parent_elem.style = "";
const skillpoints = [
stats.get('str'),
stats.get('dex'),
@ -1431,16 +1249,13 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
stats.get('def'),
stats.get('agi')
];
parent_elem.textContent = ""
let title = document.createElement("b");
title.textContent = "Powder Specials";
parent_elem.appendChild(title);
parent_elem.append(make_elem("b", [], { textContent: "Powder Specials" }));
let specials = powderSpecials.slice();
let expandedStats = new Map();
//each entry of powderSpecials is [ps, power]
for (special of specials) {
//iterate through the special and display its effects.
let powder_special = document.createElement("p");
let powder_special = make_elem("p", ["pt-3"]);
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("p");
let specialEffects = document.createElement("p");
@ -1572,7 +1387,7 @@ function getSpellCost(stats, spell) {
function getBaseSpellCost(stats, spell) {
// old intelligence:
let cost = spell.cost; //Math.ceil(spell.cost * (1 - skillPointsToPercentage(stats.get('int'))));
let cost = Math.ceil(spell.cost * (1 - skillPointsToPercentage(stats.get('int')) * skillpoint_final_mult[2]));
cost += stats.get("spRaw"+spell.base_spell);
return Math.floor(cost * (1 + stats.get("spPct"+spell.base_spell) / 100));
}
@ -1583,31 +1398,23 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
// TODO: move cost calc out
parent_elem.textContent = "";
let title_elem = document.createElement("p");
let title_elem = make_elem("p");
overallparent_elem.textContent = "";
let title_elemavg = document.createElement("b");
if ('cost' in spell) {
let first = document.createElement("span");
first.textContent = spell.name + " (";
let first = make_elem("span", [], { textContent: spell.name + " (" });
title_elem.appendChild(first.cloneNode(true)); //cloneNode is needed here.
title_elemavg.appendChild(first);
let second = document.createElement("span");
second.textContent = getSpellCost(stats, spell);
second.classList.add("Mana");
let second = make_elem("span", ["Mana"], { textContent: getSpellCost(stats, spell) });
title_elem.appendChild(second.cloneNode(true));
title_elemavg.appendChild(second);
let third = document.createElement("span");
third.textContent = ")";// [Base: " + getBaseSpellCost(stats, spellIdx, spell.cost) + " ]";
title_elem.appendChild(third);
let third_summary = document.createElement("span");
third_summary.textContent = ")";
title_elemavg.appendChild(third_summary);
let third = make_elem("span", [], { textContent: ")" });// " + getBaseSpellCost(stats, spellIdx, spell.cost) + " ]";
title_elem.appendChild(third.cloneNode(true));
title_elemavg.appendChild(third);
}
else {
title_elem.textContent = spell.name;
@ -1624,30 +1431,26 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
let critChance = skillPointsToPercentage(stats.get('dex'));
let part_divavg = document.createElement("p");
let part_divavg = make_elem("p");
overallparent_elem.append(part_divavg);
function _summary(text, val, fmt) {
let overallaverageLabel = document.createElement("p");
let first = document.createElement("span");
let second = document.createElement("span");
first.textContent = text;
second.textContent = val.toFixed(2);
overallaverageLabel.appendChild(first);
overallaverageLabel.appendChild(second);
second.classList.add(fmt);
part_divavg.append(overallaverageLabel);
function add_summary(text, val, fmt) {
if (typeof(val) === 'number') { val = val.toFixed(2); }
let summary_elem = make_elem("p");
summary_elem.append(
make_elem("span", [], { textContent: text }),
make_elem("span", [fmt], { textContent: val })
);
part_divavg.append(summary_elem);
}
for (let i = 0; i < spell_results.length; ++i) {
const spell_info = spell_results[i];
let part_div = document.createElement("p");
let part_div = make_elem("p", ["pt-3"]);
parent_elem.append(part_div);
let subtitle_elem = document.createElement("p");
subtitle_elem.textContent = spell_info.name
part_div.append(subtitle_elem);
part_div.append(make_elem("p", [], { textContent: spell_info.name }));
if (spell_info.type === "damage") {
let totalDamNormal = spell_info.normal_total;
@ -1657,14 +1460,27 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
let averageLabel = document.createElement("p");
averageLabel.textContent = "Average: "+averageDamage.toFixed(2);
let averageLabel = make_elem("p", [], { textContent: "Average: "+averageDamage.toFixed(2) });
// averageLabel.classList.add("damageSubtitle");
part_div.append(averageLabel);
if (spell_info.name === spell.display) {
_summary(spell_info.name+ ": ", averageDamage, "Damage");
if (spellIdx === 0) {
let display_attack_speeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"];
let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier");
console.log(stats);
if(adjAtkSpd > 6) {
adjAtkSpd = 6;
} else if(adjAtkSpd < 0) {
adjAtkSpd = 0;
}
add_summary("Average DPS: ", averageDamage * baseDamageMultiplier[adjAtkSpd], "Damage");
add_summary("Attack Speed: ", display_attack_speeds[adjAtkSpd], "Damage");
add_summary("Per Attack: ", averageDamage, "Damage");
}
else {
add_summary(spell_info.name+ ": ", averageDamage, "Damage");
}
}
function _damage_display(label_text, average, dmg_min, dmg_max) {
@ -1690,7 +1506,7 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
// healLabel.classList.add("damagep");
part_div.append(healLabel);
if (spell_info.name === spell.display) {
_summary(spell_info.name+ ": ", heal_amount, "Set");
add_summary(spell_info.name+ ": ", heal_amount, "Set");
}
}
}
@ -1900,6 +1716,7 @@ function displayIDProbabilities(parent_id, item, amp) {
parent_elem.appendChild(table_elem);
for (const [id,val] of Object.entries(itemMap.get(item_name))) {
if (rolledIDs.includes(id)) {
if (!item.get("maxRolls").get(id)) { continue; }
let min = item.get("minRolls").get(id);
let max = item.get("maxRolls").get(id);
//Apply corkian amps
@ -2110,12 +1927,10 @@ function stringCDF(id,val,base,amp) {
function addClickableArrow(elem, target) {
//up and down arrow - done ugly
let arrow = document.createElement("img");
arrow.id = "arrow_" + elem.id;
let arrow = make_elem("img", [], { id: "arrow_" + elem.id, src: "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png" });
arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem";
arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png";
elem.appendChild(arrow);
arrow.addEventListener("click", () => toggle_spell_tab(arrow, target));
elem.addEventListener("click", () => toggle_spell_tab(arrow, target));
}
// toggle arrow thinger

View file

@ -20,14 +20,17 @@ function init_itempage() {
//displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view");
try{
item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []);
displaysq2ExpandedItem(item, "item-view");
displaysq2AdditionalInfo("additional-info", item);
displaysq2IDCosts("identification-costs", item);
if (item.get("set") && sets[item.get("set")]) {
displaysq2AllSetBonuses("set-bonus-info",item.get("set"));
if (item.get('category') === 'weapon') {
item.set('powders', []);
apply_weapon_powders(item);
}
console.log(item);
displaysq2IDProbabilities("identification-probabilities", item, amp_state);
displayExpandedItem(item, "item-view");
displayAdditionalInfo("additional-info", item);
displayIDCosts("identification-costs", item);
if (item.get("set") && sets[item.get("set")]) {
displayAllSetBonuses("set-bonus-info",item.get("set"));
}
displayIDProbabilities("identification-probabilities", item, amp_state);
} catch (error) {
console.log(error);
console.log(error.stack);
@ -56,6 +59,8 @@ function toggleAmps(button_id) {
}
load_init(init_itempage);
//load_ing_init(init);
(async function() {
let load_promises = [ load_init() ];
await Promise.all(load_promises);
init_itempage();
})();

View file

@ -1,4 +1,4 @@
const DB_VERSION = 91;
const DB_VERSION = 99;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
let db;

View file

@ -1,4 +1,4 @@
const ING_DB_VERSION = 13;
const ING_DB_VERSION = 14;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
@ -9,13 +9,13 @@ let iload_complete = false;
let ings;
let recipes;
let ingMap;
let ingMap = new Map();
let ingList = [];
let recipeMap;
let recipeList = [];
let ingIDMap;
let ingIDMap = new Map();
let recipeIDMap;
/*
@ -163,39 +163,41 @@ async function load_ing_init() {
}
function init_ing_maps() {
ingMap = new Map();
recipeMap = new Map();
ingIDMap = new Map();
recipeIDMap = new Map();
let ing = Object();
ing.name = "No Ingredient";
ing.displayName = "No Ingredient";
ing.tier = 0;
ing.lvl = 0;
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"];
ing.ids= {};
ing.itemIDs = {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
ing.consumableIDs = {"dura": 0, "charges": 0};
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
ing.id = 4000;
ingMap.set(ing["displayName"], ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
let ing = {
name: "No Ingredient",
displayName: "No Ingredient",
tier: 0,
lvl: 0,
skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"],
ids: {},
itemIDs: {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,},
consumableIDs: {"dura": 0, "charges": 0},
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0},
id: 4000
};
ingMap.set(ing.displayName, ing);
ingList.push(ing.displayName);
ingIDMap.set(ing.id, ing.displayName);
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
for (let i = 0; i < 5; i ++) {
for (const powderIng of powderIngreds) {
let ing = Object();
ing.name = "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1);
ing.displayName = ing.name
ing.tier = 0;
ing.lvl = 0;
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"];
ing.ids = {};
ing.isPowder = true;
ing.pid = 6*i + powderIngreds.indexOf(powderIng);
let ing = {
name: "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1),
tier: 0,
lvl: 0,
skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"],
ids: {},
isPowder: true,
pid: 6*i + powderIngreds.indexOf(powderIng),
itemIDs: {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0},
consumableIDs: {"dura": 0, "charges": 0},
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}
};
ing.id = 4001 + ing.pid;
ing.itemIDs = {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
ing.diplayName = ing.name;
switch(i) {
case 0:
ing.itemIDs["strReq"] = powderIng["skpReq"];
@ -213,24 +215,21 @@ function init_ing_maps() {
ing.itemIDs["agiReq"] = powderIng["skpReq"];
break;
}
ing.consumableIDs = {"dura": 0, "charges": 0};
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
ingMap.set(ing["displayName"],ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
ingMap.set(ing.displayName, ing);
ingList.push(ing.displayName);
ingIDMap.set(ing.id, ing.displayName);
}
}
for (const ing of ings) {
ingMap.set(ing["displayName"], ing);
ingList.push(ing["displayName"]);
ingIDMap.set(ing["id"], ing["displayName"]);
ingMap.set(ing.displayName, ing);
ingList.push(ing.displayName);
ingIDMap.set(ing.id, ing.displayName);
}
for (const recipe of recipes) {
recipeMap.set(recipe["name"], recipe);
recipeList.push(recipe["name"]);
recipeIDMap.set(recipe["id"],recipe["name"]);
recipeMap.set(recipe.name, recipe);
recipeList.push(recipe.name);
recipeIDMap.set(recipe.id, recipe.name);
}
console.log(ingMap);
}

View file

@ -169,14 +169,14 @@ function calc_weapon_powder(weapon, damageBases) {
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
let max_diff = Math.min(neutralRemainingRaw[1], conversionRatio * neutralBase[1]);
//damages[element+1][0] = Math.floor(round_near(damages[element+1][0] + min_diff));
//damages[element+1][1] = Math.floor(round_near(damages[element+1][1] + max_diff));
//neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
//neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
damages[element+1][0] += min_diff;
damages[element+1][1] += max_diff;
neutralRemainingRaw[0] -= min_diff;
neutralRemainingRaw[1] -= max_diff;
damages[element+1][0] = Math.floor(round_near(damages[element+1][0] + min_diff));
damages[element+1][1] = Math.floor(round_near(damages[element+1][1] + max_diff));
neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
//damages[element+1][0] += min_diff;
//damages[element+1][1] += max_diff;
//neutralRemainingRaw[0] -= min_diff;
//neutralRemainingRaw[1] -= max_diff;
}
damages[element+1][0] += powder.min;
damages[element+1][1] += powder.max;

View file

@ -1,32 +1,14 @@
function calculate_skillpoints(equipment, weapon) {
// Calculate equipment equipping order and required skillpoints.
// Return value: [equip_order, best_skillpoints, final_skillpoints, best_total];
let fixed = [];
let consider = [];
let noboost = [];
let crafted = [];
for (const item of equipment) {
if (item.get("crafted")) {
crafted.push(item);
}
else if (item.get("reqs").every(x => x === 0) && item.get("skillpoints").every(x => x >= 0)) {
// All reqless item without -skillpoints.
fixed.push(item);
}
// TODO hack: We will treat ALL set items as unsafe :(
else if (item.get("skillpoints").every(x => x === 0) && item.get("set") === null) {
noboost.push(item);
}
else {
consider.push(item);
}
}
/**
* Apply skillpoint bonuses from an item.
* Also applies set deltas.
* Modifies the skillpoints array.
*/
function apply_skillpoints(skillpoints, item, activeSetCounts) {
for (let i = 0; i < 5; i++) {
skillpoints[i] += item.get("skillpoints")[i];
skillpoints[i] += item.skillpoints[i];
}
const setName = item.get("set");
const setName = item.set;
if (setName) { // undefined/null means no set.
let setCount = activeSetCounts.get(setName);
let old_bonus = {};
@ -47,30 +29,33 @@ function calculate_skillpoints(equipment, weapon) {
}
}
/**
* Apply skillpoints until this item can be worn.
* Also applies set deltas.
* Confusingly, does not modify the skillpoints array.
* Instead, return an array of deltas.
*/
function apply_to_fit(skillpoints, item, skillpoint_min, activeSetCounts) {
let applied = [0, 0, 0, 0, 0];
let total = 0;
for (let i = 0; i < 5; i++) {
if (item.get("skillpoints")[i] < 0 && skillpoint_min[i]) {
const unadjusted = skillpoints[i] + item.get("skillpoints")[i];
if (item.skillpoints[i] < 0 && skillpoint_min[i]) {
const unadjusted = skillpoints[i] + item.skillpoints[i];
const delta = skillpoint_min[i] - unadjusted;
if (delta > 0) {
applied[i] += delta;
total += delta;
}
}
if (item.get("reqs")[i] == 0) continue;
skillpoint_min[i] = Math.max(skillpoint_min[i], item.get("reqs")[i] + item.get("skillpoints")[i]);
const req = item.get("reqs")[i];
if (item.reqs[i] == 0) continue;
skillpoint_min[i] = Math.max(skillpoint_min[i], item.reqs[i] + item.skillpoints[i]);
const req = item.reqs[i];
const cur = skillpoints[i];
if (req > cur) {
const diff = req - cur;
applied[i] += diff;
total += diff;
}
}
const setName = item.get("set");
const setName = item.set;
if (setName) { // undefined/null means no set.
const setCount = activeSetCounts.get(setName);
if (setCount) {
@ -84,15 +69,48 @@ function calculate_skillpoints(equipment, weapon) {
const delta = skillpoint_min[i] - unadjusted;
if (delta > 0) {
applied[i] += delta;
total += delta;
}
}
}
}
}
return applied;
}
function calculate_skillpoints(equipment, weapon) {
// const start = performance.now();
// Calculate equipment equipping order and required skillpoints.
// Return value: [equip_order, best_skillpoints, final_skillpoints, best_total];
let fixed = [];
let consider = [];
let noboost = [];
let crafted = [];
weapon.skillpoints = weapon.get('skillpoints');
weapon.reqs = weapon.get('reqs');
weapon.set = weapon.get('set');
for (const item of equipment) {
item.skillpoints = item.get('skillpoints');
item.reqs = item.get('reqs');
item.set = item.get('set');
if (item.get("crafted")) {
crafted.push(item);
}
// TODO hack: We will treat ALL set items as unsafe :(
else if (item.set !== null) {
consider.push(item);
}
else if (item.get("reqs").every(x => x === 0) && item.skillpoints.every(x => x >= 0)) {
// All reqless item without -skillpoints.
fixed.push(item);
}
else if (item.skillpoints.every(x => x <= 0)) {
noboost.push(item);
}
else {
consider.push(item);
}
}
return [applied, total];
}
// Separate out the no req items and add them to the static skillpoint base.
let static_skillpoints_base = [0, 0, 0, 0, 0]
@ -101,7 +119,7 @@ function calculate_skillpoints(equipment, weapon) {
apply_skillpoints(static_skillpoints_base, item, static_activeSetCounts);
}
let best = consider.concat(noboost);
let best = consider;
let final_skillpoints = static_skillpoints_base.slice();
let best_skillpoints = [0, 0, 0, 0, 0];
let best_total = Infinity;
@ -110,100 +128,125 @@ function calculate_skillpoints(equipment, weapon) {
let allFalse = [0, 0, 0, 0, 0];
if (consider.length > 0 || noboost.length > 0 || crafted.length > 0) {
// Try every combination and pick the best one.
for (let permutation of perm(consider)) {
let activeSetCounts = new Map(static_activeSetCounts);
const [root, terminal, sccs] = construct_scc_graph(consider);
const end_checks = crafted.concat(noboost);
end_checks.push(weapon);
let has_skillpoint = allFalse.slice();
permutation = permutation.concat(noboost);
let skillpoints_applied = [0, 0, 0, 0, 0];
// Complete slice is a shallow copy.
let skillpoints = static_skillpoints_base.slice();
let total_applied = 0;
let result;
let needed_skillpoints;
let total_diff;
for (const item of permutation) {
result = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts);
needed_skillpoints = result[0];
total_diff = result[1];
function check_end(skillpoints_applied, skillpoints, activeSetCounts, total_applied) {
// Crafted skillpoint does not count initially.
for (const item of end_checks) {
const needed_skillpoints = apply_to_fit(skillpoints, item,
[false, false, false, false, false], activeSetCounts);
for (let i = 0; i < 5; ++i) {
skillpoints_applied[i] += needed_skillpoints[i];
skillpoints[i] += needed_skillpoints[i];
const skp = needed_skillpoints[i]
skillpoints_applied[i] += skp;
skillpoints[i] += skp;
total_applied += skp;
}
if (best_total < total_applied) { return -1; }
}
return total_applied;
}
function permute_check(idx, _applied, _skillpoints, _sets, _has, _total_applied, order) {
const {nodes, children} = sccs[idx];
if (nodes[0] === terminal) {
const total = check_end(_applied, _skillpoints, _sets, _total_applied);
if (total !== -1 && total < best_total) {
final_skillpoints = _skillpoints;
best_skillpoints = _applied;
best_total = total;
best_activeSetCounts = _sets;
best = order;
}
return;
}
for (let permutation of perm(nodes)) {
const skillpoints_applied = _applied.slice();
const skillpoints = _skillpoints.slice();
const activeSetCounts = new Map(_sets);
const has_skillpoint = _has.slice();
let total_applied = _total_applied;
let short_circuit = false;
for (const {item} of permutation) {
needed_skillpoints = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts);
for (let i = 0; i < 5; ++i) {
skp = needed_skillpoints[i];
skillpoints_applied[i] += skp;
skillpoints[i] += skp;
total_applied += skp;
}
if (total_applied >= best_total) {
short_circuit = true;
break; // short circuit failure
}
apply_skillpoints(skillpoints, item, activeSetCounts);
total_applied += total_diff;
if (total_applied >= best_total) {
break;
}
if (short_circuit) { continue; }
permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item)));
}
}
// skip root.
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []);
// Crafted skillpoint does not count initially.
for (const item of crafted) {
//console.log(item)
result = apply_to_fit(skillpoints, item, allFalse.slice(), activeSetCounts);
//console.log(result)
needed_skillpoints = result[0];
total_diff = result[1];
for (let i = 0; i < 5; ++i) {
skillpoints_applied[i] += needed_skillpoints[i];
skillpoints[i] += needed_skillpoints[i];
}
total_applied += total_diff;
}
if (total_applied >= best_total) {
continue;
}
let pre = skillpoints.slice();
result = apply_to_fit(skillpoints, weapon, allFalse.slice(), activeSetCounts);
needed_skillpoints = result[0];
total_diff = result[1];
for (let i = 0; i < 5; ++i) {
skillpoints_applied[i] += needed_skillpoints[i];
skillpoints[i] += needed_skillpoints[i];
}
apply_skillpoints(skillpoints, weapon, activeSetCounts);
total_applied += total_diff;
// add extra sp bonus
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);
// Applying crafted item skill points last.
for (const item of crafted) {
apply_skillpoints(skillpoints, item, activeSetCounts);
//total_applied += total_diff;
apply_skillpoints(final_skillpoints, item, best_activeSetCounts);
}
if (total_applied < best_total) {
best = permutation;
final_skillpoints = skillpoints;
best_skillpoints = skillpoints_applied;
best_total = total_applied;
best_activeSetCounts = activeSetCounts;
}
}
}
else {
best_total = 0;
result = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts);
needed_skillpoints = result[0];
total_diff = result[1];
needed_skillpoints = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts);
for (let i = 0; i < 5; ++i) {
best_skillpoints[i] += needed_skillpoints[i];
final_skillpoints[i] += needed_skillpoints[i];
const skp = needed_skillpoints[i];
best_skillpoints[i] += skp;
final_skillpoints[i] += skp;
best_total += skp;
}
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);
best_total += total_diff;
}
let equip_order = fixed.concat(best).concat(crafted);
let equip_order = fixed.concat(best).concat(noboost).concat(crafted);
// best_skillpoints: manually assigned (before any gear)
// final_skillpoints: final totals (5 individ)
// best_total: total skillpoints assigned (number)
// const end = performance.now();
// const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`;
// console.log(output_msg);
return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts];
}
function construct_scc_graph(items_to_consider) {
let nodes = [];
let terminal_node = {
item: null,
children: [],
parents: nodes
};
let root_node = {
item: null,
children: nodes,
parents: [],
};
for (const item of items_to_consider) {
nodes.push({item: item, children: [terminal_node], parents: [root_node]});
}
// Dependency graph construction.
for (const node_a of nodes) {
const {item: a, children: a_children} = node_a;
for (const node_b of nodes) {
const {item: b, parents: b_parents} = node_b;
for (let i = 0; i < 5; ++i) {
if (a.skillpoints[i] > 0 && (a.reqs[i] < b.reqs[i] || b.skillpoints[i] < 0)) {
a_children.push(node_b);
b_parents.push(node_a);
break;
}
}
}
}
const sccs = make_SCC_graph(root_node, nodes);
return [root_node, terminal_node, sccs];
}

View file

@ -1,57 +1,4 @@
document.addEventListener("DOMContentLoaded", function() {
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;
};
},
},
}
}));
};
});
let itemCategories = [ "armor", "accessory", "weapon" ];
const sq2_translate_mappings = {
//"Name": "name",
@ -184,7 +131,11 @@ function displayItems(items_copy) {
box.appendChild(bckgrdbox);
bckgrdbox.id = "item"+i+"b";
items_parent.appendChild(box);
displaysq2ExpandedItem(item, bckgrdbox.id);
item.set("powders", []);
if (item.get("category") == "weapon") {
apply_weapon_powders(item);
}
displayExpandedItem(item, bckgrdbox.id, true);
}
}
@ -218,12 +169,12 @@ function doItemSearch() {
for (let i = 1; i <= 4; ++i) {
let raw_dat = document.getElementById("filter"+i+"-choice").value;
let filter_dat = translate_mappings[raw_dat];
let filter_dat = sq2_translate_mappings[raw_dat];
if (filter_dat !== undefined) {
queries.push(new IdQuery(filter_dat));
continue;
}
filter_dat = special_mappings[raw_dat];
filter_dat = sq2_special_mappings[raw_dat];
if (filter_dat !== undefined) {
queries.push(filter_dat);
continue;
@ -251,6 +202,63 @@ function resetItemSearch() {
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;
};
},
},
}
}));
};
}
load_init(init_items);
(async function() {
await Promise.resolve(load_init());
init_items();
})();

View file

@ -630,8 +630,15 @@ function addClasses(elem, classes) {
*/
async function hardReload() {
//https://gist.github.com/rmehner/b9a41d9f659c9b1c3340
try {
const dbs = await window.indexedDB.databases();
await dbs.forEach(db => { window.indexedDB.deleteDatabase(db.name) });
} catch (error) {
// Hacky patch for firefox...
console.log(error);
const db_names = ['item_db', 'ing_db', 'map_db', 'tome_db'];
await db_names.forEach(db => { window.indexedDB.deleteDatabase(db) });
}
location.reload(true);
}
@ -770,112 +777,204 @@ function assert_error(func_binding, msg) {
/**
* Deep copy object/array of basic types.
*/
function deepcopy(obj) {
function deepcopy(obj, refs=undefined) {
if (refs === undefined) {
refs = new Map();
}
if (typeof(obj) !== 'object' || obj === null) { // null or value type
return obj;
}
let ret = Array.isArray(obj) ? [] : {};
for (let key in obj) {
ret[key] = deepcopy(obj[key]);
let val;
try {
val = obj[key];
} catch (exc) {
console.trace();
val = undefined;
}
if (typeof(obj) === 'object') {
if (refs.has(val)) {
ret[key] = refs.get(val);
}
else {
refs.set(val, val);
ret[key] = deepcopy(val, refs);
}
}
else {
ret[key] = val;
}
}
return ret;
}
/**
*
*/
function gen_slider_labeled({label_name, label_classlist = [], min = 0, max = 100, step = 1, default_val = min, id = undefined, color = "#FFFFFF", classlist = []}) {
let slider_container = document.createElement("div");
function bv_test() {
console.log("=====STARTING BITVECTOR UNIT TESTS=====");
let buf_col = document.createElement("div");
buf_col.classList.add("col");
// Empty Constructor + append str
let bv = new BitVector("");
bv.append("Bc8");
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 18);
assert_equals(bv.toB64(), "Bc8");
let label = document.createElement("div");
label.classList.add("col");
label.classList.add(...label_classlist);
label.textContent = label_name + ": " + default_val;
// Empty Constructor + append num 1
bv = new BitVector("");
bv.append(10000, 18);
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 18);
assert_equals(bv.toB64(), "GS2"); //have to read backwards
let slider = gen_slider(min, max, step, default_val, id, color, classlist, label);
// Empty Constructor + append num 1
bv = new BitVector("");
bv.append(10000, 14);
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 14);
assert_equals(bv.toB64(), "GS2"); //have to read backwards
//we set IDs here because the slider's id is potentially only meaningful after gen_slider() is called
label.id = slider.id + "_label";
slider_container.id = slider.id + "-container";
// 1-int constructor (num)
bv = new BitVector(10000, 14);
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 14);
assert_equals(bv.toB64(), "GS2");
buf_col.append(slider, label);
slider_container.appendChild(buf_col);
// 1-int constructor (str)
bv = new BitVector("abcde");
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 30);
assert_equals(bv.toB64(), "abcde");
// test constructor ignore length when data is str
bv = new BitVector("abcde", 40);
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 30);
assert_equals(bv.toB64(), "abcde");
bv = new BitVector("abcdefghij", 20);
assert_equals(bv.bits.length, 2);
assert_equals(bv.length, 60);
assert_equals(bv.toB64(), "abcdefghij");
//test 32-bit length constructor
bv = new BitVector(2**31, 32);
assert_equals(bv.bits.length, 1);
assert_equals(bv.length, 32);
//test multiple length constructor (+ get to last bit)
bv = new BitVector("abcdefghijklmnop");
assert_equals(bv.bits.length, 3);
assert_equals(bv.length, 96);
assert_equals(bv.toB64(), "abcdefghijklmnop");
//test append resize w num 1
bv = new BitVector("abcd");
bv.append(10000, 14);
assert_equals(bv.bits.length, 2);
assert_equals(bv.length, 38);
assert_equals(bv.slice(24, 38), 10000);
assert_equals(bv.toB64().slice(0, 4), "abcd");
//test append resize w num 2
bv = new BitVector("abcdefghi");
bv.append(10000, 14);
assert_equals(bv.bits.length, 4);
assert_equals(bv.length, 68);
assert_equals(bv.slice(54, 68), 10000);
assert_equals(bv.toB64().slice(0, 9), "abcdefghi");
//test append resize w str 1
bv = new BitVector("abcd");
bv.append("efgh");
assert_equals(bv.bits.length, 2);
assert_equals(bv.length, 48);
assert_equals(bv.toB64(), "abcdefgh");
//test append resize w str 2
bv = new BitVector("abcd");
bv.append("efghijklmnopqrstuvwxyz");
assert_equals(bv.bits.length, 8);
assert_equals(bv.length, 156);
assert_equals(bv.toB64(), "abcdefghijklmnopqrstuvwxyz");
//test append resize w str 3
bv = new BitVector("abcdefgh");
bv.append("ijkl");
assert_equals(bv.bits.length, 4);
assert_equals(bv.length, 72);
assert_equals(bv.toB64(), "abcdefghijkl");
console.log("=====FINISHED BITVECTOR UNIT TESTS=====");
return slider_container;
}
bv_test();
/** Creates a slider input (input type = range) given styling parameters
*
* @param {Number | String} min - The minimum value for the slider. defaults to 0
* @param {Number | String} max - The maximum value for the slider. defaults to 100
* @param {Number | String} step - The granularity between possible values. defaults to 1
* @param {Number | String} default_val - The default value to set the slider to.
* @param {String} id - The element ID to use for the slider. defaults to the current date time
* @param {String} color - The hex color to use for the slider. Needs the # character.
* @param {Array<String>} classlist - A list of classes to add to the slider.
* @returns
*/
function gen_slider(min = 0, max = 100, step = 1, default_val = min, id = undefined, color = "#FFFFFF", classlist = [], label = undefined) {
//simple attribute vals
let slider = document.createElement("input");
slider.type = "range";
slider.min = min;
slider.max = max;
slider.step = step;
slider.value = default_val;
slider.autocomplete = "off";
if (id) {
if (document.getElementById(id)) {
throw new Error("ID " + id + " already exists within the DOM.")
} else {
slider.id = id;
}
} else {
slider.id = new Date().toLocaleTimeString();
}
slider.color = color;
slider.classList.add(...classlist); //special spread operator -
//necessary for display purposes
slider.style.webkitAppearance = "none";
slider.style.borderRadius = "30px";
slider.style.height = "0.5rem";
slider.classList.add("px-0", "slider");
//set up recoloring
slider.addEventListener("change", function(e) {
recolor_slider(slider, label);
});
//do recoloring for the default val
let pct = Math.round(100 * (parseInt(slider.value) - parseInt(slider.min)) / (parseInt(slider.max) - parseInt(slider.min)));
slider.style.background = `rgba(0, 0, 0, 0) linear-gradient(to right, ${color}, ${color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
//return slider
return slider;
}
/** Recolors a slider. If the corresponding label exists, also update that.
*
* @param {slider} slider - the slider element
* @param {label} label - the label element
*/
function recolor_slider(slider, label) {
let color = slider.color;
let pct = Math.round(100 * (parseInt(slider.value) - parseInt(slider.min)) / (parseInt(slider.max) - parseInt(slider.min)));
slider.style.background = `rgba(0, 0, 0, 0) linear-gradient(to right, ${color}, ${color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
if (label) {
//convention is that the number goes at the end... I parse by separating it at ':'
label.textContent = label.textContent.split(":")[0] + ": " + slider.value;
}
}
/**
* Shorthand for making an element in html.
*
* @param {String} type : type of element
* @param {List[String]} classlist : css classes for element
* @param {Map[String, String]} args : Properties for the element
*/
function make_elem(type, classlist = [], args = {}) {
const ret_elem = document.createElement(type);
ret_elem.classList.add(...classlist);
for (const i in args) {
ret_elem[i] = args[i];
}
return ret_elem;
}
/**
* Nodes must have:
* node: {
* parents: List[node]
* children: List[node]
* }
*
* This function will define: "visited, assigned, scc" properties
* Assuming a connected graph. (only one root)
*/
function make_SCC_graph(root_node, nodes) {
for (const node of nodes) {
node.visited = false;
node.assigned = false;
node.scc = null;
}
const res = []
/*
* SCC graph construction.
* https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
*/
function visit(u, res) {
if (u.visited) { return; }
u.visited = true;
for (const child of u.children) {
if (!child.visited) { visit(child, res); }
}
res.push(u);
}
visit(root_node, res);
res.reverse();
const sccs = [];
function assign(node, cur_scc) {
if (node.assigned) { return; }
cur_scc.nodes.push(node);
node.scc = cur_scc;
node.assigned = true;
for (const parent of node.parents) {
assign(parent, cur_scc);
}
}
for (const node of res) {
if (node.assigned) { continue; }
const cur_scc = {
nodes: [],
children: new Set(),
parents: new Set()
};
assign(node, cur_scc);
sccs.push(cur_scc);
}
for (const scc of sccs) {
for (const node of scc.nodes) {
for (const child of node.children) {
scc.children.add(child.scc);
}
for (const parent of node.parents) {
scc.parents.add(parent.scc);
}
}
}
return sccs;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show more