Compare commits

..

20 commits

Author SHA1 Message Date
ferricles
9aa9d871b8 Merge branch 'master' into sock_encoding 2022-12-20 15:09:18 -08:00
ferricles
f9b98ef7e4 BitVector fixes w/ unit tests + unit testing page (/unit_tests/) 2022-12-20 15:08:55 -08:00
ferricles
420e66842d parse hash 2022-12-20 11:46:40 -08:00
ferricles
7b295e4e46 merge conflict 2022-12-20 11:11:30 -08:00
ferricles
9b8564261a pre-merge commit 2022-12-20 11:05:38 -08:00
ferricles
b5e950b8a5 preparing to merge 2022-12-20 11:04:19 -08:00
ferricles
07ad16934b Merge branch 'atree' into sock_encoding 2022-07-26 11:37:25 -07:00
ferricles
31fd1a2f01 change Build internal item repr to include separate tomes array, allow bitvec to append using other bitvec (untested) 2022-07-26 11:37:08 -07:00
ferricles
d76ad45b9c Merge branch 'atree' into sock_encoding 2022-07-21 16:24:24 -07:00
ferricles
c65da33ccd sock encoding build.encode started, bitvec append allow appending bitvecs started 2022-07-21 16:24:03 -07:00
ferricles
99d2b8273a fix merge conflicts 2022-07-20 14:53:35 -07:00
ferricles
ec241891fc append bug fix + unit tests 2022-07-06 21:40:50 -07:00
ferricles
80b65eb0e4 continue bitvec unit testing, changed all asserts to console.trace instead of errors so that future tests are run too and TEST_FAIL constant can be returned 2022-07-06 21:25:04 -07:00
ferricles
c6cc5bfc24 begin writing bitvec unit tests + return constants for unit tests 2022-07-06 20:27:04 -07:00
ferricles
e8a9439b64 Merge branch 'atree' into sock_encoding 2022-07-04 22:25:37 -07:00
ferricles
73a7cec2ed Merge branch 'atree' into sock_encoding 2022-07-04 16:29:21 -07:00
ferricles
458c18297d array fill bug fix 2022-07-04 10:08:44 -07:00
ferricles
e956d28c56 fixed merge conflict 2022-07-04 00:21:38 -07:00
ferricles
1486a8befa add recursion in size check to reduce code duplication 2022-07-04 00:16:15 -07:00
ferricles
1eeabc2c08 updated append(), not tested 2022-07-03 23:42:29 -07:00
147 changed files with 119809 additions and 340116 deletions

View file

@ -10,9 +10,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/sq2bs.css">
@ -27,8 +27,6 @@
<a href = "/wynnfo/index.html"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 py-1 vh-100">
@ -62,8 +60,8 @@
<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="/thirdparty/autoComplete.min.js"></script>
<script src="/thirdparty/macy@2"></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>

View file

@ -15,9 +15,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/sidebar.css">
@ -34,8 +34,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
@ -75,14 +73,6 @@
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://nori.fish/wynn/build/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_build.png" alt="" style="height: 100%;">
<span>Build Search</span>
</a>
<a href="https://nori.fish/wynn/recipe/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_recipe.png" alt="" style="height: 100%;">
<span>Recipe Search</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
@ -474,7 +464,7 @@
War Scream
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+20%)
Ragnarokkr (+30%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="totem-boost" onclick="update_boosts('totem-boost')">
Vengeful Spirit (+20%)
@ -516,19 +506,19 @@
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1" >
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1')">
Lv.1
Lv.4 [e4e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2')">
Lv.2
Lv.4.5 [e5e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3')">
Lv.3
Lv.5 [e5e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4')">
Lv.4
Lv.5.5 [e6e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5')">
Lv.5
Lv.6 [e6e6]
</button>
</div>
<div class="col eDam">
@ -541,19 +531,19 @@
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1')">
Lv.1
Lv.4 [t4t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2')">
Lv.2
Lv.4.5 [t5t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3')">
Lv.3
Lv.5 [t5t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4')">
Lv.4
Lv.5.5 [t6t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5')">
Lv.5
Lv.6 [t6t6]
</button>
</div>
<div class="col tDam">
@ -566,19 +556,19 @@
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1')">
Lv.1
Lv.4 [w4w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2')">
Lv.2
Lv.4.5 [w5w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3')">
Lv.3
Lv.5 [w5w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4')">
Lv.4
Lv.5.5 [w6w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5')">
Lv.5
Lv.6 [w6w6]
</button>
</div>
<div class="col wDam">
@ -591,19 +581,19 @@
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1')">
Lv.1
Lv.4 [f4f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2')">
Lv.2
Lv.4.5 [f5f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3')">
Lv.3
Lv.5 [f5f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4')">
Lv.4
Lv.5.5 [f6f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5')">
Lv.5
Lv.6 [f6f6]
</button>
</div>
<div class="col fDam">
@ -616,19 +606,19 @@
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-1" onclick = "updatePowderSpecials('Wind_Prison-1')">
Lv.1
Lv.4 [a4a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-2" onclick = "updatePowderSpecials('Wind_Prison-2')">
Lv.2
Lv.4.5 [a5a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-3" onclick = "updatePowderSpecials('Wind_Prison-3')">
Lv.3
Lv.5 [a5a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-4" onclick = "updatePowderSpecials('Wind_Prison-4')">
Lv.4
Lv.5.5 [a6a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-5" onclick = "updatePowderSpecials('Wind_Prison-5')">
Lv.5
Lv.6 [a6a6]
</button>
</div>
<div class="col aDam">
@ -985,15 +975,7 @@
</div>
</div>
<div class = "col-lg-3 col-sm-6">
<div class = "row">
Life Steal:
</div>
<div class = "row">
<input type = "number" placeholder = "0" id="ls" name="ls" value="0" class="border-dark text-light dark-10 rounded scaled-font form-control form-control-sm"/>
</div>
<div class = "row" id = "ls-base">
Original Value: 0
</div>
</div>
</div>
<div class = "row big-title justify-content-center">
@ -1253,7 +1235,7 @@
<div class="col-12 dark-5 scaled-font">
<footer class="text-center">
<div id="header2">
<p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, <b>reschan</b>, and <b>blankman</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">
@ -1315,14 +1297,12 @@
</div>
</div>
<script src="/thirdparty/autoComplete.min.js"></script>
<script src="/thirdparty/macy@2"></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>
<script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/computation_graph.js"></script>
<script type="text/javascript">COMPUTE_GRAPH_DEBUG=true</script>
<script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="../js/powders.js"></script>
<script type="text/javascript" src="../js/skillpoints.js"></script>
@ -1341,11 +1321,13 @@
<script type="text/javascript" src="../js/builder/builder_graph.js"></script>
<script type="text/javascript" src="../js/builder/builder.js"></script>
<!--script type="text/javascript" src="../js/builder/optimize.js"></script-->
<div id="graph_body" style="max-width: 100%; height: 100vh">
<button id="saveButton">JANKY Export SVG</button>
<a id="saveLink">savelink</a>
</div>
<script src="https://d3js.org/d3.v7.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

View file

@ -15,9 +15,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/sidebar.css">
@ -34,8 +34,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
@ -75,14 +73,6 @@
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://nori.fish/wynn/build/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_build.png" alt="" style="height: 100%;">
<span>Build Search</span>
</a>
<a href="https://nori.fish/wynn/recipe/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_recipe.png" alt="" style="height: 100%;">
<span>Recipe Search</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
@ -92,7 +82,7 @@
<div class="container-fluid overall-box mt-lg-2" style="margin-top: 6vh;">
<!-- REMOVE THIS DIV AT SOME POINT. -->
<div class = "row scaled-font mx-auto" id = "discord-banner-dev">
<div id='discord-banner' 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 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 gx-0">
<div class="col-xl-6">
@ -474,7 +464,7 @@
War Scream
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+20%)
Ragnarokkr (+30%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="totem-boost" onclick="update_boosts('totem-boost')">
Vengeful Spirit (+20%)
@ -837,30 +827,6 @@
</div>
</div>
</div>
<div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='lootrunTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="lootrunTome1-img-loc">
<div id="lootrunTome1-img" class="img-fluid rounded tome-image"></div>
</div>
<div class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
<div class="col scaled-font fw-bold gx-3">
Lootrun
</div>
<div class="col scaled-font fw-bold gx-3">
Tome
</div>
</div>
</div>
<div class="col g-0 rounded">
<div class="row row-cols-1 h-100 align-items-center">
<div class="col d-flex justify-content-end">
<input class="equipment-input border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" id="lootrunTome1-choice" name="lootrunTome1-choice" placeholder="No Tome" value=""/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;">
@ -1009,15 +975,7 @@
</div>
</div>
<div class = "col-lg-3 col-sm-6">
<div class = "row">
Life Steal:
</div>
<div class = "row">
<input type = "number" placeholder = "0" id="ls" name="ls" value="0" class="border-dark text-light dark-10 rounded scaled-font form-control form-control-sm"/>
</div>
<div class = "row" id = "ls-base">
Original Value: 0
</div>
</div>
</div>
<div class = "row big-title justify-content-center">
@ -1339,8 +1297,8 @@
</div>
</div>
<script src="/thirdparty/autoComplete.min.js"></script>
<script src="/thirdparty/macy@2"></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>
<script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/computation_graph.js"></script>
@ -1362,11 +1320,5 @@
<script type="text/javascript" src="../js/builder/builder_graph.js"></script>
<script type="text/javascript" src="../js/builder/builder.js"></script>
<!--script type="text/javascript" src="../js/builder/optimize.js"></script-->
<!--div id="graph_body" style="max-width: 100%; height: 100vh">
<button id="saveButton">JANKY Export SVG</button>
<a id="saveLink">savelink</a>
</div>
<script src="https://d3js.org/d3.v7.js"></script>
<script type="text/javascript" src="../js/debug/render_compute_graph.js"></script-->
</body>
</html>

174100
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

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="icon" href="../media/icons/new/crafter.png">

View file

@ -10,9 +10,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
@ -29,8 +29,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
@ -70,14 +68,6 @@
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://nori.fish/wynn/build/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_build.png" alt="" style="height: 100%;">
<span>Build Search</span>
</a>
<a href="https://nori.fish/wynn/recipe/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_recipe.png" alt="" style="height: 100%;">
<span>Recipe Search</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>

View file

@ -1 +0,0 @@
wrote some stuff in the file

View file

@ -17,7 +17,6 @@ Additional Contributors, in no particular order:
- lemonalade (ability tree pdf for us to copy from :) )
- Lennon (Skill point formula reversing)
- Phanta (WynnAtlas custom expression parser / item search)
- RawFish (and WIM team) (wynn api shenanigans, major IDs, misc.)
- 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

@ -16,49 +16,41 @@
/* rarity selectors */
#tier-box {
#rarity-box {
cursor: pointer;
width: 58.33%;
text-align: center;
}
#tier-box > div {
#rarity-box > div {
width: calc(100% / 7);
aspect-ratio: 1/1;
position: relative;
display: inline-block;
}
#tier-box > div > b {
#rarity-box > div > b {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#tier-box > div.tier-selected > b {
#rarity-box > div.rarity-selected > b {
color: black;
}
#tier-normal { color: #FFFFFF; }
#tier-normal.tier-selected { background-color: #FFFFFF; }
#tier-unique { color: #FFFF55; }
#tier-unique.tier-selected { background-color: #FFFF55; }
#tier-set { color: #55FF55; }
#tier-set.tier-selected { background-color: #55FF55; }
#tier-rare { color: #FF55FF; }
#tier-rare.tier-selected { background-color: #FF55FF; }
#tier-legendary { color: #55FFFF; }
#tier-legendary.tier-selected { background-color: #55FFFF; }
#tier-fabled { color: #FF5555; }
#tier-fabled.tier-selected { background-color: #FF5555; }
#tier-mythic { color: #AA00AA; }
#tier-mythic.tier-selected { background-color: #AA00AA; }
#tier-zero { color: #FFFFFF; }
#tier-zero.tier-selected { background-color: #FFFFFF; }
#tier-one { color: #FFFFBB; }
#tier-one.tier-selected { background-color: #FFFFBB; }
#tier-two { color: #FFFF88; }
#tier-two.tier-selected { background-color: #FFFF88; }
#tier-three { color: #FFFF55; }
#tier-three.tier-selected { background-color: #FFFF55; }
#rarity-normal { color: #FFFFFF; }
#rarity-normal.rarity-selected { background-color: #FFFFFF; }
#rarity-unique { color: #FFFF55; }
#rarity-unique.rarity-selected { background-color: #FFFF55; }
#rarity-set { color: #55FF55; }
#rarity-set.rarity-selected { background-color: #55FF55; }
#rarity-rare { color: #FF55FF; }
#rarity-rare.rarity-selected { background-color: #FF55FF; }
#rarity-legendary { color: #55FFFF; }
#rarity-legendary.rarity-selected { background-color: #55FFFF; }
#rarity-fabled { color: #FF5555; }
#rarity-fabled.rarity-selected { background-color: #FF5555; }
#rarity-mythic { color: #AA00AA; }
#rarity-mythic.rarity-selected { background-color: #AA00AA; }
/* filters */
.filter-row {

View file

@ -10,9 +10,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
@ -31,8 +31,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
@ -72,14 +70,6 @@
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://nori.fish/wynn/build/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_build.png" alt="" style="height: 100%;">
<span>Build Search</span>
</a>
<a href="https://nori.fish/wynn/recipe/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_recipe.png" alt="" style="height: 100%;">
<span>Recipe Search</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="icon" href="../media/icons/new/crafter.png">

View file

@ -1 +0,0 @@
{"MAGNET":{"displayName":"Magnet","description":"Pulls items within an 8 block radius towards you","abilities":[]},"PLAGUE":{"displayName":"Plague","description":"Poisoned mobs spread their poison to nearby mobs","abilities":[]},"HAWKEYE":{"displayName":"Hawkeye","description":"Condense Arrow Storm into a tight beam. Arrows deal ✤10%, ✦1%, and ❋1%","abilities":[{"class":"Archer","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","behavior":"overwrite","hits":{"Single Arrow":5}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","behavior":"overwrite","multipliers":[10,0,1,0,0,1]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","behavior":"modify","hits":{"Single Stream":4}}]}]},"GREED":{"displayName":"Greed","description":"Picking up emeralds heals you and nearby players for 15% max health","abilities":[]},"CAVALRYMAN":{"displayName":"Cavalryman","description":"You may cast spells and attack with a 70% damage penalty while on a horse","abilities":[]},"GUARDIAN":{"displayName":"Guardian","description":"20% of the damage taken by nearby allies is redirected to you","abilities":[]},"HERO":{"displayName":"Saviours Sacrifice","description":"While under 50% maximum health, nearby allies gain 30% bonus damage and defense","abilities":[]},"ALTRUISM":{"displayName":"Heart of the Pack","description":"Nearby players gain 35% of the health you naturally regenerate","abilities":[]},"ARCANES":{"displayName":"Transcendence","description":"30% chance for spells to cost no mana when casted","abilities":[]},"ENTROPY":{"displayName":"Entropy","description":"Meteor falls three times faster","abilities":[]},"ROVINGASSASSIN":{"displayName":"Roving Assassin","description":"Vanish no longer drains mana while invisible","abilities":[]},"MADNESS":{"displayName":"Madness","description":"Cast a random ability every 3 seconds","abilities":[]},"LIGHTWEIGHT":{"displayName":"Lightweight","description":"You no longer take fall damage","abilities":[]},"SORCERY":{"displayName":"Sorcery","description":"30% chance for spells and attacks to cast a second time at no additional cost","abilities":[]},"TAUNT":{"displayName":"Taunt","description":"Mobs within 12 blocks target you upon casting War Scream","abilities":[]},"RALLY":{"displayName":"Rally","description":"Charge heals you by 10% and nearby allies by 15% on impact, but becomes harmless","abilities":[{"class":"Warrior","base_abil":4,"effects":[{"type":"add_spell_prop","base_spell":2,"display":"Rally Self Heal","target_part":"Rally Self Heal","power":0.1},{"type":"add_spell_prop","base_spell":2,"target_part":"Rally Ally Heal","power":0.15},{"type":"raw_stat","bonuses":[{"type":"stat","name":"damMult.Rally:2.Flying Kick","value":-100},{"type":"stat","name":"damMult.Rally:2.Collide","value":-100},{"type":"stat","name":"damMult.Rally:2.Heavy Impact","value":-100},{"type":"stat","name":"damMult.Rally:2.Flyby Jab","value":-100}]}]}]},"CHERRY_BOMBS":{"displayName":"Cherry Bombs","description":"Your Smoke Bombs explode instantly, and increase their Neutral Damage by +90%","abilities":[{"class":"Assassin","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[90,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Per Bomb","hits":{"Per Tick":-9}}]}]},"FREERUNNER":{"displayName":"Freerunner","description":"Double your sprint speed when your sprint bar is under 30%","abilities":[]},"PEACEFUL_EFFIGY":{"displayName":"Peaceful Effigy","description":"Your totem will last twice as long","abilities":[]},"FURIOUS_EFFIGY":{"displayName":"Furious Effigy","description":"Totem effects are twice as fast, but duration is halved","abilities":[{"class":"Shaman","base_abil":0,"properties":{"rate":-0.2,"totem_mul":2.5},"effects":[]}]},"FLASHFREEZE":{"displayName":"Flashfreeze","description":"Ice Snake is instant but has a reduced range","abilities":[]},"GRAVITYWELL":{"displayName":"Gravity Well","description":"Meteor has increased blast radius and pulls enemies instead","abilities":[]},"DESC_SNOWYSTEPS":{"displayName":"Snowy Steps","description":"Leaves a trail of snow behind you","abilities":[]},"GEOCENTRISM":{"displayName":"Geocentrism","description":"Aura radiates from you instead of your totem and can be cast anytime","abilities":[]},"DESC_FESTIVESPIRIT":{"displayName":"Festive Spirits","description":"Plays wintery tunes","abilities":[]}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"MAGNET":{"displayName":"Magnet","description":"Pulls items within an 8 block radius towards you","abilities":[]},"PLAGUE":{"displayName":"Plague","description":"Poisoned mobs spread their poison to nearby mobs","abilities":[]},"HAWKEYE":{"displayName":"Hawkeye","description":"Condense Arrow Storm into a tight beam. Arrows deal ✤10%, ✦1%, and ❋1%","abilities":[{"class":"Archer","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","behavior":"overwrite","hits":{"Single Arrow":5}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","behavior":"overwrite","multipliers":[10,0,1,0,0,1]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","behavior":"modify","hits":{"Single Stream":4}}]}]},"GREED":{"displayName":"Greed","description":"Picking up emeralds heals you and nearby players for 15% max health","abilities":[]},"CAVALRYMAN":{"displayName":"Cavalryman","description":"You may cast spells and attack with a 70% damage penalty while on a horse","abilities":[]},"GUARDIAN":{"displayName":"Guardian","description":"20% of the damage taken by nearby allies is redirected to you","abilities":[]},"HERO":{"displayName":"Saviours Sacrifice","description":"While under 50% maximum health, nearby allies gain 30% bonus damage and defense","abilities":[]},"ALTRUISM":{"displayName":"Heart of the Pack","description":"Nearby players gain 35% of the health you naturally regenerate","abilities":[]},"ARCANES":{"displayName":"Transcendence","description":"30% chance for spells to cost no mana when casted","abilities":[]},"ENTROPY":{"displayName":"Entropy","description":"Meteor falls three times faster","abilities":[]},"ROVINGASSASSIN":{"displayName":"Roving Assassin","description":"Vanish no longer drains mana while invisible","abilities":[]},"MADNESS":{"displayName":"Madness","description":"Cast a random ability every 3 seconds","abilities":[]},"LIGHTWEIGHT":{"displayName":"Lightweight","description":"You no longer take fall damage","abilities":[]},"SORCERY":{"displayName":"Sorcery","description":"30% chance for spells and attacks to cast a second time at no additional cost","abilities":[]},"TAUNT":{"displayName":"Taunt","description":"Mobs within 12 blocks target you upon casting War Scream","abilities":[]},"RALLY":{"displayName":"Rally","description":"Charge heals you by 10% and nearby allies by 15% on impact, but becomes harmless","abilities":[{"class":"Warrior","base_abil":4,"effects":[{"type":"add_spell_prop","base_spell":2,"display":"Rally Self Heal","target_part":"Rally Self Heal","power":0.1},{"type":"add_spell_prop","base_spell":2,"target_part":"Rally Ally Heal","power":0.15},{"type":"raw_stat","bonuses":[{"type":"stat","name":"damMult.Rally:2.Flying Kick","value":-100},{"type":"stat","name":"damMult.Rally:2.Collide","value":-100},{"type":"stat","name":"damMult.Rally:2.Heavy Impact","value":-100},{"type":"stat","name":"damMult.Rally:2.Flyby Jab","value":-100}]}]}]},"CHERRY_BOMBS":{"displayName":"Cherry Bombs","description":"Your Smoke Bombs explode instantly, and increase their Neutral Damage by +90%","abilities":[{"class":"Assassin","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[90,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Per Bomb","hits":{"Per Tick":-9}}]}]},"FREERUNNER":{"displayName":"Freerunner","description":"Double your sprint speed when your sprint bar is under 30%","abilities":[]},"PEACEFUL_EFFIGY":{"displayName":"Peaceful Effigy","description":"Your totem will last twice as long","abilities":[]},"FURIOUS_EFFIGY":{"displayName":"Furious Effigy","description":"Totem effects are twice as fast, but duration is halved","abilities":[{"class":"Shaman","base_abil":0,"properties":{"rate":-0.2,"totem_mul":2.5},"effects":[]}]},"FLASHFREEZE":{"displayName":"Flashfreeze","description":"Ice Snake is instant but has a reduced range","abilities":[]},"GRAVITYWELL":{"displayName":"Gravity Well","description":"Meteor has increased blast radius and pulls enemies instead","abilities":[]},"DESC_SNOWYSTEPS":{"displayName":"Snowy Steps","description":"Leaves a trail of snow behind you","abilities":[]},"GEOCENTRISM":{"displayName":"Geocentrism","description":"Aura radiates from you instead of your totem and can be cast anytime","abilities":[]},"DESC_FESTIVESPIRIT":{"displayName":"Festive Spirits","description":"Plays wintery tunes","abilities":[]}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"MAGNET":{"displayName":"Magnet","description":"Pulls items within an 8 block radius towards you","abilities":[]},"PLAGUE":{"displayName":"Plague","description":"Poisoned mobs spread their poison to nearby mobs","abilities":[]},"HAWKEYE":{"displayName":"Hawkeye","description":"Condense Arrow Storm into a tight beam. Arrows deal ✤10%, ✦1%, and ❋1%","abilities":[{"class":"Archer","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","behavior":"overwrite","hits":{"Single Arrow":5}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","behavior":"overwrite","multipliers":[10,0,1,0,0,1]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","behavior":"modify","hits":{"Single Stream":4}}]}]},"GREED":{"displayName":"Greed","description":"Picking up emeralds heals you and nearby players for 15% max health","abilities":[]},"CAVALRYMAN":{"displayName":"Cavalryman","description":"You may cast spells and attack with a 70% damage penalty while on a horse","abilities":[]},"GUARDIAN":{"displayName":"Guardian","description":"20% of the damage taken by nearby allies is redirected to you","abilities":[]},"HERO":{"displayName":"Saviours Sacrifice","description":"While under 50% maximum health, nearby allies gain 30% bonus damage and defense","abilities":[]},"ALTRUISM":{"displayName":"Heart of the Pack","description":"Nearby players gain 35% of the health you naturally regenerate","abilities":[]},"ARCANES":{"displayName":"Transcendence","description":"30% chance for spells to cost no mana when casted","abilities":[]},"ENTROPY":{"displayName":"Entropy","description":"Meteor falls three times faster","abilities":[]},"ROVINGASSASSIN":{"displayName":"Roving Assassin","description":"Vanish no longer drains mana while invisible","abilities":[]},"MADNESS":{"displayName":"Madness","description":"Cast a random ability every 3 seconds","abilities":[]},"LIGHTWEIGHT":{"displayName":"Lightweight","description":"You no longer take fall damage","abilities":[]},"SORCERY":{"displayName":"Sorcery","description":"30% chance for spells and attacks to cast a second time at no additional cost","abilities":[]},"TAUNT":{"displayName":"Taunt","description":"Mobs within 12 blocks target you upon casting War Scream","abilities":[]},"RALLY":{"displayName":"Rally","description":"Charge heals you by 10% and nearby allies by 15% on impact, but becomes harmless","abilities":[{"class":"Warrior","base_abil":4,"effects":[{"type":"add_spell_prop","base_spell":2,"display":"Rally Self Heal","target_part":"Rally Self Heal","power":0.1},{"type":"add_spell_prop","base_spell":2,"target_part":"Rally Ally Heal","power":0.15},{"type":"raw_stat","bonuses":[{"type":"stat","name":"damMult.Rally:2.Flying Kick","value":-100},{"type":"stat","name":"damMult.Rally:2.Collide","value":-100},{"type":"stat","name":"damMult.Rally:2.Heavy Impact","value":-100},{"type":"stat","name":"damMult.Rally:2.Flyby Jab","value":-100}]}]}]},"CHERRY_BOMBS":{"displayName":"Cherry Bombs","description":"Your Smoke Bombs explode instantly, and increase their Neutral Damage by +90%","abilities":[{"class":"Assassin","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[90,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Per Bomb","hits":{"Per Tick":-9}}]}]},"FREERUNNER":{"displayName":"Freerunner","description":"Double your sprint speed when your sprint bar is under 30%","abilities":[]},"PEACEFUL_EFFIGY":{"displayName":"Peaceful Effigy","description":"Your totem will last twice as long","abilities":[]},"FURIOUS_EFFIGY":{"displayName":"Furious Effigy","description":"Totem effects are twice as fast, but duration is halved","abilities":[{"class":"Shaman","base_abil":0,"properties":{"rate":-0.2,"totem_mul":2.5},"effects":[]}]},"FLASHFREEZE":{"displayName":"Flashfreeze","description":"Ice Snake is instant but has a reduced range","abilities":[]},"GRAVITYWELL":{"displayName":"Gravity Well","description":"Meteor has increased blast radius and pulls enemies instead","abilities":[]},"DESC_SNOWYSTEPS":{"displayName":"Snowy Steps","description":"Leaves a trail of snow behind you","abilities":[]},"GEOCENTRISM":{"displayName":"Geocentrism","description":"Aura radiates from you instead of your totem and can be cast anytime","abilities":[]},"DESC_FESTIVESPIRIT":{"displayName":"Festive Spirits","description":"Plays wintery tunes","abilities":[]}}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
{"MAGNET":{"displayName":"Magnet","description":"Pulls items within an 8 block radius towards you","abilities":[]},"PLAGUE":{"displayName":"Plague","description":"Poisoned mobs spread their poison to nearby mobs","abilities":[]},"HAWKEYE":{"displayName":"Hawkeye","description":"Condense Arrow Storm into a tight beam. Arrows deal ✤10%, ✦1%, and ❋1%","abilities":[{"class":"Archer","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","behavior":"overwrite","hits":{"Single Arrow":5}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","behavior":"overwrite","multipliers":[10,0,1,0,0,1]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","behavior":"modify","hits":{"Single Stream":4}}]}]},"GREED":{"displayName":"Greed","description":"Picking up emeralds heals you and nearby players for 15% max health","abilities":[]},"CAVALRYMAN":{"displayName":"Cavalryman","description":"You may cast spells and attack with a 70% damage penalty while on a horse","abilities":[]},"GUARDIAN":{"displayName":"Guardian","description":"20% of the damage taken by nearby allies is redirected to you","abilities":[]},"HERO":{"displayName":"Saviours Sacrifice","description":"While under 50% maximum health, nearby allies gain 30% bonus damage and defense","abilities":[]},"ALTRUISM":{"displayName":"Heart of the Pack","description":"Nearby players gain 35% of the health you naturally regenerate","abilities":[]},"ARCANES":{"displayName":"Transcendence","description":"30% chance for spells to cost no mana when casted","abilities":[]},"ENTROPY":{"displayName":"Entropy","description":"Meteor falls three times faster","abilities":[]},"ROVINGASSASSIN":{"displayName":"Roving Assassin","description":"Vanish no longer drains mana while invisible","abilities":[]},"MADNESS":{"displayName":"Madness","description":"Cast a random ability every 3 seconds","abilities":[]},"LIGHTWEIGHT":{"displayName":"Lightweight","description":"You no longer take fall damage","abilities":[]},"SORCERY":{"displayName":"Sorcery","description":"30% chance for spells and attacks to cast a second time at no additional cost","abilities":[]},"TAUNT":{"displayName":"Taunt","description":"Mobs within 12 blocks target you upon casting War Scream","abilities":[]},"RALLY":{"displayName":"Rally","description":"Charge heals you by 10% and nearby allies by 15% on impact, but becomes harmless","abilities":[{"class":"Warrior","base_abil":4,"effects":[{"type":"add_spell_prop","base_spell":2,"display":"Rally Self Heal","target_part":"Rally Self Heal","power":0.1},{"type":"add_spell_prop","base_spell":2,"target_part":"Rally Ally Heal","power":0.15},{"type":"raw_stat","bonuses":[{"type":"stat","name":"damMult.Rally:2.Flying Kick","value":-100},{"type":"stat","name":"damMult.Rally:2.Collide","value":-100},{"type":"stat","name":"damMult.Rally:2.Heavy Impact","value":-100},{"type":"stat","name":"damMult.Rally:2.Flyby Jab","value":-100}]}]}]},"CHERRY_BOMBS":{"displayName":"Cherry Bombs","description":"Your Smoke Bombs explode instantly, and increase their Neutral Damage by +90%","abilities":[{"class":"Assassin","base_abil":7,"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Per Tick","multipliers":[90,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Per Bomb","hits":{"Per Tick":-9}}]}]},"FREERUNNER":{"displayName":"Freerunner","description":"Double your sprint speed when your sprint bar is under 30%","abilities":[]},"PEACEFUL_EFFIGY":{"displayName":"Peaceful Effigy","description":"Your totem will last twice as long","abilities":[]},"FURIOUS_EFFIGY":{"displayName":"Furious Effigy","description":"Totem effects are twice as fast, but duration is halved","abilities":[{"class":"Shaman","base_abil":0,"properties":{"rate":-0.2,"totem_mul":2.5},"effects":[]}]},"FLASHFREEZE":{"displayName":"Flashfreeze","description":"Ice Snake is instant but has a reduced range","abilities":[]},"GRAVITYWELL":{"displayName":"Gravity Well","description":"Meteor has increased blast radius and pulls enemies instead","abilities":[]},"DESC_SNOWYSTEPS":{"displayName":"Snowy Steps","description":"Leaves a trail of snow behind you","abilities":[]},"GEOCENTRISM":{"displayName":"Geocentrism","description":"Aura radiates from you instead of your totem and can be cast anytime","abilities":[]},"DESC_FESTIVESPIRIT":{"displayName":"Festive Spirits","description":"Plays wintery tunes","abilities":[]}}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

0
dev/builder_colorcode.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 178 KiB

0
dev/compute_graph.svg Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View file

@ -14,11 +14,11 @@
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet"
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet"
href="/thirdparty/autoComplete.min.css">
href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
@ -43,8 +43,6 @@
<a href="" 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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 text-light px-5 scaled-font">

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="icon" href="../media/icons/new/builder.png">

View file

@ -1,184 +0,0 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<title>WynnAtlas</title>
<link rel="icon" href="../media/icons/new/searcher.png" type="image/icon type">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css">
<link rel="stylesheet" href="../css/search.css">
</head>
<body class = "text-light d-flex justify-content-center" id = "body">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "../map/"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://nori.fish/wynn/build/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_build.png" alt="" style="height: 100%;">
<span>Build Search</span>
</a>
<a href="https://nori.fish/wynn/recipe/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_recipe.png" alt="" style="height: 100%;">
<span>Recipe Search</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class = "container py-5 vh-100 mx-0 mx-lg-auto scaled-font">
<div class = "row">
<div class = "row" style = "margin-top: 3ch;">
<div class = "col text-start" id = "credits">
<a href="../items/" class="link">Item Searching</a>
</div>
<div class = "col text-end">
<a href = "../ingredients_adv/">Advanced Ingredient Search</a>
</div>
</div>
<div class = "col-auto" style = "width: 12.5%;"></div>
<div class = "col-lg-4">
<div class = "row">
<div class = "col-lg col-sm-12">
<div class = "col fw-bold">Name:</div>
<input class = "col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" style="width: 100%!important;" type="text" id="item-name-choice" name="item-name-choice" placeholder="Ingredient name (case insensitive)"/>
<p class="error col-auto"></p>
</div>
</div>
<div class = "row">
<div class = "col-lg col-sm-12">
<div class = "col"><span class = "fw-bold">Types:</span> <span style = "cursor: pointer; float: right;"><span id = "all-types">All</span> &nbsp; <span id = "none-types">None</span></span></div>
<div id = "type-box">
<p id = "type-armouring" style = "margin: 0; ">Armouring</p>
<p id = "type-tailoring" style = "margin: 0;">Tailoring</p>
<p id = "type-weaponsmithing" style = "margin: 0;">Weaponsmithing</p>
<p id = "type-woodworking" style = "margin: 0;">Woodworking</p>
<p id = "type-jeweling" style = "margin: 0;">Jeweling</p>
<p id = "type-cooking" style = "margin: 0;">Cooking</p>
<p id = "type-alchemism" style = "margin: 0;">Alchemism</p>
<p id = "type-scribing" style = "margin: 0;">Scribing</p>
</div>
<p class="error col-auto"></p>
</div>
</div>
<div class = "row">
<div class = "col-lg col-sm-12">
<div class = "col"><span class = "fw-bold">Stars:</span> <span style = "cursor: pointer; float: right;"><span id = "all-tiers">All</span> &nbsp; <span id = "none-tiers">None</span></span></div>
<div id = "tier-box">
<!-- unfortunately they must be stacked up like this because newlines are considered spaces and muck up the organization -->
<div id = "tier-zero" class = "tier-selected"><b>0</b></div><div id = "tier-one" class = "tier-selected"><b>1</b></div><div id = "tier-two" class = "tier-selected"><b>2</b></div><div id = "tier-three" class = "tier-selected"><b>3</b></div>
</div>
<p class="error col-auto"></p>
</div>
</div>
</div>
<div class = "col-lg-5">
<div id = "filter-container" class = "col">
<div class = "col fw-bold">Filters:</div>
<div class = "row">
<div id = "add-filter" class = "col fw-bold" style = "cursor: pointer; padding-top: 5px;">
+ Add Filter
</div>
</div>
</div>
<br/>
<div id = "exclude-container" class = "col">
<div class = "col fw-bold">Excluded Filters:</div>
<div class = "row">
<div id = "add-exclude" class = "col fw-bold" style = "cursor: pointer; padding-top: 5px;">
+ Add Excluded Filter
</div>
</div>
</div>
</div>
<div class = "row">
<div class = "col-auto" style = "width: 12.5%;"></div>
<div class = "col-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "search-button" onclick = "do_item_search()">
Search!
</button>
</div>
<div class = "col-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "reset_item_search()">
Reset
</button>
</div>
</div>
<div class = "row box-title justify-content-center" id = "summary">
</div>
<div class = "row" id = "search-results">
</div>
</div>
</div>
<script src="/thirdparty/autoComplete.min.js"></script>
<script type="text/javascript" src="../js/drag_drop_touch.js"></script>
<script type="text/javascript" src="../js/utils.js"></script>
<script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script>
<script type="text/javascript" src="../js/display_constants.js"></script>
<script type="text/javascript" src="../js/display.js"></script>
<script type="text/javascript" src="../js/query.js"></script>
<script type="text/javascript" src="../js/expr_parser.js"></script>
<script type="text/javascript" src="../js/load_ing.js"></script>
<script type="text/javascript" src="../js/search.js"></script>
<script type="text/javascript" src="../js/ingredients.js"></script>
<script type="text/javascript" src="../js/powders.js"></script>
</body>
</html>

View file

@ -1,89 +0,0 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<title>WynnAtlas</title>
<link rel="icon" href="../media/icons/new/searcher.png" type="image/icon type">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/items_adv.css">
<link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css">
</head>
<body class = "text-light d-flex justify-content-center" id = "body">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/customizer.html"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map.html"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "/wynnfo/index.html"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 scaled-font">
<div class = "col">
<div class = "row">
<div class = "col text-start" id = "credits">
<a href="../credits.txt" class="link">Additional credits</a>
</div>
<div class = "col text-end">
<a href = "../ingredients/">Basic Ingredient Search</a>
</div>
</div>
<div class = "row">
<div class="col" id="main">
<div class = "row" id="search-container">
<div class="col search-field-container" id="search-filter">
<div class="col fw-bold">Filter By:</div>
<input class="col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="search-filter-field" type="text" autofocus="true"
placeholder="name ?= &quot;blue&quot; & str >= 15 & dex >= 10">
<div class="search-field-error" id="search-filter-error"></div>
<div class="search-field-compl" id="search-filter-compl"></div>
</div>
<div class="col search-field-container" id="search-sort">
<div class="col fw-bold">Sort By:</div>
<input class="col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="search-sort-field" type="text"
placeholder="str + dex; meleerawdmg + spellrawdmg">
<div class="search-field-error" id="search-sort-error"></div>
<div class="search-field-compl" id="search-sort-compl"></div>
</div>
</div>
<div class = "row" id="item-list-container">
<div class="row" id="item-list"></div>
<div class="row" id="item-list-footer"></div>
</div>
</div>
</div>
<div class = "row" id="scroll-up">&uparrow;</div>
</div>
</div>
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/build_utils.js"></script>
<script type="text/javascript" src="/js/icons.js"></script>
<script type="text/javascript" src="/js/powders.js"></script>
<script type="text/javascript" src="/js/damage_calc.js"></script>
<script type="text/javascript" src="/js/display_constants.js"></script>
<script type="text/javascript" src="/js/display.js"></script>
<script type="text/javascript" src="/js/query.js"></script>
<script type="text/javascript" src="/js/expr_parser.js"></script>
<script type="text/javascript" src="/js/load_ing.js"></script>
<script type="text/javascript" src="/js/search_adv.js"></script>
<script type="text/javascript" src="/js/ingredients_adv.js"></script>
</body>
</html>

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -11,9 +11,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
@ -30,8 +30,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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>
@ -58,7 +56,6 @@
</div>
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/build_utils.js"></script>
<script type="text/javascript" src="/js/builder/build_encode_decode.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>

View file

@ -10,15 +10,15 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css">
<link rel="stylesheet" href="../css/search.css">
<link rel="stylesheet" href="../css/items.css">
</head>
<body class = "text-light d-flex justify-content-center" id = "body">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
@ -30,8 +30,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</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://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<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 id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
@ -71,14 +69,6 @@
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://nori.fish/wynn/build/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_build.png" alt="" style="height: 100%;">
<span>Build Search</span>
</a>
<a href="https://nori.fish/wynn/recipe/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/nori_recipe.png" alt="" style="height: 100%;">
<span>Recipe Search</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
@ -88,9 +78,6 @@
<div class = "container py-5 vh-100 mx-0 mx-lg-auto scaled-font">
<div class = "row">
<div class = "row" style = "margin-top: 3ch;">
<div class = "col text-start" id = "credits">
<a href="../ingredients/" class="link">Ingredient Searching</a>
</div>
<div class = "col text-end">
<a href = "../items_adv/">Advanced Item Search</a>
</div>
@ -126,10 +113,10 @@
</div>
<div class = "row">
<div class = "col-lg col-sm-12">
<div class = "col"><span class = "fw-bold">Rarity:</span> <span style = "cursor: pointer; float: right;"><span id = "all-tiers">All</span> &nbsp; <span id = "none-tiers">None</span></span></div>
<div id = "tier-box">
<div class = "col"><span class = "fw-bold">Rarity:</span> <span style = "cursor: pointer; float: right;"><span id = "all-rarities">All</span> &nbsp; <span id = "none-rarities">None</span></span></div>
<div id = "rarity-box">
<!-- unfortunately they must be stacked up like this because newlines are considered spaces and muck up the organization -->
<div id = "tier-normal" class = "tier-selected"><b>N</b></div><div id = "tier-unique" class = "tier-selected"><b>U</b></div><div id = "tier-set" class = "tier-selected"><b>S</b></div><div id = "tier-rare" class = "tier-selected"><b>R</b></div><div id = "tier-legendary" class = "tier-selected"><b>L</b></div><div id = "tier-fabled" class = "tier-selected"><b>F</b></div><div id = "tier-mythic" class = "tier-selected"><b>M</b></div>
<div id = "rarity-normal" class = "rarity-selected"><b>N</b></div><div id = "rarity-unique" class = "rarity-selected"><b>U</b></div><div id = "rarity-set" class = "rarity-selected"><b>S</b></div><div id = "rarity-rare" class = "rarity-selected"><b>R</b></div><div id = "rarity-legendary" class = "rarity-selected"><b>L</b></div><div id = "rarity-fabled" class = "rarity-selected"><b>F</b></div><div id = "rarity-mythic" class = "rarity-selected"><b>M</b></div>
</div>
<p class="error col-auto"></p>
</div>
@ -174,20 +161,18 @@
</div>
</div>
</div>
<script src="/thirdparty/autoComplete.min.js"></script>
<script type="text/javascript" src="/js/drag_drop_touch.js"></script>
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/build_utils.js"></script>
<script type="text/javascript" src="/js/builder/build_encode_decode.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/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/expr_parser.js"></script>
<script type="text/javascript" src="/js/load.js"></script>
<script type="text/javascript" src="/js/search.js"></script>
<script type="text/javascript" src="/js/powders.js"></script>
<script type="text/javascript" src="/js/items.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
<script type="text/javascript" src="../js/drag_drop_touch.js"></script>
<script type="text/javascript" src="../js/utils.js"></script>
<script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script>
<script type="text/javascript" src="../js/display_constants.js"></script>
<script type="text/javascript" src="../js/display.js"></script>
<script type="text/javascript" src="../js/query.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/powders.js"></script>
</body>
</html>

View file

@ -10,9 +10,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
@ -76,7 +76,6 @@
<script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/build_utils.js"></script>
<script type="text/javascript" src="/js/builder/build_encode_decode.js"></script>
<script type="text/javascript" src="/js/icons.js"></script>
<script type="text/javascript" src="/js/powders.js"></script>
<script type="text/javascript" src="/js/damage_calc.js"></script>
@ -85,7 +84,6 @@
<script type="text/javascript" src="/js/query.js"></script>
<script type="text/javascript" src="/js/expr_parser.js"></script>
<script type="text/javascript" src="/js/load.js"></script>
<script type="text/javascript" src="/js/search_adv.js"></script>
<script type="text/javascript" src="/js/items_adv.js"></script>
</body>
</html>

View file

@ -13,9 +13,9 @@
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
<link rel="stylesheet" href="../css/article.css">
@ -33,8 +33,6 @@
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://nori.fish/wynn/build/" target = "_blank"><img src = "../media/icons/new/nori_build.png" alt = "Build Search" title = "Build Search by Nori-Wynn"><b>Build Search</b></a>
<a href = "https://nori.fish/wynn/recipe/" target = "_blank"><img src = "../media/icons/new/nori_recipe.png" alt = "Recipe Search" title = "Recipe Search by Nori-Wynn"><b>Recipe Search</b></a>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
<main>
@ -154,7 +152,7 @@
['waterDmg%', 'number', ['waterDam%', 'wDmg%', 'wDam%', 'wDamPct'], 'The bonus water damage modifier on an item.'],
['fireDmg%', 'number', ['fireDam%', 'fDmg%', 'fDam%', 'fDamPct'], 'The bonus fire damage modifier on an item.'],
['airDmg%', 'number', ['airDam%', 'aDmg%', 'aDam%', 'aDamPct'], 'The bonus air damage modifier on an item.'],
//['sumDmg%', 'number', ['sumDam%', 'totalDmg%', 'totalDam%', 'sumDamPct', 'totalDamPct'], 'The sum of the bonus elemental damage modifiers on an item.'],
['sumDmg%', 'number', ['sumDam%', 'totalDmg%', 'totalDam%', 'sumDamPct', 'totalDamPct'], 'The sum of the bonus elemental damage modifiers on an item.'],
['meleeDmg', 'number', ['meleeDam', 'meleeDmg%', 'meleeDam%', 'mdPct'], 'The bonus main-attack damage modifier on an item.'],
['meleeNeutralDmg', 'number', ['meleeRawDam', 'meleeNeutralDmg', 'meleeNeutralDam', 'mdRaw'], 'The neutral main-attack damage on an item.'],
['spellDmg', 'number', ['spellDam', 'spellDmg%', 'spellDam%', 'sdPct'], 'The bonus spell damage modifier on an item.'],

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -60,15 +60,7 @@ const armorTypes = [ "helmet", "chestplate", "leggings", "boots" ];
const accessoryTypes = [ "ring", "bracelet", "necklace" ];
const weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
const consumableTypes = [ "potion", "scroll", "food"];
const tome_types = ['weaponTome', 'armorTome', 'guildTome', 'lootrunTome', 'gatherXpTome', 'dungeonXpTome', 'mobXpTome'];
const tome_type_map = new Map([["weaponTome", "Weapon Tome"],
["armorTome", "Armor Tome"],
["guildTome", "Guild Tome"],
["gatherXpTome", "Gather XP Tome"],
["dungeonXpTome", "Dungeon XP Tome"],
["mobXpTome", "Slaying XP Tome"],
]);
const tome_types = ['weaponTome', 'armorTome', 'guildTome'];
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
//0.51, 0.82, 1.50, 2.05, 2.50, 3.11, 4.27
@ -83,10 +75,7 @@ let item_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(to
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
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",
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",
@ -98,20 +87,17 @@ let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slot
/*"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",
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final",
"healPct", "kb", "weakenEnemy", "slowEnemy", "rDefPct"
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
];
// 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
let reversetranslations = new Map();
let _translations_list = [["name", "name"],["displayName", "displayName"],["tier", "tier"],["set", "set"],["sockets", "slots"],["type", "type"],["armorColor", "color"],["addedLore", "lore"],["dropType", "drop"],["quest", "quest"],["restrictions", "restrict"],["damage", "nDam"],["fireDamage", "fDam"],["waterDamage", "wDam"],["airDamage", "aDam"],["thunderDamage", "tDam"],["earthDamage", "eDam"],["attackSpeed", "atkSpd"],["health", "hp"],["fireDefense", "fDef"],["waterDefense", "wDef"],["airDefense", "aDef"],["thunderDefense", "tDef"],["earthDefense", "eDef"],["level", "lvl"],["classRequirement", "classReq"],["strength", "strReq"],["dexterity", "dexReq"],["intelligence", "intReq"],["agility", "agiReq"],["defense", "defReq"],["healthRegen", "hprPct"],["manaRegen", "mr"],["spellDamageBonus", "sdPct"],["spellElementalDamageBonus", "rSdPct"],["spellNeutralDamageBonus", "nSdPct"],["spellFireDamageBonus", "fSdPct"],["spellWaterDamageBonus", "wSdPct"],["spellAirDamageBonus", "aSdPct"],["spellThunderDamageBonus", "tSdPct"],["spellEarthDamageBonus", "eSdPct"],["mainAttackDamageBonus", "mdPct"],["mainAttackElementalDamageBonus", "rMdPct"],["mainAttackNeutralDamageBonus", "nMdPct"],["mainAttackFireDamageBonus", "fMdPct"],["mainAttackWaterDamageBonus", "wMdPct"],["mainAttackAirDamageBonus", "aMdPct"],["mainAttackThunderDamageBonus", "tMdPct"],["mainAttackEarthDamageBonus", "eMdPct"],["lifeSteal", "ls"],["manaSteal", "ms"],["xpBonus", "xpb"],["lootBonus", "lb"],["reflection", "ref"],["strengthPoints", "str"],["dexterityPoints", "dex"],["intelligencePoints", "int"],["agilityPoints", "agi"],["defensePoints", "def"],["thorns", "thorns"],["exploding", "expd"],["speed", "spd"],["attackSpeedBonus", "atkTier"],["poison", "poison"],["healthBonus", "hpBonus"],["soulPoints", "spRegen"],["emeraldStealing", "eSteal"],["healthRegenRaw", "hprRaw"],["spellDamageBonusRaw", "sdRaw"],["spellElementalDamageBonusRaw", "rSdRaw"],["spellNeutralDamageBonusRaw", "nSdRaw"],["spellFireDamageBonusRaw", "fSdRaw"],["spellWaterDamageBonusRaw", "wSdRaw"],["spellAirDamageBonusRaw", "aSdRaw"],["spellThunderDamageBonusRaw", "tSdRaw"],["spellEarthDamageBonusRaw", "eSdRaw"],["mainAttackDamageBonusRaw", "mdRaw"],["mainAttackElementalDamageBonusRaw", "rMdRaw"],["mainAttackNeutralDamageBonusRaw", "nMdRaw"],["mainAttackFireDamageBonusRaw", "fMdRaw"],["mainAttackWaterDamageBonusRaw", "wMdRaw"],["mainAttackAirDamageBonusRaw", "aMdRaw"],["mainAttackThunderDamageBonusRaw", "tMdRaw"],["mainAttackEarthDamageBonusRaw", "eMdRaw"],["fireDamageBonus", "fDamPct"],["waterDamageBonus", "wDamPct"],["airDamageBonus", "aDamPct"],["thunderDamageBonus", "tDamPct"],["earthDamageBonus", "eDamPct"],["bonusFireDefense", "fDefPct"],["bonusWaterDefense", "wDefPct"],["bonusAirDefense", "aDefPct"],["bonusThunderDefense", "tDefPct"],["bonusEarthDefense", "eDefPct"],["accessoryType", "type"],["identified", "fixID"],["skin", "skin"],["category", "category"],["spellCostPct1", "spPct1"],["spellCostRaw1", "spRaw1"],["spellCostPct2", "spPct2"],["spellCostRaw2", "spRaw2"],["spellCostPct3", "spPct3"],["spellCostRaw3", "spRaw3"],["spellCostPct4", "spPct4"],["spellCostRaw4", "spRaw4"],["sprint", "sprint"],["sprintRegen", "sprintReg"],["jumpHeight", "jh"],["lootQuality", "lq"],["gatherXpBonus", "gXp"],["gatherSpeed", "gSpd"],["healingEfficiency", "healPct"], ["knockback", "kb"], ["weakenEnemy", "weakenEnemy"], ["slowEnemy", "slowEnemy"], ["elementalDefense", "rDefPct"]];
let translations = new Map(_translations_list);
let translations = new Map([["name", "name"],["displayName", "displayName"],["tier", "tier"],["set", "set"],["sockets", "slots"],["type", "type"],["armorColor", "color"],["addedLore", "lore"],["dropType", "drop"],["quest", "quest"],["restrictions", "restrict"],["damage", "nDam"],["fireDamage", "fDam"],["waterDamage", "wDam"],["airDamage", "aDam"],["thunderDamage", "tDam"],["earthDamage", "eDam"],["attackSpeed", "atkSpd"],["health", "hp"],["fireDefense", "fDef"],["waterDefense", "wDef"],["airDefense", "aDef"],["thunderDefense", "tDef"],["earthDefense", "eDef"],["level", "lvl"],["classRequirement", "classReq"],["strength", "strReq"],["dexterity", "dexReq"],["intelligence", "intReq"],["agility", "agiReq"],["defense", "defReq"],["healthRegen", "hprPct"],["manaRegen", "mr"],["spellDamageBonus", "sdPct"],["spellElementalDamageBonus", "rSdPct"],["spellNeutralDamageBonus", "nSdPct"],["spellFireDamageBonus", "fSdPct"],["spellWaterDamageBonus", "wSdPct"],["spellAirDamageBonus", "aSdPct"],["spellThunderDamageBonus", "tSdPct"],["spellEarthDamageBonus", "eSdPct"],["mainAttackDamageBonus", "mdPct"],["mainAttackElementalDamageBonus", "rMdPct"],["mainAttackNeutralDamageBonus", "nMdPct"],["mainAttackFireDamageBonus", "fMdPct"],["mainAttackWaterDamageBonus", "wMdPct"],["mainAttackAirDamageBonus", "aMdPct"],["mainAttackThunderDamageBonus", "tMdPct"],["mainAttackEarthDamageBonus", "eMdPct"],["lifeSteal", "ls"],["manaSteal", "ms"],["xpBonus", "xpb"],["lootBonus", "lb"],["reflection", "ref"],["strengthPoints", "str"],["dexterityPoints", "dex"],["intelligencePoints", "int"],["agilityPoints", "agi"],["defensePoints", "def"],["thorns", "thorns"],["exploding", "expd"],["speed", "spd"],["attackSpeedBonus", "atkTier"],["poison", "poison"],["healthBonus", "hpBonus"],["soulPoints", "spRegen"],["emeraldStealing", "eSteal"],["healthRegenRaw", "hprRaw"],["spellDamageBonusRaw", "sdRaw"],["spellElementalDamageBonusRaw", "rSdRaw"],["spellNeutralDamageBonusRaw", "nSdRaw"],["spellFireDamageBonusRaw", "fSdRaw"],["spellWaterDamageBonusRaw", "wSdRaw"],["spellAirDamageBonusRaw", "aSdRaw"],["spellThunderDamageBonusRaw", "tSdRaw"],["spellEarthDamageBonusRaw", "eSdRaw"],["mainAttackDamageBonusRaw", "mdRaw"],["mainAttackElementalDamageBonusRaw", "rMdRaw"],["mainAttackNeutralDamageBonusRaw", "nMdRaw"],["mainAttackFireDamageBonusRaw", "fMdRaw"],["mainAttackWaterDamageBonusRaw", "wMdRaw"],["mainAttackAirDamageBonusRaw", "aMdRaw"],["mainAttackThunderDamageBonusRaw", "tMdRaw"],["mainAttackEarthDamageBonusRaw", "eMdRaw"],["fireDamageBonus", "fDamPct"],["waterDamageBonus", "wDamPct"],["airDamageBonus", "aDamPct"],["thunderDamageBonus", "tDamPct"],["earthDamageBonus", "eDamPct"],["bonusFireDefense", "fDefPct"],["bonusWaterDefense", "wDefPct"],["bonusAirDefense", "aDefPct"],["bonusThunderDefense", "tDefPct"],["bonusEarthDefense", "eDefPct"],["accessoryType", "type"],["identified", "fixID"],["skin", "skin"],["category", "category"],["spellCostPct1", "spPct1"],["spellCostRaw1", "spRaw1"],["spellCostPct2", "spPct2"],["spellCostRaw2", "spRaw2"],["spellCostPct3", "spPct3"],["spellCostRaw3", "spRaw3"],["spellCostPct4", "spPct4"],["spellCostRaw4", "spRaw4"],["sprint", "sprint"],["sprintRegen", "sprintReg"],["jumpHeight", "jh"],["lootQuality", "lq"],["gatherXpBonus", "gXp"],["gatherSpeed", "gSpd"]]);
//does not include damMobs (wep tomes) and defMobs (armor tomes)
for (const [k, v] of _translations_list) {
if (reversetranslations.has(v)) { continue; }
for (const [k, v] of translations) {
reversetranslations.set(v, k);
}
@ -188,13 +174,10 @@ let rolledIDs = [
"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
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final",
"healPct", "kb", "weakenEnemy", "slowEnemy", "rDefPct"
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
];
let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ];
let ingFields = rolledIDs.concat(["str", "dex", "int", "def", "agi"]);
/**
* Take an item with id list and turn it into a set of minrolls and maxrolls.
*/
@ -212,16 +195,28 @@ function expandItem(item) {
} else { //The item does not have fixed IDs.
for (const id of rolledIDs) {
let val = (item[id] || 0);
if (val == 0) {
if (val > 0) { // positive rolled IDs
if (reversedIDs.includes(id)) {
maxRolls.set(id,idRound(val*0.7));
minRolls.set(id,idRound(val*1.3));
} else {
maxRolls.set(id,idRound(val*1.3));
minRolls.set(id,idRound(val*0.3));
}
} else if (val < 0) { //negative rolled IDs
if (reversedIDs.includes(id)) {
maxRolls.set(id,idRound(val*1.3));
minRolls.set(id,idRound(val*0.3));
}
else {
maxRolls.set(id,idRound(val*0.7));
minRolls.set(id,idRound(val*1.3));
}
}
else { // if val == 0
// NOTE: DO NOT remove this case! idRound behavior does not round to 0!
maxRolls.set(id,0);
minRolls.set(id,0);
} else if ((val > 0) != (reversedIDs.includes(id))) { // logical XOR. positive IDs
maxRolls.set(id,idRound(val*1.3));
minRolls.set(id,idRound(val*0.3));
} else { //negative rolled IDs
maxRolls.set(id,idRound(val*0.7));
minRolls.set(id,idRound(val*1.3));
}
}
}
@ -305,7 +300,7 @@ function expandRecipe(recipe) {
function idRound(id){
rounded = Math.round(id);
if(rounded == 0){
return Math.sign(id); //this is a hack, will need changing along w/ rest of ID system if anything changes
return 1; //this is a hack, will need changing along w/ rest of ID system if anything changes
}else{
return rounded;
}
@ -315,8 +310,8 @@ function idRound(id){
* stupid stupid multiplicative stats
*/
function merge_stat(stats, name, value) {
const start = name.split('.', limit=1)[0];
if (start === 'damMult' || start === 'defMult' || start === 'healMult') {
const start = name.slice(0, 7);
if (start === 'damMult' || start === 'defMult') {
if (!stats.has(start)) {
stats.set(start, new Map());
}
@ -327,7 +322,7 @@ function merge_stat(stats, name, value) {
}
return;
}
merge_stat(map, name.slice(name.indexOf('.')+1), value);
merge_stat(map, name.slice(8), value);
return;
}
if (stats.has(name)) {

View file

@ -45,10 +45,9 @@ add_spell_prop: {
base_spell: int // spell identifier
target_part: Optional[str] // Part of the spell to modify. Can be not present/empty for ex. cost modifier.
// If target part does not exist, a new part is created.
behavior: Optional[str] // One of: "merge", "modify", "overwrite". default: merge
behavior: Optional[str] // One of: "merge", "modify". default: merge
// merge: add if exist, make new part if not exist
// modify: increment existing part. do nothing if not exist
// overwrite: set part. do nothing if not exist
cost: Optional[int] // change to spellcost. If the spell is not spell 1-4, this must be left empty.
multipliers: Optional[array[float, 6]] // Additive changes to spellmult (for damage spell)
power: Optional[float] // Additive change to healing power (for heal spell)
@ -76,23 +75,22 @@ raw_stat: {
bonuses: List[stat_bonus]
}
stat_bonus: {
type: "stat" | "prop",
abil: Optional[int],
name: str,
value: float
"type": "stat" | "prop",
"abil": Optional[int],
"name": str,
"value": float
}
stat_scaling: {
type: "stat_scaling",
slider: bool,
"type": "stat_scaling",
"slider": bool,
positive: bool // True to keep stat above 0. False to ignore floor. Default: True for normal, False for scaling
slider_name: Optional[str],
slider_step: Optional[float],
round: Optional[bool] // Control floor behavior. True for stats and false for slider by default
behavior: Optional[str] // One of: "merge", "modify". default: merge
"slider_name": Optional[str],
"slider_step": Optional[float],
round: Optional[bool] // Control floor behavior. True for stats and false for slider by default
slider_behavior: Optional[str] // One of: "merge", "modify". default: merge
// merge: add if exist, make new part if not exist
// modify: change existing part, by incrementing properties. do nothing if not exist
slider_max: Optional[int] // affected by behavior
slider_default: Optional[int] // affected by behavior
slider_max: Optional[float] // affected by slider_behavior
inputs: Optional[list[scaling_target]] // List of things to scale. Omit this if using slider
output: Optional[scaling_target | List[scaling_target]] // One of the following:
@ -100,12 +98,12 @@ stat_scaling: {
// 2. List of scaling targets (all scaled the same)
// 3. Omitted. no output (useful for modifying slider only without input or output)
scaling: Optional[list[float]] // One float for each input. Sums into output.
max: float // Hardcap on this effect (slider value * slider_step). Can be negative if scaling is negative
max: float
}
scaling_target: {
type: "stat" | "prop",
abil: Optional[int],
name: str
"type": "stat" | "prop",
"abil": Optional[int],
"name": str
}
*/
@ -166,64 +164,6 @@ const default_abils = {
}, elem_mastery_abil ],
};
/**
* Given a json of raw atree data, and a wynn class name (ex. Archer, Mage),
* sort out their ability tree and return it as a list in roughly topologically
* sorted order.
*
* TODO: Why do we care about the toposort?
* This is not a useful representation of the tree.
* It's useless.
* atree needs a cleanup and anything depending on the ordering of items
* coming out of this function is probably doing something wrong.
* NOTE2: Actually this might be more complicated because some nodes do need
* an application ordering and that matters (esp. replace_spell nodes).
*
* Parameters:
* --------------------------
* atrees: Raw atree data. This is a parameter to allow oldversion shenanigans
* player_class: Wynn class name (string)
*
* Return:
* List of atree nodes.
*/
function get_sorted_class_atree(atrees, player_class) {
const atree_raw = atrees[player_class];
if (!atree_raw) return [];
let atree_map = new Map();
let atree_head;
for (const i of atree_raw) {
atree_map.set(i.id, {children: [], ability: i});
if (i.parents.length == 0) {
// Assuming there is only one head.
atree_head = atree_map.get(i.id);
}
}
for (const i of atree_raw) {
let node = atree_map.get(i.id);
let parents = [];
for (const parent_id of node.ability.parents) {
let parent_node = atree_map.get(parent_id);
parent_node.children.push(node);
parents.push(parent_node);
}
node.parents = parents;
}
let sccs = make_SCC_graph(atree_head, atree_map.values());
let atree_topo_sort = [];
for (const scc of sccs) {
for (const node of scc.nodes) {
delete node.visited;
delete node.assigned;
delete node.scc;
atree_topo_sort.push(node);
}
}
return atree_topo_sort;
}
/**
* Update ability tree internal representation. (topologically sorted node list)
*
@ -235,7 +175,43 @@ const atree_node = new (class extends ComputeNode {
compute_func(input_map) {
if (input_map.size !== 1) { throw "AbilityTreeUpdateNode accepts exactly one input (player-class)"; }
const [player_class] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
return get_sorted_class_atree(atrees, player_class);
const atree_raw = atrees[player_class];
if (!atree_raw) return [];
let atree_map = new Map();
let atree_head;
for (const i of atree_raw) {
atree_map.set(i.id, {children: [], ability: i});
if (i.parents.length == 0) {
// Assuming there is only one head.
atree_head = atree_map.get(i.id);
}
}
for (const i of atree_raw) {
let node = atree_map.get(i.id);
let parents = [];
for (const parent_id of node.ability.parents) {
let parent_node = atree_map.get(parent_id);
parent_node.children.push(node);
parents.push(parent_node);
}
node.parents = parents;
}
let sccs = make_SCC_graph(atree_head, atree_map.values());
let atree_topo_sort = [];
for (const scc of sccs) {
for (const node of scc.nodes) {
delete node.visited;
delete node.assigned;
delete node.scc;
atree_topo_sort.push(node);
}
}
//console.log("Approximate topological order ability tree:");
//console.log(atree_topo_sort);
return atree_topo_sort;
}
})();
@ -445,7 +421,6 @@ const atree_merge = new (class extends ComputeNode {
const [hard_error, errors] = input_map.get('atree-errors');
if (hard_error) { return null; }
const player_class = input_map.get('player-class');
const build = input_map.get('build');
const atree_state = input_map.get('atree-state');
const atree_order = input_map.get('atree');
@ -458,20 +433,26 @@ const atree_merge = new (class extends ComputeNode {
else if (!Array.isArray(tmp_abil.desc)) {
tmp_abil.desc = [tmp_abil.desc];
}
tmp_abil.subparts = [abil.id];
abils_merged.set(abil.id, tmp_abil);
}
function merge_abil(abil) {
for (const node of atree_order) {
const abil_id = node.ability.id;
if (!atree_state.get(abil_id).active) {
continue;
}
const abil = node.ability;
if ('base_abil' in abil) {
if (abils_merged.has(abil.base_abil)) {
// Merge abilities.
// TODO: What if there is more than one base abil?
let base_abil = abils_merged.get(abil.base_abil);
if (abil.desc) {
if (Array.isArray(abil.desc)) { base_abil.desc = base_abil.desc.concat(abil.desc); }
else { base_abil.desc.push(abil.desc); }
}
if (Array.isArray(abil.desc)) { base_abil.desc = base_abil.desc.concat(abil.desc); }
else { base_abil.desc.push(abil.desc); }
base_abil.subparts.push(abil.id);
base_abil.effects = base_abil.effects.concat(abil.effects);
for (let propname in abil.properties) {
if (propname in base_abil.properties) {
@ -487,54 +468,10 @@ const atree_merge = new (class extends ComputeNode {
if (!Array.isArray(tmp_abil.desc)) {
tmp_abil.desc = [tmp_abil.desc];
}
abils_merged.set(abil.id, tmp_abil);
tmp_abil.subparts = [abil.id];
abils_merged.set(abil_id, tmp_abil);
}
}
for (const node of atree_order) {
const abil_id = node.ability.id;
if (!atree_state.get(abil_id).active) {
continue;
}
merge_abil(node.ability);
}
// Apply major IDs.
const build_class = wep_to_class.get(build.weapon.statMap.get("type"));
for (const major_id_name of build.statMap.get("activeMajorIDs")) {
// Sometimes, something silly happens and we haven't implemented a major ID that
// exists. This makes sure we don't try to apply unimplemented major IDs.
//
// `major_ids` is a global map loaded from data json.
if (major_id_name in major_ids) {
// A major ID can have multiple abilities, specified as atree nodes,
// as part of its effects. Apply each of them.
for (const abil of major_ids[major_id_name].abilities) {
// But only the ones that match the current class.
if (abil["class"] === build_class) {
// Major IDs can have ability dependencies.
// By default they are always on.
if (abil.dependencies !== undefined) {
let dep_satisfied = true;
for (const dep_id of abil.dependencies) {
if (!atree_state.get(dep_id).active) {
dep_satisfied = false;
break;
}
}
if (!dep_satisfied) { continue; }
}
merge_abil(abil);
}
}
}
}
console.log(abils_merged)
return abils_merged;
}
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state').link_to(atree_validate, 'atree-errors');
@ -574,43 +511,27 @@ const atree_make_interactives = new (class extends ComputeNode {
const slider_map = new Map();
const button_map = new Map();
let to_process = [];
for (const [abil_id, ability] of merged_abils) {
// first, pull out all the sliders and toggles.
for (const [abil_id, ability] of merged_abils.entries()) {
for (const effect of ability.effects) {
if (effect['type'] === "stat_scaling" && effect['slider'] === true) {
to_process.push([effect, abil_id, ability]);
}
if (effect['type'] === "raw_stat" && effect['toggle']) {
to_process.push([effect, abil_id, ability]);
}
}
}
let unprocessed = [];
// first, pull out all the sliders and toggles.
let k = to_process.length;
for (let i = 0; i < k; ++i) {
for (const [effect, abil_id, ability] of to_process) {
if (effect['type'] === "stat_scaling" && effect['slider'] === true) {
const { slider_name, behavior = 'merge', slider_max = 0, slider_step, slider_default = 0 } = effect;
const { slider_name, slider_behavior = 'merge', slider_max, slider_step } = effect;
if (slider_map.has(slider_name)) {
const slider_info = slider_map.get(slider_name);
slider_info.max += slider_max;
slider_info.default_val += slider_default;
if (slider_max !== undefined) {
const slider_info = slider_map.get(slider_name);
slider_info.max += slider_max;
}
}
else if (behavior === 'merge') {
else if (slider_behavior === 'merge') {
slider_map.set(slider_name, {
label_name: slider_name+' ('+ability.display_name+')',
max: slider_max,
default_val: slider_default,
step: slider_step,
id: "ability-slider"+ability.id,
//color: effect['slider_color'] TODO: add colors to json
abil: ability
});
}
else {
unprocessed.push([effect, abil_id, ability]);
}
}
if (effect['type'] === "raw_stat" && effect['toggle']) {
const { toggle: toggle_name } = effect;
@ -619,9 +540,6 @@ const atree_make_interactives = new (class extends ComputeNode {
});
}
}
if (unprocessed.length == to_process.length) { break; }
to_process = unprocessed;
unprocessed = [];
}
// next, render the sliders and toggles onto the abilities.
for (const [slider_name, slider_info] of slider_map.entries()) {
@ -705,13 +623,9 @@ const atree_scaling = new (class extends ComputeNode {
continue;
case 'stat_scaling':
let total = 0;
const {slider = false, scaling = [0], behavior="merge"} = effect;
const {slider = false, scaling = [0]} = effect;
let { positive = true, round = true } = effect;
if (slider) {
if (behavior == "modify" && !slider_map.has(effect.slider_name)) {
// Dangerous control flow.. early continue
continue;
}
const slider_val = slider_map.get(effect.slider_name).slider.value;
total = parseInt(slider_val) * scaling[0];
round = false;
@ -727,10 +641,7 @@ const atree_scaling = new (class extends ComputeNode {
if ('output' in effect) { // sometimes nodes will modify slider without having effect.
if (round) { total = Math.floor(round_near(total)); }
if (positive && total < 0) { total = 0; } // Normal stat scaling will not go negative.
if ('max' in effect) {
if (effect.max > 0 && total > effect.max) { total = effect.max; }
if (effect.max < 0 && total < effect.max) { total = effect.max; }
}
if ('max' in effect && total > effect.max) { total = effect.max; }
if (Array.isArray(effect.output)) {
for (const output of effect.output) {
apply_bonus(output, total);
@ -903,45 +814,33 @@ const atree_collect_spells = new (class extends ComputeNode {
continue;
case 'add_spell_prop': {
const { base_spell, target_part = null, cost = 0, behavior = 'merge'} = effect;
if (!ret_spells.has(base_spell)) {
continue;
}
const ret_spell = ret_spells.get(base_spell);
// :enraged:
// NOTE to hpp: this is out here because:
// target_part doesn't exist for spell cost modification abilities
// except when it does... in which case it should apply exactly once.
// TODO: unjankify this...
if ('cost' in ret_spell) { ret_spell.cost += cost; }
// NOTE: see above comment for the weird placement of this code block.
if (target_part === null) { continue; }
if (target_part === null) {
continue;
}
let found_part = false;
for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
if (part.name !== target_part) {
continue;
}
// we found the part. merge or modify it!
if ('multipliers' in effect) {
for (const [idx, v] of effect.multipliers.entries()) { // python: enumerate()
if (behavior === 'overwrite') { part.multipliers[idx] = v; }
else { part.multipliers[idx] += v; }
part.multipliers[idx] += v;
}
}
else if ('power' in effect) {
if (behavior === 'overwrite') { part.power = effect.power; }
else { part.power += effect.power; }
part.power += effect.power;
}
else if ('hits' in effect) {
for (const [idx, _v] of Object.entries(effect.hits)) { // looks kinda similar to multipliers case... hmm... can we unify all of these three? (make healpower a list)
let v = translate(_v);
if (behavior === 'overwrite') { part.hits[idx] = v; }
else {
if (idx in part.hits) { part.hits[idx] += v; }
else { part.hits[idx] = v; }
}
if (idx in part.hits) { part.hits[idx] += v; }
else { part.hits[idx] = v; }
}
}
else {
@ -965,7 +864,6 @@ const atree_collect_spells = new (class extends ComputeNode {
}
continue;
}
// NOTE: Legacy support
case 'convert_spell_conv':
const { base_spell, target_part, conversion } = effect;
const ret_spell = ret_spells.get(base_spell);
@ -1066,9 +964,13 @@ class AbilityTreeEnsureNodesNode extends ComputeNode {
// TODO shortcut update path for sliders
for (const [spell_id, spell] of new Map([...spell_map].sort((a, b) => a[0] - b[0])).entries()) {
let calc_node = new SpellDamageCalcNode(spell)
let spell_node = new SpellSelectNode(spell)
.link_to(build_node, 'build');
let calc_node = new SpellDamageCalcNode(spell.base_spell)
.link_to(build_node, 'build')
.link_to(stat_agg_node, 'stats');
.link_to(stat_agg_node, 'stats')
.link_to(spell_node, 'spell-info');
this.spelldmg_nodes.push(calc_node);
let display_elem = make_elem('div', ["col", "pe-0"]);
@ -1080,8 +982,9 @@ class AbilityTreeEnsureNodesNode extends ComputeNode {
display_elem.append(spell_summary, spell_detail);
let display_node = new SpellDisplayNode(spell)
let display_node = new SpellDisplayNode(spell.base_spell)
.link_to(stat_agg_node, 'stats')
.link_to(spell_node, 'spell-info')
.link_to(calc_node, 'spell-damage');
this.spell_display_elem.appendChild(display_elem);

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -10,7 +10,7 @@ const classDefenseMultipliers = new Map([ ["relik",0.60], ["bow",0.70], ["wand",
/*
* Class that represents a wynn player's build.
*/
class Build {
class Build{
/**
* @description Construct a build.
@ -20,7 +20,6 @@ class Build {
* @param {Item} weapon: Weapon that this build is using.
*/
constructor(level, items, weapon){
if (level < 1) { //Should these be constants?
this.level = 1;
} else if (level > 106) {
@ -79,8 +78,7 @@ class Build {
"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
"healPct",
"rMdPct","rMdRaw","rSdPct","rSdRaw","rDamPct","rDamRaw","rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
]
//Create a map of this build's stats
@ -116,8 +114,8 @@ class Build {
}
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.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];
@ -132,8 +130,7 @@ class Build {
}
statMap.set("poisonPct", 0);
statMap.set("critDamPct", 0);
statMap.set("healMult", new Map());
statMap.get('healMult').set('item', statMap.get('healPct'));
statMap.set("healPct", 0);
// The stuff relevant for damage calculation!!! @ferricles
statMap.set("atkSpd", this.weapon.statMap.get("atkSpd"));

View file

@ -29,46 +29,27 @@ function parsePowdering(powder_info) {
let atree_data = null;
const wynn_version_names = [
'2.0.1.1',
'2.0.1.2',
'2.0.2.1',
'2.0.2.3',
'2.0.3.1',
'2.0.4.1',
'2.0.4.3',
'2.0.4.4',
'2.1.0.0'
'2.0.1.2'
];
const WYNN_VERSION_LATEST = wynn_version_names.length - 1;
// Default to the newest version.
let wynn_version_id = WYNN_VERSION_LATEST;
let major_ids = null;
let major_id_load_complete = false;
async function load_major_id_data(version_str) {
let getUrl = window.location;
let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// No random string -- we want to use caching
let url = `${baseUrl}/data/${version_str}/majid.json`;
major_ids = await (await fetch(url)).json();
major_id_load_complete = true;
}
/*
* Populate fields based on url, and calculate build.
* TODO: THIS CODE IS GOD AWFUL result of being lazy
* fix all the slice() and break into functions or do something about it... its inefficient, ugly and error prone
*/
async function parse_hash(url_tag) {
let latest_ver_name = wynn_version_names[WYNN_VERSION_LATEST];
const default_load_promises = [ load_atree_data(latest_ver_name), load_major_id_data(latest_ver_name),
load_init(), load_ing_init(), load_tome_init()];
const default_load_promises = [ load_atree_data(wynn_version_names[WYNN_VERSION_LATEST]),
load_init(), load_ing_init(), load_tome_init() ];
if (!url_tag) {
await Promise.all(default_load_promises);
return;
}
//default values
let equipment = [null, null, null, null, null, null, null, null, null];
let tomes = [null, null, null, null, null, null, null, null];
let tomes = [null, null, null, null, null, null, null];
let powdering = ["", "", "", "", ""];
let info = url_tag.split("_");
let version = info[0];
@ -115,7 +96,6 @@ async function parse_hash(url_tag) {
else {
version_name = wynn_version_names[wynn_version_id];
const load_promises = [ load_atree_data(version_name),
load_major_id_data(version_name),
load_old_version(version_name),
load_ings_old_version(version_name),
load_tome_old_version(version_name) ];
@ -123,7 +103,6 @@ async function parse_hash(url_tag) {
await Promise.all(load_promises);
}
}
if (wynn_version_id == WYNN_VERSION_LATEST) {
await Promise.all(default_load_promises);
}
@ -154,7 +133,7 @@ async function parse_hash(url_tag) {
}
data_str = info_str.slice(start_idx);
}
else if (version_number <= 9) {
else if (version_number <= 8) {
let info_str = data_str;
let start_idx = 0;
for (let i = 0; i < 9; ++i ) {
@ -195,7 +174,7 @@ async function parse_hash(url_tag) {
let powder_info = data_str.slice(10);
let res = parsePowdering(powder_info);
powdering = res[0];
} else if (version_number <= 9){
} else if (version_number <= 8){
level = Base64.toInt(data_str.slice(10,12));
setValue("level-choice",level);
save_skp = true;
@ -214,7 +193,7 @@ async function parse_hash(url_tag) {
if (version_number >= 6) {
//tome values do not appear in anything before v6.
if (version_number < 8) {
for (let i = 0; i < 7; ++i) {
for (let i in tomes) {
let tome_str = data_str.charAt(i);
let tome_name = getTomeNameFromID(Base64.toInt(tome_str));
setValue(tomeInputs[i], tome_name);
@ -223,21 +202,12 @@ async function parse_hash(url_tag) {
}
else {
// 2chr tome encoding to allow for more tomes.
// Lootrun tome was added in v9.
let num_tomes = 7;
if (version_number <= 8) {
num_tomes = 7;
}
else {
num_tomes = 8;
}
for (let i = 0; i < num_tomes; ++i) {
for (let i in tomes) {
let tome_str = data_str.slice(2*i, 2*i+2);
let tome_name = getTomeNameFromID(Base64.toInt(tome_str));
setValue(tomeInputs[i], tome_name);
}
data_str = data_str.slice(num_tomes*2);
data_str = data_str.slice(14);
}
}
@ -262,80 +232,119 @@ async function parse_hash(url_tag) {
/* Stores the entire build in a string using B64 encoding and adds it to the URL.
*/
function encodeBuild(build, powders, skillpoints, atree, atree_state) {
//currently on version 8 - a unified version for all build types using bit-level encoding
if (build) {
let build_string;
//V6 encoding - Tomes
//V7 encoding - ATree
//V8 encoding - wynn version
//V9 encoding - lootrun tome
build_version = 9;
build_string = "";
tome_string = "";
//final link will be [build_vers]_[len_string]_[build_string]
build_version = 8;
let len_string = "";
let build_string = "";
let build_bits = new BitVector(0, 0);
//ITEMS
for (const item of build.items) {
if (item.statMap.get("custom")) {
let custom = "CI-"+encodeCustom(item, true);
build_string += Base64.fromIntN(custom.length, 3) + custom;
//build_version = Math.max(build_version, 5);
if (item.statMap.get("NONE") && item.statMap.get("NONE") === true) {
build_bits.append(0, 2); //00
} else if (item.statMap.get("custom")) {
build_bits.append(3, 2); //11
//BitVector CI encoding TODO
// let custom = "CI-"+encodeCustom(item, true);
// build_string += Base64.fromIntN(custom.length, 3) + custom;
// build_version = Math.max(build_version, 5);
} else if (item.statMap.get("crafted")) {
build_string += "CR-"+encodeCraft(item);
} else if (item.statMap.get("category") === "tome") {
let tome_id = item.statMap.get("id");
//if (tome_id <= 60) {
// valid normal tome. ID 61-63 is for NONE tomes.
//build_version = Math.max(build_version, 6);
//}
tome_string += Base64.fromIntN(tome_id, 2);
build_bits.append(2, 2); //10
//BitVector CR encoding TODO
// build_string += "CR-"+encodeCraft(item);
} else {
build_string += Base64.fromIntN(item.statMap.get("id"), 3);
}
}
if (item.statMap.get("category") === "tome") {
//we will encode tomes later
continue;
} else {
build_bits.append(1, 2); //01
build_bits.append(item.statMap.get("id"), 13);
for (const skp of skillpoints) {
build_string += Base64.fromIntN(skp, 2); // Maximum skillpoints: 2048
}
build_string += Base64.fromIntN(build.level, 2);
for (const _powderset of powders) {
let n_bits = Math.ceil(_powderset.length / 6);
build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders.
// Slice copy.
let powderset = _powderset.slice();
while (powderset.length != 0) {
let firstSix = powderset.slice(0,6).reverse();
let powder_hash = 0;
for (const powder of firstSix) {
powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first.
//powderable
if (powderable_keys.includes(item.statMap.get("type"))) {
if (item.statMap.get("powders") && item.statMap.get("powders").length !== 0) {
//has powders
build_bits.append(1, 1);
//num of powders in 8 bits, then each powder (6 bits)
//Having more than 256 powders on a vanilla item is NOT HANDLED.
build_bits.append(item.statMap.get("powders").length, 8);
for (const powder of item.statMap.get("powders")) {
build_bits.append(powder, 6);
}
} else {
//no powders
build_bits.append(0, 1);
}
}
}
build_string += Base64.fromIntN(powder_hash, 5);
powderset = powderset.slice(6);
}
}
build_string += tome_string;
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();
//SKILL POINTS
//the original schema included a flag to indicate whether or not skill points are included.
//any reason for having a flag isn't implemented yet, so for now every build will have the skill point flag set.
build_bits.append(1, 1);
for (const skp of build.base_skillpoints) {
build_bits.append(skp, 8); // Maximum skillpoints: 255 (allows for manual assign up to 150)
}
return build_version.toString() + "_" + build_string;
//BUILD LEVEL
// [flag to indicate if level is not 106 (0/1)]
// [else: level (7 bits, allows for lv 1->127)]
if (player_build.level != 106) {
build_bits.append(1, 1);
build_bits.append(player_build.level, 7);
} else {
build_bits.append(0, 1);
}
// TOMES
// [flag to indicate if tomes are included (0/1)]
// [if set: 7 sequential tome IDs, each 6 bits unsigned]
if (build.tomes.length > 0) {
build_bits.append(1, 1);
//decoding will assume that tomes has length of 7.
for (const tome of build.tomes) {
build_bits.append(tome.id, 6);
}
} else {
build_bits.append(0, 1);
}
// ATREE
// [flag to indicate if atree data is present]
// [atree data: see existing encoding impl] //idk the impl
if (atree.length > 0 && atree_state.get(atree[0].ability.id).active) {
build_bits.append(1, 1);
const atree_bitvec = encode_atree(atree, atree_state);
build_bits.append(atree_bitvec);
} else {
build_bits.append(0, 1);
}
//compute length and return final build hash
return build_version.toString() + "_" + len_string + "_" + build_string;
}
}
function get_full_url() {
return `${url_base}?v=${wynn_version_id.toString()}${location.hash}`
}
function copyBuild() {
copyTextToClipboard(get_full_url());
copyTextToClipboard(url_base+location.hash);
document.getElementById("copy-button").textContent = "Copied!";
}
function shareBuild(build) {
if (build) {
let text = get_full_url()+"\n"+
let text = url_base+location.hash+"\n"+
"WynnBuilder build:\n"+
"> "+build.items[0].statMap.get("displayName")+"\n"+
"> "+build.items[1].statMap.get("displayName")+"\n"+
@ -345,13 +354,7 @@ function shareBuild(build) {
"> "+build.items[5].statMap.get("displayName")+"\n"+
"> "+build.items[6].statMap.get("displayName")+"\n"+
"> "+build.items[7].statMap.get("displayName")+"\n"+
"> "+build.items[16].statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]\n";
for (let tomeslots = 8; tomeslots < 16; tomeslots++) {
if (!build.items[tomeslots].statMap.has('NONE')) {
text += ">"+' (Has Tomes)' ;
break;
}
}
"> "+build.items[15].statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]";
copyTextToClipboard(text);
document.getElementById("share-button").textContent = "Copied!";
}

View file

@ -89,12 +89,13 @@ 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").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`;
}
const nodes_to_reset = equip_inputs.concat(powder_nodes).concat(edit_input_nodes).concat([powder_special_input, boosts_node, armor_powder_node]);
const nodes_to_reset = item_nodes.concat(powder_nodes).concat(edit_input_nodes).concat([powder_special_input, boosts_node, armor_powder_node]);
for (const node of nodes_to_reset) {
node.mark_dirty();
}
@ -230,10 +231,8 @@ function init_autocomplete() {
}
let tome_alias = tome_obj['alias'];
tome_arr.push(tome_name);
if (tome_alias) {
tome_arr.push(tome_alias);
tome_aliases.set(tome_alias, tome_name);
}
tome_arr.push(tome_alias);
tome_aliases.set(tome_alias, tome_name);
}
// create dropdown
@ -369,7 +368,7 @@ async function init() {
console.log(e);
}
builder_graph_init(save_skp);
for (const item_node of item_final_nodes) {
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?')) {

View file

@ -12,7 +12,7 @@ const BUILD_VERSION = "7.0.19";
let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
"hprRaw", "hprPct", "hpBonus", "atkTier", "ls",
"hprRaw", "hprPct", "hpBonus", "atkTier",
"spPct1", "spRaw1", "spPct2", "spRaw2",
"spPct3", "spRaw3", "spPct4", "spRaw4" ];
@ -60,7 +60,6 @@ let tome_fields = [
"armorTome3",
"armorTome4",
"guildTome1",
"lootrunTome1"
]
let equipment_names = [
"Helmet",
@ -100,7 +99,7 @@ let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots'];
let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace'];
let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon'];
let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'];
let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1', 'lootrunTome1'];
let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1'];
let spell_disp = ['build-melee-stats', 'spell0-info', 'spell1-info', 'spell2-info', 'spell3-info'];
let other_disp = ['build-order', 'set-info', 'int-info'];

View file

@ -17,7 +17,7 @@ let armor_powder_node = new (class extends ComputeNode {
}
})();
const damageMultipliers = new Map([ ["totem", 0.2], ["warscream", 0.0], ["ragnarokkr", 0.20], ["fortitude", 0.60], ["radiance", 0.0] ]);
const damageMultipliers = new Map([ ["totem", 0.2], ["warscream", 0.0], ["ragnarokkr", 0.30], ["fortitude", 0.60], ["radiance", 0.0] ]);
let boosts_node = new (class extends ComputeNode {
constructor() { super('builder-boost-input'); }
@ -117,7 +117,7 @@ class PowderSpecialDisplayNode extends ComputeNode {
const powder_specials = input_map.get('powder-specials');
const stats = input_map.get('stats');
const weapon = input_map.get('build').weapon;
displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap);
displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap, true);
}
}
@ -183,7 +183,7 @@ class ItemInputNode extends InputNode {
for (const [i, x] of zip2(equipment_inputs, replace_items)) { setValue(i, x); }
for (const node of equip_inputs) {
for (const node of item_nodes) {
if (node !== this) {
// save a tiny bit of compute
calcSchedule(node, 10);
@ -413,8 +413,7 @@ class BuildAssembleNode extends ComputeNode {
input_map.get('armorTome2'),
input_map.get('armorTome3'),
input_map.get('armorTome4'),
input_map.get('guildTome1'),
input_map.get('lootrunTome1')
input_map.get('guildTome1')
];
let weapon = input_map.get('weapon');
let level = parseInt(input_map.get('level-input'));
@ -429,7 +428,7 @@ class BuildAssembleNode extends ComputeNode {
if (all_none && !location.hash) {
return null;
}
return new Build(level, equipments, weapon);
return new Build(level, equipments, tomes, weapon);
}
}
@ -501,6 +500,28 @@ class PowderInputNode extends InputNode {
}
}
/**
* Select a spell+spell "variation" based on a build / spell idx.
* Right now this isn't much logic and is only used to abstract away major id interactions
* but will become significantly more complex in wynn2.
*
* Signature: SpellSelectNode<int>(build: Build) => [Spell, SpellParts]
*/
class SpellSelectNode extends ComputeNode {
constructor(spell) {
super("builder-spell"+spell.base_spell+"-select");
this.spell = spell;
}
compute_func(input_map) {
const build = input_map.get('build');
let stats = build.statMap;
// TODO: apply major ids... DOOM.....
return [this.spell, this.spell.parts];
}
}
/*
* Get all defensive stats for this build.
*/
@ -529,8 +550,7 @@ function getDefenseStats(stats) {
defenseStats.push(totalHpr);
//EHPR
let ehpr = [totalHpr, totalHpr];
ehpr[0] = ehpr[0] / (0.1*agi_pct + (1-agi_pct) * (1-def_pct));
ehpr[0] /= defMult;
ehpr[0] /= (1-def_pct)*(1-agi_pct)*defMult;
ehpr[1] /= (1-def_pct)*defMult;
defenseStats.push(ehpr);
//skp stats
@ -538,7 +558,7 @@ function getDefenseStats(stats) {
//eledefs - TODO POWDERS
let eledefs = [0, 0, 0, 0, 0];
for(const i in skp_elements){ //kinda jank but ok
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), (stats.get(skp_elements[i] + "DefPct") + stats.get("rDefPct"))/100.);
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.);
}
defenseStats.push(eledefs);
@ -555,15 +575,15 @@ function getDefenseStats(stats) {
* spell-info: [Spell, SpellParts]) => List[SpellDamage]
*/
class SpellDamageCalcNode extends ComputeNode {
constructor(spell) {
super("builder-spell"+spell.base_spell+"-calc");
this.spell = spell;
constructor(spell_num) {
super("builder-spell"+spell_num+"-calc");
}
compute_func(input_map) {
const weapon = input_map.get('build').weapon.statMap;
const spell = this.spell;
const spell_parts = spell.parts;
const spell_info = input_map.get('spell-info');
const spell = spell_info[0];
const spell_parts = spell_info[1];
const stats = input_map.get('stats');
const skillpoints = [
stats.get('str'),
@ -572,26 +592,13 @@ class SpellDamageCalcNode extends ComputeNode {
stats.get('def'),
stats.get('agi')
];
let display_spell_results = []
let spell_results = []
let spell_result_map = new Map();
const use_speed = (('use_atkspd' in spell) ? spell.use_atkspd : true);
const use_spell = (('scaling' in spell) ? spell.scaling === 'spell' : true);
// TODO: move preprocessing to separate node/node chain
for (const part of spell_parts) {
const {name, display=true} = part;
spell_result_map.set(name, {type: "need_eval", store_part: part});
}
function eval_part(part_name) {
let dat = spell_result_map.get(part_name);
if (!dat) {
return dat; // return null, or undefined, or whatever it is that this gives
}
if (dat.type !== "need_eval") {
return dat; // Already evaluated. Return it
}
let part = dat.store_part;
let spell_result;
const part_id = spell.base_spell + '.' + part.name
if ('multipliers' in part) { // damage type spell
@ -607,70 +614,65 @@ class SpellDamageCalcNode extends ComputeNode {
}
} else if ('power' in part) {
// TODO: wynn2 formula
const mult_map = stats.get("healMult");
let heal_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_id) {
continue;
}
}
heal_mult *= (1 + v/100);
let _heal_amount = (part.power * getDefenseStats(stats)[0] * (1+stats.get('healPct')/100));
if (stats.has('healPct:'+part_id)) {
_heal_amount *= 1+(stats.get('healPct:'+part_id)/100);
}
let _heal_amount = part.power * getDefenseStats(stats)[0] * heal_mult;
spell_result = {
type: "heal",
heal_amount: _heal_amount
}
} else { // if 'hits' in part
spell_result = {
normal_min: [0, 0, 0, 0, 0, 0],
normal_max: [0, 0, 0, 0, 0, 0],
normal_total: [0, 0],
crit_min: [0, 0, 0, 0, 0, 0],
crit_max: [0, 0, 0, 0, 0, 0],
crit_total: [0, 0],
heal_amount: 0
}
else {
continue;
}
const {name, display = true} = part;
spell_result.name = name;
spell_result.display = display;
spell_results.push(spell_result);
spell_result_map.set(name, spell_result);
}
for (const part of spell_parts) {
if (!('hits' in part)) { continue; }
let spell_result = {
normal_min: [0, 0, 0, 0, 0, 0],
normal_max: [0, 0, 0, 0, 0, 0],
normal_total: [0, 0],
crit_min: [0, 0, 0, 0, 0, 0],
crit_max: [0, 0, 0, 0, 0, 0],
crit_total: [0, 0],
heal_amount: 0
}
const dam_res_keys = ['normal_min', 'normal_max', 'normal_total', 'crit_min', 'crit_max', 'crit_total'];
for (const [subpart_name, hits] of Object.entries(part.hits)) {
const subpart = spell_result_map.get(subpart_name);
if (!subpart) { continue; }
if (spell_result.type) {
if (subpart.type !== spell_result.type) {
throw "SpellCalc total subpart type mismatch";
}
}
const dam_res_keys = ['normal_min', 'normal_max', 'normal_total', 'crit_min', 'crit_max', 'crit_total'];
for (const [subpart_name, hits] of Object.entries(part.hits)) {
const subpart = eval_part(subpart_name);
if (!subpart) { continue; }
if (spell_result.type) {
if (subpart.type !== spell_result.type) {
throw "SpellCalc total subpart type mismatch";
else {
spell_result.type = subpart.type;
}
if (spell_result.type === 'damage') {
for (const key of dam_res_keys) {
for (let i in spell_result.normal_min) {
spell_result[key][i] += subpart[key][i] * hits;
}
}
else {
spell_result.type = subpart.type;
}
if (spell_result.type === 'damage') {
for (const key of dam_res_keys) {
for (let i in spell_result.normal_min) {
spell_result[key][i] += subpart[key][i] * hits;
}
}
}
else {
spell_result.heal_amount += subpart.heal_amount * hits;
}
}
else {
spell_result.heal_amount += subpart.heal_amount * hits;
}
}
const {name, display = true} = part;
spell_result.name = name;
spell_result.display = display;
spell_results.push(spell_result);
spell_result_map.set(name, spell_result);
return spell_result;
}
// TODO: move preprocessing to separate node/node chain
for (const part of spell_parts) {
let spell_result = eval_part(part.name);
display_spell_results.push(spell_result);
}
return display_spell_results;
return spell_results;
}
}
@ -684,17 +686,18 @@ class SpellDamageCalcNode extends ComputeNode {
* spell-damage: List[SpellDamage]) => null
*/
class SpellDisplayNode extends ComputeNode {
constructor(spell) {
super("builder-spell"+spell.base_spell+"-display");
this.spell = spell;
constructor(spell_num) {
super("builder-spell"+spell_num+"-display");
this.spell_idx = spell_num;
}
compute_func(input_map) {
const stats = input_map.get('stats');
const spell_info = input_map.get('spell-info');
const damages = input_map.get('spell-damage');
const spell = this.spell;
const spell = spell_info[0];
const i = this.spell.base_spell;
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, damages);
@ -844,13 +847,9 @@ class AggregateStatsNode extends ComputeNode {
}
}
let radiance_affected = [ /*"hp"*/, "fDef", "wDef", "aDef", "tDef", "eDef", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms",
// "xpb", "lb",
"ref",
/*"str", "dex", "int", "agi", "def",*/ // TODO its affected but i have to make it not affect req
"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",
let radiance_affected = [ /*"hp"*/, "fDef", "wDef", "aDef", "tDef", "eDef", "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",
// wynn2 damages.
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw",//"eDamAddMin","eDamAddMax",
@ -862,8 +861,7 @@ let radiance_affected = [ /*"hp"*/, "fDef", "wDef", "aDef", "tDef", "eDef", "hpr
/*"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",
//"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final",
"healPct", "kb", "weakenEnemy", "slowEnemy", "rDefPct"
//"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
];
/**
* Scale stats if radiance is enabled.
@ -889,12 +887,12 @@ const radiance_node = new (class extends ComputeNode {
}
}
}
// const dam_mults = new Map(ret.get('damMult'));
// dam_mults.set('tome', dam_mults.get('tome') * 1.2)
// ret.set('damMult', dam_mults)
// const def_mults = new Map(ret.get('defMult'));
// def_mults.set('tome', def_mults.get('tome') * 1.2)
// ret.set('defMult', def_mults)
const dam_mults = new Map(ret.get('damMult'));
dam_mults.set('tome', dam_mults.get('tome') * 1.2)
ret.set('damMult', dam_mults)
const def_mults = new Map(ret.get('defMult'));
def_mults.set('tome', def_mults.get('tome') * 1.2)
ret.set('defMult', def_mults)
return ret;
}
else {
@ -953,7 +951,6 @@ class EditableIDSetterNode extends ComputeNode {
this.notify_nodes = notify_nodes.slice();
for (const child of this.notify_nodes) {
child.link_to(this);
child.fail_cb = true;
}
}
@ -990,9 +987,6 @@ class SkillPointSetterNode extends ComputeNode {
this.notify_nodes = notify_nodes.slice();
for (const child of this.notify_nodes) {
child.link_to(this);
child.fail_cb = true;
// This is needed because initially there is a value mismatch possibly... due to setting skillpoints manually
child.mark_input_clean(this.name, null);
}
}
@ -1035,7 +1029,8 @@ class SumNumberInputNode extends InputNode {
}
}
let item_final_nodes = [];
let item_nodes = [];
let item_nodes_map = new Map();
let powder_nodes = [];
let edit_input_nodes = [];
let skp_inputs = [];
@ -1057,9 +1052,9 @@ function builder_graph_init(save_skp) {
// "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) {
}
build_node.link_to(level_input);
atree_merge.link_to(build_node, "build");
let build_encode_node = new BuildEncodeNode();
build_encode_node.link_to(build_node, 'build');
@ -1081,7 +1076,8 @@ function builder_graph_init(save_skp) {
.link_to(powder_node, 'powdering').link_to(item_input, 'item');
item_input = item_powdering;
}
item_final_nodes.push(item_input);
item_nodes.push(item_input);
item_nodes_map.set(eq, item_input);
new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input);
new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input);
//new PrintNode(eq+'-debug').link_to(item_input);
@ -1089,13 +1085,13 @@ function builder_graph_init(save_skp) {
build_node.link_to(item_input, eq);
}
for (const [eq, none_item] of zip2(tome_fields, [none_tomes[0], none_tomes[0], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[2], none_tomes[3]])) {
for (const [eq, none_item] of zip2(tome_fields, [none_tomes[0], none_tomes[0], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[1], none_tomes[2]])) {
let input_field = document.getElementById(eq+"-choice");
let item_image = document.getElementById(eq+"-img");
let item_input = new ItemInputNode(eq+'-input', input_field, none_item);
equip_inputs.push(item_input);
item_final_nodes.push(item_input);
item_nodes.push(item_input);
new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input);
build_node.link_to(item_input, eq);
}
@ -1103,7 +1099,7 @@ function builder_graph_init(save_skp) {
// weapon image changer node.
let weapon_image = document.getElementById("weapon-img");
let weapon_dps = document.getElementById("weapon-dps");
new WeaponInputDisplayNode('weapon-type-display', weapon_image, weapon_dps).link_to(item_final_nodes[8]);
new WeaponInputDisplayNode('weapon-type', weapon_image, weapon_dps).link_to(item_nodes[8]);
// linking to atree verification
atree_validate.link_to(level_input, 'level');

View file

@ -1,601 +0,0 @@
{
"FISSION": {
"displayName": "Fission",
"description": "Explosions from your Exploding ID are twice as big and twice as strong",
"abilities": []
},
"EXPLOSIVE_IMPACT": {
"displayName": "Explosive Impact",
"description": "Your Exploding ID can trigger when hitting mobs with your Main Attack",
"abilities": []
},
"MAGNET": {
"displayName": "Magnet",
"description": "Pulls items within an 8 block radius towards you",
"abilities": []
},
"PLAGUE": {
"displayName": "Plague",
"description": "Poisoned mobs spread their poison to nearby mobs",
"abilities": []
},
"HAWKEYE": {
"displayName": "Hawkeye",
"description": "Condense Arrow Storm into a tight beam. Arrows deal ✤10%, ✦1%, and ❋1%",
"abilities": [{
"class": "Archer",
"base_abil": "Arrow Storm",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Single Stream",
"behavior": "overwrite",
"hits": { "Single Arrow": 5 }
},
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Single Arrow",
"behavior": "overwrite",
"multipliers": [ 10, 0, 1, 0, 0, 1 ]
},
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Total Damage",
"behavior": "modify",
"hits": { "Single Stream": 4 }
}
]
}]
},
"GREED": {
"displayName": "Greed",
"description": "Picking up emeralds heals you and nearby players for 15% max health",
"abilities": []
},
"CAVALRYMAN": {
"displayName": "Cavalryman",
"description": "You may cast spells and attack with a 70% damage penalty while on a horse",
"abilities": []
},
"GUARDIAN": {
"displayName": "Guardian",
"description": "20% of the damage taken by nearby allies is redirected to you",
"abilities": []
},
"HERO": {
"displayName": "Saviours Sacrifice",
"description": "While under 50% maximum health, nearby allies gain 20% bonus damage and defense",
"abilities": []
},
"ALTRUISM": {
"displayName": "Heart of the Pack",
"description": "Nearby players gain 35% of the health you naturally regenerate",
"abilities": []
},
"ARCANES": {
"displayName": "Transcendence",
"description": "30% chance for spells to cost no mana when casted",
"abilities": []
},
"ENTROPY": {
"displayName": "Entropy",
"description": "Meteor falls three times faster",
"abilities": []
},
"ROVINGASSASSIN": {
"displayName": "Roving Assassin",
"description": "Vanish no longer drains mana while invisible",
"abilities": []
},
"MADNESS": {
"displayName": "Madness",
"description": "Cast a random ability every 3 seconds",
"abilities": []
},
"LIGHTWEIGHT": {
"displayName": "Lightweight",
"description": "You no longer take fall damage",
"abilities": []
},
"SORCERY": {
"displayName": "Sorcery",
"description": "30% chance for spells and attacks to cast a second time at no additional cost",
"abilities": []
},
"TAUNT": {
"displayName": "Taunt",
"description": "Mobs within 12 blocks target you upon casting War Scream",
"abilities": []
},
"RALLY": {
"displayName": "Rally",
"description": "Charge heals you by 10% and nearby allies by 15% on impact, but becomes harmless",
"abilities": [{
"class": "Warrior",
"base_abil": "Charge",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 2,
"display": "Rally Self Heal",
"target_part": "Rally Self Heal",
"power": 0.1
},
{
"type": "add_spell_prop",
"base_spell": 2,
"target_part": "Rally Ally Heal",
"power": 0.15
},
{
"type": "raw_stat",
"bonuses": [
{
"type": "stat",
"name": "damMult.Rally:2.Flying Kick",
"value": -100
},
{
"type": "stat",
"name": "damMult.Rally:2.Collide",
"value": -100
},
{
"type": "stat",
"name": "damMult.Rally:2.Heavy Impact",
"value": -100
},
{
"type": "stat",
"name": "damMult.Rally:2.Flyby Jab",
"value": -100
}
]
}
]
}]
},
"CHERRY_BOMBS": {
"displayName": "Cherry Bombs",
"description": "Turn Smoke Bomb into three firecrackers that fly farther, tighter, and deal high damage instantly",
"abilities": [{
"class": "Assassin",
"base_abil": "Smoke Bomb",
"effects": [{
"type": "replace_spell",
"name": "Cherry Bomb",
"cost": 35,
"base_spell": 4,
"display": "Total Damage",
"parts": [
{
"name": "Single Bomb",
"type": "damage",
"multipliers": [95, 40, 0, 0, 0, 40]
},
{
"name": "Total Damage",
"type": "total",
"hits": {
"Single Bomb": 3
}
}
]
}]
}]
},
"FREERUNNER": {
"displayName": "Freerunner",
"description": "Double your sprint speed when your sprint bar is under 30%",
"abilities": []
},
"PEACEFUL_EFFIGY": {
"displayName": "Peaceful Effigy",
"description": "Your totem will last twice as long",
"abilities": []
},
"FURIOUS_EFFIGY": {
"displayName": "Furious Effigy",
"description": "Totem effects are twice as fast, but duration is halved",
"abilities": [{
"class": "Shaman",
"base_abil": "Totem",
"properties": {
"rate": -0.2,
"totem_mul": 2.5
},
"effects": []
}]
},
"FLASHFREEZE": {
"displayName": "Flashfreeze",
"description": "Ice Snake is instant but has a reduced range",
"abilities": []
},
"GRAVITYWELL": {
"displayName": "Gravity Well",
"description": "Meteor has increased blast radius and pulls enemies instead",
"abilities": []
},
"DESC_SNOWYSTEPS": {
"displayName": "Snowy Steps",
"description": "Leaves a trail of snow behind you",
"abilities": []
},
"GEOCENTRISM": {
"displayName": "Geocentrism",
"description": "Aura radiates from you instead of your totem and can be cast anytime",
"abilities": []
},
"DESC_FESTIVESPIRIT": {
"displayName": "Festive Spirit",
"description": "Plays wintery tunes",
"abilities": []
},
"TEMBLOR": {
"displayName": "Temblor",
"description": "Bash gains +1 Area of Effect and is 25% faster.",
"abilities": [{
"class": "Warrior",
"base_abil": "Bash",
"properties": { "aoe": 1 },
"effects": []
}]
},
"RECKLESS_ABANDON": {
"displayName": "Reckless Abandon",
"description": "Sacrifice War Scream's defense bonus to give Tempest two extra hits, extra damage, and extra speed",
"abilities": [{
"class": "Warrior",
"base_abil": "War Scream",
"dependencies": [ "Tempest" ],
"effects": [
{
"type": "add_spell_prop",
"base_spell": 4,
"target_part": "Tempest",
"behavior": "modify",
"multipliers": [0, 5, 0, 0, 15, 5]
},
{
"type": "add_spell_prop",
"base_spell": 4,
"target_part": "Tempest Total Damage",
"behavior": "modify",
"hits": { "Tempest": 2 }
}
]
}]
},
"ALTEREGO": {
"displayName": "Alter Ego",
"description": "Awakened can be activated after saving 40% less mana, but its duration is reduced by 25%.",
"abilities": []
},
"FOREST_BLESSING": {
"displayName": "Forest's Blessing",
"description": "Your archer summons have increased movement speed, attack speed and vision. Arrow Bomb's damage is reduced by -30% Neutral damage.",
"abilities": [{
"class": "Archer",
"base_abil": "Arrow Bomb",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Arrow Bomb",
"behavior": "modify",
"multipliers": [-30, 0, 0, 0, 0, 0]
},
{
"type": "add_spell_prop",
"base_spell": 8,
"target_part": "DPS",
"behavior": "modify",
"hits": { "Single Hit": 1.666666666666667 }
},
{
"type": "add_spell_prop",
"base_spell": 10,
"target_part": "Crow DPS",
"behavior": "modify",
"hits": { "Single Hit": 0.555555555555556 }
}
]
}]
},
"SOUL_EATER": {
"displayName": "Soul Eater",
"description": "Devour and Harvester grant double mana, but your maximum Marks are decreased by 1.",
"abilities": [{
"class": "Assassin",
"base_abil": "Marked",
"effects": [{
"type": "stat_scaling",
"slider": true,
"slider_name": "Marked",
"slider_max": -1
}]
}]
},
"STRINGS_OF_FATE": {
"displayName": "Strings of Fate",
"description": "Your puppets have a lifetime of 3 seconds, but do double damage with attacks and explosions.",
"abilities": [{
"class": "Shaman",
"base_abil": "Puppet Master",
"effects": [
{
"type": "raw_stat",
"bonuses": [
{
"type": "stat",
"name": "damMult.FateString:6.Puppet Hit",
"value": 100
},
{
"type": "stat",
"name": "damMult.FateString:6.Puppet Explosion",
"value": 100
}
]
}
]
}]
},
"ESCAPE_ROUTE": {
"displayName": "Escape Route",
"description": "Frenzy and Time Dilation charge twice as fast, but to a halved maximum.",
"abilities": [
{
"class": "Archer",
"base_abil": "Frenzy",
"effects": [
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Hits dealt",
"slider_max": -18,
"output": {
"type": "stat",
"name": "spd"
},
"scaling": [-3]
},
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Hits dealt",
"output": {
"type": "stat",
"name": "spd"
},
"scaling": [6],
"max": 35
}
]
},
{
"class": "Mage",
"base_abil": "Time Dilation",
"effects": [
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Time Dilated",
"slider_max": -22,
"output": {
"type": "stat",
"name": "spd"
},
"scaling": [-10]
},
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Time Dilated",
"output": {
"type": "stat",
"name": "spd"
},
"scaling": [20],
"max": 150
}
]
}
]
},
"DIVINE_HONOR": {
"displayName": "Divine Honor",
"description": "Increase the bonus from Radiance by 5%. Decrease the Earth damage of Bash by -15%.",
"abilities": [{
"class": "Warrior",
"base_abil": "Bash",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Single Hit",
"behavior": "modify",
"multipliers": [0, -15, 0, 0, 0, 0]
}
]
}]
},
"GENTLE_GLOW": {
"displayName": "Gentle Glow",
"description": "Orphion's Pulse and Fluid Healing restore more health, especially to allies, but at a slower speed.",
"abilities": []
},
"PERFECT_RECALL": {
"displayName": "Perfect Recall",
"description": "Memory Recollection casts an extra spell, but only activates at 150 banked mana.",
"abilities": []
},
"OVERWHELM": {
"displayName": "Overwhelm",
"description": "Bash will hit +2 times.",
"abilities": [{
"class": "Warrior",
"base_abil": "Bash",
"properties": { "hits": 2 },
"effects": []
}]
},
"JUGGLE": {
"displayName": "Juggle",
"description": "Stronger Multihit adds an additional 12 hits. All hits are reduced by -10% Neutral damage.",
"abilities": [{
"class": "Assassin",
"base_abil": "Multihit",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Total Damage",
"behavior": "modify",
"hits": { "Per Hit": 12 }
},
{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Per Hit",
"behavior": "modify",
"multipliers": [ -10, 0, 0, 0, 0, 0 ]
}
]
}]
},
"GRUESOME_KNOTS": {
"displayName": "Gruesome Knots",
"description":"Twisted Tether spends twice as much of your Blood Pool to deal triple damage",
"abilities": [{
"class": "Shaman",
"base_abil": "Twisted Tether",
"effects": [{
"type": "raw_stat",
"bonuses": [
{
"type": "stat",
"name": "damMult.GruesomeKnots:8.Tether Tick",
"value": 200
}
]
}]
}]
},
"COAGULATE": {
"displayName": "Coagulate",
"description":"Blood Connection launches you further and higher upon teleporting to a nearby Totem",
"abilities": []
},
"DEADWEIGHT": {
"displayName": "Dead Weight",
"description":"Totem's horizontal velocity is greatly increased at the cost of vertical movement",
"abilities": []
},
"EXPUNGE": {
"displayName": "Expunge",
"description":"When using Heal, instead cast one instant pulse that heals 20% of your max health",
"abilities": [{
"class": "Mage",
"base_abil": "Heal",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Second and Third Pulses",
"behavior": "modify",
"power": -0.20
},
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Heal",
"behavior": "modify",
"power": 0.05
}
]
}]
},
"LUNGE": {
"displayName": "Lunge",
"description":"Hop's horizontal velocity is greatly increased",
"abilities": []
},
"WINDSURF": {
"displayName": "Windsurf",
"description":"Righting Reflex lasts twice as long and is affected stronger by movement speed",
"abilities": []
},
"HELLFIRE": {
"displayName": "Hellfire",
"description":"Boiling Blood has no cooldown, is stronger, and does not slow. Cuts Bash's power and disables Discombobulate",
"abilities": [{
"class": "Warrior",
"base_abil": "Bash",
"dependencies": [ "Boiling Blood" ],
"properties": {
"rate": 0.8333333333333333333333333333,
"num_bloods": 0
},
"effects": [
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Single Hit",
"behavior": "modify",
"multipliers": [ -85, -30, 0, 0, 0, 0 ]
},
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Boiling Blood Tick",
"behavior": "modify",
"multipliers": [ -25, 0, 0, 0, 30, 0 ]
},
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Boiling Blood DPS (Total)",
"behavior": "merge",
"display": "Boiling Blood DPS (Total)",
"hits": { "Boiling Blood DPS": "Bash.num_bloods" }
},
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Boiling Blood Stacks",
"slider_step": 1,
"slider_max": 20,
"slider_default": 4,
"output": [
{
"type": "prop",
"abil": "Bash",
"name": "num_bloods"
}
],
"scaling": [1]
},
{
"type": "stat_scaling",
"slider": true,
"behavior": "modify",
"slider_name": "Hits dealt",
"output": [
{ "type": "stat", "name": "nDamAddMin" }, { "type": "stat", "name": "nDamAddMax" },
{ "type": "stat", "name": "eDamAddMin" }, { "type": "stat", "name": "eDamAddMax" },
{ "type": "stat", "name": "tDamAddMin" }, { "type": "stat", "name": "tDamAddMax" },
{ "type": "stat", "name": "wDamAddMin" }, { "type": "stat", "name": "wDamAddMax" },
{ "type": "stat", "name": "fDamAddMin" }, { "type": "stat", "name": "fDamAddMax" },
{ "type": "stat", "name": "aDamAddMin" }, { "type": "stat", "name": "aDamAddMax" }
],
"scaling": [-5]
}
]
}]
}
}

View file

@ -155,7 +155,6 @@ class ValueCheckComputeNode extends ComputeNode {
if (this.dirty === 0) {
return this;
}
if (COMPUTE_GRAPH_DEBUG) { node_debug_stack.push(this.name); }
let calc_inputs = new Map();
for (const input of this.inputs) {
@ -171,7 +170,6 @@ class ValueCheckComputeNode extends ComputeNode {
for (const child of this.children) {
child.mark_input_clean(this.name, this.value);
}
if (COMPUTE_GRAPH_DEBUG) { node_debug_stack.pop(this.name); }
return this;
}
@ -190,6 +188,7 @@ let graph_live_update = false;
* @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);
}
@ -226,8 +225,8 @@ class InputNode extends ValueCheckComputeNode {
constructor(name, input_field) {
super(name);
this.input_field = input_field;
this.input_field.addEventListener("input", () => { if (graph_live_update) calcSchedule(this, 500) } );
this.input_field.addEventListener("change", () => { if (graph_live_update) calcSchedule(this, 5) } );
this.input_field.addEventListener("input", () => calcSchedule(this, 500));
this.input_field.addEventListener("change", () => calcSchedule(this, 5));
//calcSchedule(this); Manually fire first update for better control
}

View file

@ -1,5 +1,6 @@
let recipeTypes = ["HELMET","CHESTPLATE","LEGGINGS","BOOTS","RELIK","WAND","SPEAR","DAGGER","BOW","RING","NECKLACE","BRACELET","POTION", "SCROLL","FOOD"];
let recipeTypes = ["HELMET","CHESTPLATE","LEGGINGS","BOOTS","RELIK","WAND","SPEAR","DAGGER","BOW","RING","NECKLACE","BRACELET","SCROLL","FOOD","POTION"];
let levelTypes = ["1-3","3-5","5-7","7-9","10-13","13-15","15-17","17-19","20-23","23-25","25-27","27-29","30-33","33-35","35-37","37-39","40-43","43-45","45-47","47-49","50-53","53-55","55-57","57-59","60-63","63-65","65-67","67-69","70-73","73-75","75-77","77-79","80-83","83-85","85-87","87-89","90-93","93-95","95-97","97-99","100-103","103-105",]
let ingFields = ["fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "lq", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "jh", "sprint", "sprintReg", "gXp", "gSpd"];
function encodeCraft(craft) {
if (craft) {
@ -180,6 +181,7 @@ class Craft{
/* Change certain IDs based on material tier.
healthOrDamage changes.
duration and durability change. (but not basicDuration)
*/
let matmult = 1;
let tierToMult = [0,1,1.25,1.4];

View file

@ -136,6 +136,8 @@ function calculateCraft() {
}
let ingreds = [];
for (i = 1; i < 7; i++) {
console.log("ing-choice-"+i);
// console.log(getValue("ing-choice-"+i));
getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i))));
}
let atkSpd = "NORMAL"; //default attack speed will be normal.

View file

@ -76,7 +76,7 @@ function encodeCustom(custom, verbose) {
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(val.replaceAll(" ", "%20").length, 2) + val.replaceAll(" ", "%20"); //values cannot go above 4096 chars!!!! Is this ok?
}
} else if (typeof (val) === "number" && val != 0) {
let len = Math.max(1, Math.ceil(log(64, Math.abs(val) + 1)));
let len = Math.max(1, Math.ceil(log(64, Math.abs(val))));
let sign = Boolean(val / Math.abs(val) < 0) | 0;
//console.log(sign);
//hash += Base64.fromIntN(i,2) + Base64.fromIntN(val,Math.max(1,Math.ceil(log(64,Math.abs(val))))) + "_";
@ -246,6 +246,7 @@ class Custom {
}
}
let type = this.statMap.get("type").toLowerCase();
console.log(type);
if (weaponTypes.includes(type)) {
for (const n of ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]) {
if (!(this.statMap.has(n) && this.statMap.get(n))) {

View file

@ -478,19 +478,31 @@ function base_to_range(id) {
let base = parseFloat(getValue(id+"-choice-base"));
if(base) {
//This version allows overriding of min and max.
if (base == 0) {
// NOTE: DO NOT remove this case! idRound behavior does not round to 0!
setValue(id+"-choice-max", 0);
setValue(id+"-choice-min", 0);
}
else if ((base > 0) != (reversedIDs.includes(id))) { // logical XOR. positive rolled IDs
setValue(id+"-choice-max", idRound(Math.round(pos_range[1]*base)));
setValue(id+"-choice-min", idRound(Math.round(pos_range[0]*base)));
} else { //negative rolled IDs
setValue(id+"-choice-max", idRound(Math.round(neg_range[1]*base)));
setValue(id+"-choice-min", idRound(Math.round(neg_range[0]*base)));
if (reversedIDs.includes(id)) {
if (base < 0) {
setValue(id+"-choice-min", Math.min(Math.round(neg_range[1]*base),-1));
} else {
setValue(id+"-choice-min", Math.max(Math.round(pos_range[1]*base),1));
}
if (base < 0) {
setValue(id+"-choice-max", Math.min(Math.round(neg_range[0]*base),-1));
} else {
setValue(id+"-choice-max", Math.max(Math.round(pos_range[0]*base),1));
}
} else {
if (base < 0) {
setValue(id+"-choice-min", Math.min(Math.round(neg_range[0]*base),-1));
} else {
setValue(id+"-choice-min", Math.max(Math.round(pos_range[0]*base),1));
}
if (base < 0) {
setValue(id+"-choice-max", Math.min(Math.round(neg_range[1]*base),-1));
} else {
setValue(id+"-choice-max", Math.max(Math.round(pos_range[1]*base),1));
}
}
/* No overiding min/max version
if (!getValue(id+"-choice-min")) {
if (base < 0) {

View file

@ -67,9 +67,6 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
// 2.1. First, apply neutral conversion (scale weapon damage). Keep track of total weapon damage here.
let damages = [];
const neutral_convert = conversions[0] / 100;
if (neutral_convert == 0) {
present = [false, false, false, false, false, false]
}
let weapon_min = 0;
let weapon_max = 0;
for (const damage of weapon_damages) {
@ -137,15 +134,15 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
let damageBoost = 1 + skill_boost[i] + static_boost
+ ((stats.get(damage_specific) + stats.get(damage_elements[i]+'DamPct')) /100);
if (i > 0) {
damageBoost += (stats.get('r'+specific_boost_str+'Pct') + stats.get('rDamPct')) / 100;
damageBoost += stats.get('r'+specific_boost_str+'Pct') / 100;
}
damages[i][0] *= Math.max(damageBoost, 0);
damages[i][1] *= Math.max(damageBoost, 0);
// Collect total damage post %boost
}
let total_elem_min = total_min - save_prop[0][0];
let total_elem_max = total_max - save_prop[0][1];
let total_elem_min = total_min - damages[0][0];
let total_elem_max = total_max - damages[0][1];
// 5.2: Raw application.
let prop_raw = stats.get(specific_boost_str.toLowerCase()+'Raw') + stats.get('damRaw');
@ -340,3 +337,17 @@ const default_spells = {
]
}]
};
const spell_table = {
"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},
] },
{ title: "Chain Lightning", cost: 0, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: [200, 225, 250, 275, 300], conversion: [0,0,100,0,0,0], summary: true},
]},
{ title: "Courage", cost: 0, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: [75, 87.5, 100, 112.5, 125], conversion: [0,0,0,0,100,0], summary: true},
]}, //[75, 87.5, 100, 112.5, 125]
]
};

View file

@ -214,18 +214,14 @@ function displayExpandedItem(item, parent_id){
parent_div.appendChild(make_elem("div", ["col"], { textContent: "Set: " + item.get(id).toString() }));
} else if (id === "majorIds") {
//console.log(item.get(id));
for (let major_id_str of item.get(id)) {
if (major_id_str in major_ids) {
let major_id_info = major_ids[major_id_str];
major_id_str = `+${major_id_info.displayName}: ${major_id_info.description}`;
}
for (let majorID of item.get(id)) {
let p_elem = make_elem("div", ['col']);
let title_elem = make_elem("b");
let b_elem = make_elem("b");
if (major_id_str.includes(":")) {
let name = major_id_str.substring(0, major_id_str.indexOf(":")+1);
let mid = major_id_str.substring(major_id_str.indexOf(":")+1);
if (majorID.includes(":")) {
let name = majorID.substring(0, majorID.indexOf(":")+1);
let mid = majorID.substring(majorID.indexOf(":")+1);
if (name.charAt(0) !== "+") {name = "+" + name}
title_elem.classList.add("Legendary");
title_elem.textContent = name;
@ -234,9 +230,10 @@ function displayExpandedItem(item, parent_id){
p_elem.appendChild(title_elem);
p_elem.appendChild(b_elem);
} else {
if (major_id_str.charAt(0) !== "+") {major_id_str = "+" + major_id_str}
let name = item.get(id).toString()
if (name.charAt(0) !== "+") {name = "+" + name}
b_elem.classList.add("Legendary");
b_elem.textContent = major_id_str;
b_elem.textContent = name;
p_elem.appendChild(b_elem);
}
parent_div.appendChild(p_elem);
@ -309,11 +306,11 @@ function displayExpandedItem(item, parent_id){
// TODO: kinda jank but replacing lists with txt at this step
let damages = item.get(id);
if (item.get("tier") !== "Crafted") {
damages = damages.map(x => Math.floor(x));
damages = damages.map(x => Math.round(x));
item.set(id, damages[0]+"-"+damages[1]);
}
else {
damages = damages.map(x => x.map(y => Math.floor(y)));
damages = damages.map(x => x.map(y => Math.round(y)));
item.set(id, damages[0][0]+"-"+damages[0][1]+"\u279c"+damages[1][0]+"-"+damages[1][1]);
}
}
@ -466,6 +463,7 @@ function displayExpandedItem(item, parent_id){
if (item.get("tier") && item.get("tier") !== " ") {
let item_desc_elem = make_elem("div", ["col", item.get("tier")]);
if (tome_types.includes(item.get("type"))) {
tome_type_map = new Map([["weaponTome", "Weapon Tome"],["armorTome", "Armor Tome"],["guildTome", "Guild Tome"]]);
item_desc_elem.textContent = item.get("tier")+" "+tome_type_map.get(item.get("type"));
} else {
item_desc_elem.textContent = item.get("tier")+" "+item.get("type");
@ -495,7 +493,7 @@ function displayExpandedItem(item, parent_id){
base_dps_elem.textContent = "Base DPS: "+base_dps_min.toFixed(3)+"\u279c"+base_dps_max.toFixed(3);
}
else {
base_dps_elem.textContent = "Base DPS: "+(total_damages.toFixed(3));
base_dps_elem.textContent = "Base DPS: "+(total_damages);
}
parent_div.append(make_elem("p"), base_dps_elem);
}
@ -1180,6 +1178,14 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){
boost.classList.add(eledefs[i] >= 0 ? "positive" : "negative");
boost.classList.add("col");
boost.classList.add("text-end");
let defRaw = statMap.get(skp_elements[i]+"Def");
let defPct = statMap.get(skp_elements[i]+"DefPct")/100;
if (defRaw < 0) {
defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct;
} else {
defPct >= 0 ? defPct = "+ " + defPct: defPct = "- " + defPct;
}
eledefElemRow.appendChild(boost);
if (insertSummary) {
@ -1225,7 +1231,7 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){
}
}
function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon) {
function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overall=false) {
parent_elem.textContent = "";
if (powderSpecials.length === 0) {
parent_elem.style = "display: none";
@ -1246,66 +1252,129 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon) {
//each entry of powderSpecials is [ps, power]
for (special of specials) {
//iterate through the special and display its effects.
let powder_special_elem = make_elem("p", ["pt-3"]);
let powder_special = make_elem("p", ["pt-3"]);
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = make_elem("p");
let specialEffects = make_elem("p");
// TODO janky and depends on the order of powder specials being ETWFA. This should be encoded in the powder special object.
let element_num = powderSpecialStats.indexOf(special[0]) + 1;
specialTitle.classList.add(damageClasses[element_num]);
let powder_special = special[0];
specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]);
let effects = special[0]["weaponSpecialEffects"];
let power = special[1];
specialTitle.textContent = powder_special.weaponSpecialName + " " + Math.floor((power-1)*0.5 + 4) + (power % 2 == 0 ? ".5" : "");
specialTitle.textContent = special[0]["weaponSpecialName"] + " " + Math.floor((power-1)*0.5 + 4) + (power % 2 == 0 ? ".5" : "");
for (const [key,value] of powder_special.weaponSpecialEffects) {
if(key === "Damage"){
//if this special is an instant-damage special (Quake, Chain Lightning, Courage Burst), display the damage.
let specialDamage = document.createElement("p");
// specialDamage.classList.add("item-margin");
let conversions = [0, 0, 0, 0, 0, 0];
conversions[element_num] = powder_special.weaponSpecialEffects.get("Damage")[power-1];
let _results = calculateSpellDamage(stats, weapon, conversions, false, true, "0.Powder Special");
let critChance = skillPointsToPercentage(skillpoints[1]);
let save_damages = [];
let totalDamNormal = _results[0];
let totalDamCrit = _results[1];
let results = _results[2];
for (let i = 0; i < 6; ++i) {
for (let j in results[i]) {
results[i][j] = results[i][j].toFixed(2);
}
}
let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0;
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
let averageWrap = document.createElement("p");
let averageLabel = document.createElement("span");
averageLabel.textContent = "Average: ";
let averageLabelDmg = document.createElement("span");
averageLabelDmg.classList.add("Damage");
averageLabelDmg.textContent = averageDamage.toFixed(2);
averageWrap.appendChild(averageLabel);
averageWrap.appendChild(averageLabelDmg);
specialDamage.appendChild(averageWrap);
specialEffects.append(specialDamage);
}
else {
if (!overall || powderSpecialStats.indexOf(special[0]) == 2 || powderSpecialStats.indexOf(special[0]) == 3 || powderSpecialStats.indexOf(special[0]) == 4) {
for (const [key,value] of effects) {
let effect = document.createElement("p");
effect.textContent += key + ": " + value[power-1] + specialSuffixes.get(key);
if(key === "Damage"){
effect.textContent += elementIcons[powderSpecialStats.indexOf(special[0])];
}
if(special[0]["weaponSpecialName"] === "Wind Prison" && key === "Damage Boost") {
effect.textContent += " (only 1st hit)";
}
specialEffects.appendChild(effect);
}
}
powder_special.appendChild(specialTitle);
powder_special.appendChild(specialEffects);
powder_special_elem.appendChild(specialTitle);
powder_special_elem.appendChild(specialEffects);
//if this special is an instant-damage special (Quake, Chain Lightning, Courage Burst), display the damage.
let specialDamage = document.createElement("p");
// specialDamage.classList.add("item-margin");
let spells = spell_table["powder"];
if (powderSpecialStats.indexOf(special[0]) == 0 || powderSpecialStats.indexOf(special[0]) == 1 || powderSpecialStats.indexOf(special[0]) == 3) { //Quake, Chain Lightning, or Courage
let spell = (powderSpecialStats.indexOf(special[0]) == 3 ? spells[2] : spells[powderSpecialStats.indexOf(special[0])]);
let part = spell["parts"][0];
parent_elem.appendChild(powder_special_elem);
let tmp_conv = [];
for (let i in part.conversion) {
tmp_conv.push(part.conversion[i] * part.multiplier[power-1] / 100);
}
console.log(tmp_conv);
let _results = calculateSpellDamage(stats, weapon, tmp_conv, false, true);
let critChance = skillPointsToPercentage(skillpoints[1]);
let save_damages = [];
let totalDamNormal = _results[0];
let totalDamCrit = _results[1];
let results = _results[2];
for (let i = 0; i < 6; ++i) {
for (let j in results[i]) {
results[i][j] = results[i][j].toFixed(2);
}
}
let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0;
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
let averageWrap = document.createElement("p");
let averageLabel = document.createElement("span");
averageLabel.textContent = "Average: ";
let averageLabelDmg = document.createElement("span");
averageLabelDmg.classList.add("Damage");
averageLabelDmg.textContent = averageDamage.toFixed(2);
averageWrap.appendChild(averageLabel);
averageWrap.appendChild(averageLabelDmg);
specialDamage.appendChild(averageWrap);
if (!overall) {
let nonCritLabel = document.createElement("p");
nonCritLabel.textContent = "Non-Crit Average: "+nonCritAverage.toFixed(2);
nonCritLabel.classList.add("damageSubtitle");
nonCritLabel.classList.add("item-margin");
specialDamage.append(nonCritLabel);
for (let i = 0; i < 6; i++){
if (results[i][1] > 0){
let p = document.createElement("p");
p.classList.add("damagep");
p.classList.add(damageClasses[i]);
p.textContent = results[i][0]+"-"+results[i][1];
specialDamage.append(p);
}
}
let normalDamage = document.createElement("p");
normalDamage.textContent = "Total: " + totalDamNormal[0].toFixed(2) + "-" + totalDamNormal[1].toFixed(2);
normalDamage.classList.add("itemp");
specialDamage.append(normalDamage);
let nonCritChanceLabel = document.createElement("p");
nonCritChanceLabel.textContent = "Non-Crit Chance: " + ((1-critChance)*100).toFixed(2) + "%";
specialDamage.append(nonCritChanceLabel);
let critLabel = document.createElement("p");
critLabel.textContent = "Crit Average: "+critAverage.toFixed(2);
critLabel.classList.add("damageSubtitle");
critLabel.classList.add("item-margin");
specialDamage.append(critLabel);
for (let i = 0; i < 6; i++){
if (results[i][1] > 0){
let p = document.createElement("p");
p.classList.add("damagep");
p.classList.add(damageClasses[i]);
p.textContent = results[i][2]+"-"+results[i][3];
specialDamage.append(p);
}
}
let critDamage = document.createElement("p");
critDamage.textContent = "Total: " + totalDamCrit[0].toFixed(2) + "-" + totalDamCrit[1].toFixed(2);
critDamage.classList.add("itemp");
specialDamage.append(critDamage);
let critChanceLabel = document.createElement("p");
critChanceLabel.textContent = "Crit Chance: " + (critChance*100).toFixed(2) + "%";
specialDamage.append(critChanceLabel);
save_damages.push(averageDamage);
}
powder_special.append(specialDamage);
}
parent_elem.appendChild(powder_special);
}
}

View file

@ -35,60 +35,42 @@ let idPrefixes = {"displayName": "",
"str":"Strength: ",
"dex":"Dexterity: ",
"int":"Intelligence: ",
"def":"Defense: ",
"agi":"Agility: ",
"def":"Defense: ","agi":"Agility: ",
"hpBonus":"Health Bonus: ",
"hprRaw":"Raw Health Regen: ",
"hprRaw":"Health Regen Raw: ",
"hprPct":"Health Regen %: ",
"healPct":"Healing Effectiveness %",
"sdRaw":"Spell Damage Raw: ",
"sdRaw":"Raw Spell Damage: ",
"rSdRaw":"Elem. Spell Damage Raw: ",
"nSdRaw":"Neut. Spell Damage Raw: ",
"eSdRaw":"Earth Spell Damage Raw: ",
"tSdRaw":"Thunder Spell Damage Raw: ",
"wSdRaw":"Water Spell Damage Raw: ",
"fSdRaw":"Fire Spell Damage Raw: ",
"aSdRaw":"Air Spell Damage Raw: ",
"eSdRaw":"Raw Earth Spell Damage: ",
"tSdRaw":"Raw Thunder Spell Damage: ",
"wSdRaw":"Raw Water Spell Damage: ",
"fSdRaw":"Raw Fire Spell Damage: ",
"aSdRaw":"Raw Air Spell Damage: ",
"sdPct":"Spell Damage %: ",
"rSdPct":"Elem. Spell Damage %: ",
"nSdPct":"Neut. Spell Damage %: ",
"eSdPct":"Earth Spell Damage %: ",
"tSdPct":"Thunder Spell Damage %: ",
"wSdPct":"Water Spell Damage %: ",
"fSdPct":"Fire Spell Damage %: ",
"aSdPct":"Air Spell Damage %: ",
"mdRaw":"Melee Damage Raw: ",
"eSdPct":"% Earth Spell Damage: ",
"tSdPct":"% Thunder Spell Damage: ",
"wSdPct":"% Water Spell Damage: ",
"fSdPct":"% Fire Spell Damage: ",
"aSdPct":"% Air Spell Damage: ",
"mdRaw":"Raw Melee Damage: ",
"rMdRaw":"Elem. Melee Damage Raw: ",
"nMdRaw":"Neut. Melee Damage Raw: ",
"eMdRaw":"Earth Melee Damage Raw: ",
"tMdRaw":"Thunder Melee Damage Raw: ",
"wMdRaw":"Water Melee Damage Raw: ",
"fMdRaw":"Fire Melee Damage Raw: ",
"aMdRaw":"Air Melee Damage Raw: ",
"eMdRaw":"Raw Earth Melee Damage: ",
"tMdRaw":"Raw Thunder Melee Damage: ",
"wMdRaw":"Raw Water Melee Damage: ",
"fMdRaw":"Raw Fire Melee Damage: ",
"aMdRaw":"Raw Air Melee Damage: ",
"mdPct":"Melee Damage %: ",
"rMdPct":"Elem. Melee Damage %: ",
"nMdPct":"Neut. Melee Damage %: ",
"eMdPct":"Earth Melee Damage %: ",
"tMdPct":"Thunder Melee Damage %: ",
"wMdPct":"Water Melee Damage %: ",
"fMdPct":"Fire Melee Damage %: ",
"aMdPct":"Air Melee Damage %: ",
"damRaw":"Damage Raw: ",
"rDamRaw":"Elemental Damage Raw: ",
"nDamRaw":"Neutral Damage Raw: ",
"eDamRaw":"Earth Damage Raw: ",
"tDamRaw":"Thunder Damage Raw: ",
"wDamRaw":"Water Damage Raw: ",
"fDamRaw":"Fire Damage Raw: ",
"aDamRaw":"Air Damage Raw: ",
"damPct":"Damage %: ",
"rDamPct":"Elemental Damage %: ",
"nDamPct":"Neutral Damage %: ",
"eDamPct":"Earth Damage %: ",
"tDamPct":"Thunder Damage %: ",
"wDamPct":"Water Damage %: ",
"fDamPct":"Fire Damage %: ",
"aDamPct":"Air Damage %: ",
"eMdPct":"% Earth Melee Damage: ",
"tMdPct":"% Thunder Melee Damage: ",
"wMdPct":"% Water Melee Damage: ",
"fMdPct":"% Fire Melee Damage: ",
"aMdPct":"% Air Melee Damage: ",
"mr":"Mana Regen: ",
"ms":"Mana Steal: ",
"ref":"Reflection: ",
@ -98,12 +80,16 @@ let idPrefixes = {"displayName": "",
"expd":"Exploding: ",
"spd":"Walk Speed Bonus: ",
"atkTier":"Attack Speed Bonus: ",
"eDamPct":"Earth Damage %: ",
"tDamPct":"Thunder Damage %: ",
"wDamPct":"Water Damage %: ",
"fDamPct":"Fire Damage %: ",
"aDamPct":"Air Damage %: ",
"eDefPct":"Earth Defense %: ",
"tDefPct":"Thunder Defense %: ",
"wDefPct":"Water Defense %: ",
"fDefPct":"Fire Defense %: ",
"aDefPct":"Air Defense %: ",
"rDefPct":"Elemental Defense %: ",
"spPct1":"1st Spell Cost %: ",
"spRaw1":"1st Spell Cost Raw: ",
"spPct2":"2nd Spell Cost %: ",
@ -122,9 +108,6 @@ let idPrefixes = {"displayName": "",
"eSteal":"Stealing: ",
"gXp":"Gathering XP Bonus: ",
"gSpd":"Gathering Speed Bonus: ",
"kb":"Knockback: ",
"weakenEnemy":"Weaken Enemy: ",
"slowEnemy":"Slow Enemy: ",
"slots":"Powder Slots: ",
"set":"Set: ",
"quest":"Quest Req: ",
@ -160,7 +143,6 @@ let idSuffixes = {"displayName": "",
"hpBonus":"",
"hprRaw":"",
"hprPct":"%",
"healPct":"%",
"sdRaw":"",
"rSdRaw":"",
"nSdRaw":"",
@ -193,22 +175,6 @@ let idSuffixes = {"displayName": "",
"wMdPct":"%",
"fMdPct":"%",
"aMdPct":"%",
"damRaw":"",
"rDamRaw":"",
"nDamRaw":"",
"eDamRaw":"",
"tDamRaw":"",
"wDamRaw":"",
"fDamRaw":"",
"aDamRaw":"",
"damPct":"%",
"rDamPct":"%",
"nDamPct":"%",
"eDamPct":"%",
"tDamPct":"%",
"wDamPct":"%",
"fDamPct":"%",
"aDamPct":"%",
"mr":"/5s",
"ms":"/3s",
"ref":"%",
@ -218,12 +184,16 @@ let idSuffixes = {"displayName": "",
"expd":"%",
"spd":"%",
"atkTier":" tier",
"eDamPct":"%",
"tDamPct":"%",
"wDamPct":"%",
"fDamPct":"%",
"aDamPct":"%",
"eDefPct":"%",
"tDefPct":"%",
"wDefPct":"%",
"fDefPct":"%",
"aDefPct":"%",
"rDefPct":"%",
"spPct1":"%",
"spRaw1":"",
"spPct2":"%",
@ -242,14 +212,11 @@ let idSuffixes = {"displayName": "",
"eSteal":"%",
"gXp":"%",
"gSpd":"%",
"kb": "%",
"weakenEnemy": "%",
"slowEnemy": "%",
"slots":"",
"set":" set.",
"quest":"",
"restrict":"",
"lore": "",
"lore": ""
};
//Used for item IDs and ingredient id field IDs
@ -278,10 +245,10 @@ let itemIDPrefixes = {
//Used for ingredient posMods IDs
let posModPrefixes = {
"left":"Effectiveness Left: ",
"right":"Effectiveness Right: ",
"right":"EFfectiveness Right: ",
"above":"Effectiveness Above: ",
"under":"Effectiveness Under: ",
"touching":"Effectiveness Touching: ",
"touching":"EFfectiveness Touching: ",
"notTouching":"Effectiveness Not Touching: "
}
let posModSuffixes = {
@ -312,7 +279,6 @@ let build_overall_display_commands = [
"spRegen",
"eSteal",
"gXp", "gSpd",
"kb", "weakenEnemy", "slowEnemy",
];
let build_detailed_display_commands = [
@ -320,24 +286,19 @@ let build_detailed_display_commands = [
"str", "dex", "int", "def", "agi",
"!spacer",
"mr", "ms",
"hprRaw", "hprPct", "healPct",
"hprRaw", "hprPct",
"ls",
"sdRaw", "nSdRaw", "rSdRaw",
"sdPct", "nSdPct", "rSdPct",
"mdRaw", "nMdRaw", "rMdRaw",
"mdPct", "nMdPct", "rMdPct",
"damRaw", "nDamRaw", "rDamRaw",
"damPct", "nDamPct", "rDamPct",
"!elemental",
"fSdRaw", "wSdRaw", "aSdRaw", "tSdRaw", "eSdRaw",
"fSdPct", "wSdPct", "aSdPct", "tSdPct", "eSdPct",
"fMdRaw", "wMdRaw", "aMdRaw", "tMdRaw", "eMdRaw",
"fMdPct", "wMdPct", "aMdPct", "tMdPct", "eMdPct",
"fDamRaw", "wDamRaw", "aDamRaw", "tDamRaw", "eDamRaw",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
"!elemental",
"rDefPct",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"atkTier",
"poison",
@ -351,7 +312,6 @@ let build_detailed_display_commands = [
"spRegen",
"eSteal",
"gXp", "gSpd",
"kb", "weakenEnemy", "slowEnemy",
];
// full
@ -408,7 +368,11 @@ let sq2_item_display_commands = [
"!spacer",
"str", "dex", "int", "def", "agi",
"hpBonus",
"hprRaw", "hprPct", "healPct",
"hprRaw", "hprPct",
"sdRaw", "nSdRaw", "eSdRaw", "tSdRaw", "wSdRaw", "fSdRaw", "aSdRaw", "rSdRaw",
"sdPct", "nSdPct", "eSdPct", "tSdPct", "wSdPct", "fSdPct", "aSdPct", "rSdPct",
"mdRaw", "nMdRaw", "eMdRaw", "tMdRaw", "wMdRaw", "fMdRaw", "aMdRaw", "rMdRaw",
"mdPct", "nMdPct", "eMdPct", "tMdPct", "wMdPct", "fMdPct", "aMdPct", "rMdPct",
"mr", "ms",
"ref", "thorns",
"ls",
@ -416,23 +380,10 @@ let sq2_item_display_commands = [
"expd",
"spd",
"atkTier",
"sdRaw", "nSdRaw", "rSdRaw",
"sdPct", "nSdPct", "rSdPct",
"mdRaw", "nMdRaw", "rMdRaw",
"mdPct", "nMdPct", "rMdPct",
"damRaw", "nDamRaw", "rDamRaw",
"damPct", "nDamPct", "rDamPct",
"!elemental",
"fSdRaw", "wSdRaw", "aSdRaw", "tSdRaw", "eSdRaw",
"fSdPct", "wSdPct", "aSdPct", "tSdPct", "eSdPct",
"fMdRaw", "wMdRaw", "aMdRaw", "tMdRaw", "eMdRaw",
"fMdPct", "wMdPct", "aMdPct", "tMdPct", "eMdPct",
"fDamRaw", "wDamRaw", "aDamRaw", "tDamRaw", "eDamRaw",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
"!elemental",
"rDefPct",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"sprint", "sprintReg",
"jh",
@ -440,7 +391,6 @@ let sq2_item_display_commands = [
"spRegen",
"eSteal",
"gXp", "gSpd",
"kb", "weakenEnemy", "slowEnemy",
"majorIds",
"!spacer",
"slots",
@ -448,7 +398,7 @@ let sq2_item_display_commands = [
"set",
"lore",
"quest",
"restrict",
"restrict"
];
let sq2_ing_display_order = [

View file

@ -50,14 +50,6 @@ const ExprParser = (function() {
case '=':
pushSymbol(exprStr[col]);
continue;
case 'or':
tokens.push({ type: '|' });
col += 2;
continue;
case 'and':
tokens.push({ type: '&' });
col += 2;
continue;
case '>':
pushSymbol(exprStr[col + 1] === '=' ? '>=' : '>');
continue;

View file

@ -1,209 +0,0 @@
// commented out filters
// "Name": "name",
// "Display Name": "displayName",
// "Tier": "stars",
// "Powder Slots": "slots",
// "Health": "hp",
// "Raw Fire Defense": "fDef",
// "Raw Water Defense": "wDef",
//"Raw Air Defense": "aDef",
// "Raw Thunder Defense": "tDef",
// "Raw Earth Defense": "eDef",
const translate_mappings = {
"Durability": "durability",
"Duration": "duration",
"Charges": "charges",
"Effectiveness Left": "left",
"Effectiveness Right": "right",
"Effectiveness Above": "above",
"Effectiveness Under": "under",
"Effectiveness Touching": "touching",
"Effectiveness Not Touching": "nottouching",
"Combat Level": "lvl",
"Req Strength": "strReq",
"Req Dexterity": "dexReq",
"Req Intelligence": "intReq",
"Req Agility": "agiReq",
"Req Defense": "defReq",
"% Health Regen": "hprPct",
"Mana Regen": "mr",
"Life Steal": "ls",
"Mana Steal": "ms",
"XP Bonus": "xpb",
"Loot Bonus": "lb",
"Reflection": "ref",
"Strength": "str",
"Dexterity": "dex",
"Intelligence": "int",
"Agility": "agi",
"Defense": "def",
"Thorns": "thorns",
"Exploding": "expd",
"Walk Speed": "spd",
"Attack Speed Bonus": "atkTier",
"Poison": "poison",
"Health Bonus": "hpBonus",
"Soul Point Regen": "spRegen",
"Stealing": "eSteal",
"Raw Health Regen": "hprRaw",
"Spell Damage Raw": "sdRaw",
"Elem. Spell Damage Raw": "rSdRaw",
"Neut. Spell Damage Raw": "nSdRaw",
"Earth Spell Damage Raw": "eSdRaw",
"Thunder Spell Damage Raw": "tSdRaw",
"Water Spell Damage Raw": "wSdRaw",
"Fire Spell Damage Raw": "fSdRaw",
"Air Spell Damage Raw": "aSdRaw",
"Spell Damage %": "sdPct",
"Elem. Spell Damage %": "rSdPct",
"Neut. Spell Damage %": "nSdPct",
"Earth Spell Damage %": "eSdPct",
"Thunder Spell Damage %": "tSdPct",
"Water Spell Damage %": "wSdPct",
"Fire Spell Damage %": "fSdPct",
"Air Spell Damage %": "aSdPct",
"Melee Damage Raw": "mdRaw",
"Elem. Melee Damage Raw": "rMdRaw",
"Neut. Melee Damage Raw": "nMdRaw",
"Earth Melee Damage Raw": "eMdRaw",
"Thunder Melee Damage Raw": "tMdRaw",
"Water Melee Damage Raw": "wMdRaw",
"Fire Melee Damage Raw": "fMdRaw",
"Air Melee Damage Raw": "aMdRaw",
"Melee Damage %": "mdPct",
"Elem. Melee Damage %": "rMdPct",
"Neut. Melee Damage %": "nMdPct",
"Earth Melee Damage %": "eMdPct",
"Thunder Melee Damage %": "tMdPct",
"Water Melee Damage %": "wMdPct",
"Fire Melee Damage %": "fMdPct",
"Air Melee Damage %": "aMdPct",
"Damage Raw": "damRaw",
"Elemental Damage Raw": "rDamRaw",
"Neutral Damage Raw": "nDamRaw",
"Earth Damage Raw": "eDamRaw",
"Thunder Damage Raw": "tDamRaw",
"Water Damage Raw": "wDamRaw",
"Fire Damage Raw": "fDamRaw",
"Air Damage Raw": "aDamRaw",
"Damage %": "damPct",
"Elemental Damage %": "rDamPct",
"Neutral Damage %": "nDamPct",
"Earth Damage %": "eDamPct",
"Thunder Damage %": "tDamPct",
"Water Damage %": "wDamPct",
"Fire Damage %": "fDamPct",
"Air Damage %": "aDamPct",
"% Fire Defense": "fDefPct",
"% Water Defense": "wDefPct",
"% Air Defense": "aDefPct",
"% Thunder Defense": "tDefPct",
"% Earth Defense": "eDefPct",
"% Elemental Defense": "rDefPct",
"1st Spell Cost %": "-spPct1",
"1st Spell Cost Raw": "-spRaw1",
"2nd Spell Cost %": "-spPct2",
"2nd Spell Cost Raw": "-spRaw2",
"3rd Spell Cost %": "-spPct3",
"3rd Spell Cost Raw": "-spRaw3",
"4th Spell Cost %": "-spPct4",
"4th Spell Cost Raw": "-spRaw4",
"Rainbow Spell Damage Raw": "rainbowRaw",
"Sprint": "sprint",
"Sprint Regen": "sprintReg",
"Jump Height": "jh",
"Loot Quality": "lq",
"Gather XP Bonus": "gXp",
"Gather Speed Bonus": "gSpd",
"Healing Efficiency": "healPct",
"Knockback": "kb",
"Weaken Enemy": "weakenEnemy",
"Slow Enemy": "slowEnemy"
};
const special_mappings = {
"Sum (skill points)": "str+dex+int+def+agi",
"Sum (Mana Sustain)": "mr+ms",
"Sum (Life Sustain)": "hpr+ls",
"Sum (Effectiveness)": "7/3 * touching + 8/3 * nottouching + 2/3 * (top + bottom) + 1/2 * (left + right)"
};
for (let x in translate_mappings) {
item_filters.push(x);
}
for (let x in special_mappings) {
item_filters.push(x);
}
types = {armouring: false, tailoring: false, weaponsmithing: false, woodworking: false, jeweling: false, cooking: false, alchemism: false, scribing: false};
search_tiers = {zero: true, one: true, two: true, three: true};
function display(ing_copy) {
let ing_parent = document.getElementById("search-results");
for (let i in ing_copy) {
if (i > 200) {break;}
let ing = ing_copy[i].itemExp;
let box = make_elem('div', ['ing-stats', 'col-lg-3', 'p-2', 'col-sm-6'], {id: 'ing'+i});
let bckgrdbox = make_elem('div', ["rounded", "g-0", "dark-7", "border", "border-dark", "dark-shadow", "p-3", "col-auto"], {id: 'ing'+i+'b'});
box.append(bckgrdbox);
ing_parent.appendChild(box);
displayExpandedIngredient(ing, bckgrdbox.id, true);
}
}
function filter_types_tiers(queries) {
// type
let allTypes = true, noTypes = true;
let typeQuery = "f:("
for (const type of Object.keys(types)) {
if (types[type]) {
typeQuery += type + "|";
noTypes = false;
} else {
allTypes = false;
}
}
if (noTypes) {
document.getElementById("summary").innerHTML = "Error: Cannot search without at least 1 type selected";
return false;
} else if (!allTypes) {
queries.push(typeQuery.substring(0, typeQuery.length - 1) + ")");
}
// stars
let allStars = true, noStars = true;
let starQuery = "f:("
for (const star of Object.keys(search_tiers)) {
if (search_tiers[star]) {
starQuery += "starsname=\"" + star + "\"|";
noStars = false;
} else {
allStars = false;
}
}
if (noStars) {
document.getElementById("summary").innerHTML = "Error: Cannot search without at least 1 star selected";
return false;
} else if (!allStars) {
queries.push(starQuery.substring(0, starQuery.length - 1) + ")");
}
return true;
}
function init_values() {
search_db = ings.filter( i => ! i.remapID ).map( i => [i, expandIngredient(i, [])] );
expr_parser = new ExprParser(ingredientQueryProps, queryFuncs);
}
(async function() {
await Promise.resolve(load_ing_init());
init_search();
})();

View file

@ -1,54 +0,0 @@
const getQueryIdentifiers = (function() {
let identCache = null;
return function() {
if (identCache === null) {
const idents = new Set();
for (const ident of Object.keys(ingredientQueryProps)) {
idents.add(ident);
}
for (const ident of Object.keys(queryFuncs)) {
idents.add(ident);
}
identCache = [...idents].sort(); // might use a trie optimally, but the set is probably small enough...
}
return identCache;
};
})();
function generateEntries(size, itemList, itemEntries) {
for (let i = 0; i < size; i++) {
const itemElem = document.createElement('div');
itemElem.classList.add('col-lg-3', 'col-sm-6', "p-2", "ing-stats");
// itemElem.setAttribute('id', `item-entry-${i}`);
itemList.append(itemElem);
itemEntries.push(itemElem);
const itemElemContained = document.createElement("div");
itemElemContained.classList.add("dark-7", "rounded", "p-3", "col-auto", "g-0", "border", "border-dark", "dark-shadow");
itemElemContained.setAttribute('id', `item-entry-${i}`);
itemElem.appendChild(itemElemContained);
const sortKeyListContainer = document.createElement('div');
sortKeyListContainer.classList.add('row');
sortKeyListContainer.setAttribute('id', `item-sort-entry-${i}`);
itemEntries[i].append(sortKeyListContainer);
}
}
function init_values() {
// compile the search db from the item db
searchDb = ings.filter(i => !i.remapID).map(i => [i, expandIngredient(i)]);
// create the expression parser
exprParser = new ExprParser(ingredientQueryProps, queryFuncs);
}
function display(itemExp, id) {
displayExpandedIngredient(itemExp, id);
}
(async function() {
await Promise.resolve(load_ing_init());
init_items_adv();
})();

View file

@ -60,8 +60,7 @@ function toggleAmps(button_id) {
(async function() {
let latest_ver_name = wynn_version_names[WYNN_VERSION_LATEST];
let load_promises = [ load_init(), load_major_id_data(latest_ver_name) ];
let load_promises = [ load_init() ];
await Promise.all(load_promises);
init_itempage();
})();

View file

@ -1,414 +0,0 @@
/*
* File for display commands specific to the single item page.
*/
/** Displays the ID costs of an item
*
* @param {String} elemID - the id of the parent element.
* @param {Map} item - the statMap of an item.
*/
function displayIDCosts(elemID, item) {
let parent_elem = document.getElementById(elemID);
let tier = item.get("tier");
if ( (item.has("fixID") && item.get("fixID")) || ["Normal","Crafted","Custom","none", " ",].includes(item.get("tier"))) {
return;
} else {
/** Returns the number of inventory slots minimum an amount of emeralds would take up + the configuration of doing so.
* Returns an array of [invSpace, E, EB, LE, Stx LE]
*
* @param {number} ems - the total numerical value of emeralds to compact.
*/
function emsToInvSpace(ems) {
let stx = Math.floor(ems/262144);
ems -= stx*4096*64;
let LE = Math.floor(ems/4096);
ems -= LE*4096;
let EB = Math.floor(ems/64);
ems -= EB*64;
let e = ems;
return [ stx + Math.ceil(LE/64) + Math.ceil(EB/64) + Math.ceil(e/64) , e, EB, LE, stx];
}
/**
*
* @param {String} tier - item tier
* @param {Number} lvl - item level
*/
function getIDCost(tier, lvl) {
switch (tier) {
case "Unique":
return Math.round(0.5*lvl + 3);
case "Rare":
return Math.round(1.2*lvl + 8);
case "Legendary":
return Math.round(4.5*lvl + 12);
case "Fabled":
return Math.round(12*lvl + 26);
case "Mythic":
return Math.round(18*lvl + 90);
case "Set":
return Math.round(1.5*lvl + 8)
default:
return -1;
}
}
parent_elem.style = "display: visible";
let lvl = item.get("lvl");
if (typeof(lvl) === "string") { lvl = parseFloat(lvl); }
let title_elem = document.createElement("p");
title_elem.classList.add("smalltitle");
title_elem.style.color = "white";
title_elem.textContent = "Identification Costs";
parent_elem.appendChild(title_elem);
parent_elem.appendChild(document.createElement("br"));
let grid_item = document.createElement("div");
grid_item.style.display = "flex";
grid_item.style.flexDirection = "rows";
grid_item.style.flexWrap = "wrap";
grid_item.style.gap = "5px";
parent_elem.appendChild(grid_item);
let IDcost = getIDCost(tier, lvl);
let initIDcost = IDcost;
let invSpace = emsToInvSpace(IDcost);
let rerolls = 0;
while(invSpace[0] <= 28 && IDcost > 0) {
let container = document.createElement("div");
container.classList.add("container");
container.style = "grid-item-" + (rerolls+1);
container.style.maxWidth = "max(120px, 15%)";
let container_title = document.createElement("p");
container_title.style.color = "white";
if (rerolls == 0) {
container_title.textContent = "Initial ID Cost: ";
} else {
container_title.textContent = "Reroll to [" + (rerolls+1) + "] Cost:";
}
container.appendChild(container_title);
let total_cost_container = document.createElement("p");
let total_cost_number = document.createElement("b");
total_cost_number.classList.add("Set");
total_cost_number.textContent = IDcost + " ";
let total_cost_suffix = document.createElement("b");
total_cost_suffix.textContent = "emeralds."
total_cost_container.appendChild(total_cost_number);
total_cost_container.appendChild(total_cost_suffix);
container.appendChild(total_cost_container);
let OR = document.createElement("p");
OR.classList.add("center");
OR.textContent = "OR";
container.appendChild(OR);
let esuffixes = ["", "emeralds.", "EB.", "LE.", "stacks of LE."];
for (let i = 4; i > 0; i--) {
let n_container = document.createElement("p");
let n_number = document.createElement("b");
n_number.classList.add("Set");
n_number.textContent = invSpace[i] + " ";
let n_suffix = document.createElement("b");
n_suffix.textContent = esuffixes[i];
n_container.appendChild(n_number);
n_container.appendChild(n_suffix);
container.appendChild(n_container);
}
grid_item.appendChild(container);
rerolls += 1;
IDcost = Math.round(initIDcost * (5 ** rerolls));
invSpace = emsToInvSpace(IDcost);
}
}
}
/** Displays Additional Info for
*
* @param {String} elemID - the parent element's id
* @param {Map} item - the statMap of the item
* @returns
*/
function displayAdditionalInfo(elemID, item) {
let parent_elem = document.getElementById(elemID);
parent_elem.classList.add("left");
let droptype_elem = document.createElement("div");
droptype_elem.classList.add("container");
droptype_elem.style.marginBottom = "5px";
droptype_elem.textContent = "Drop type: " + (item.has("drop") ? item.get("drop"): "NEVER");
parent_elem.appendChild(droptype_elem);
let warning_elem = document.createElement("div");
warning_elem.classList.add("container");
warning_elem.style.marginBottom ="5px";
warning_elem.textContent = "This page is incomplete. Will work on it later.";
parent_elem.appendChild(warning_elem);
return;
}
/** Displays the individual probabilities of each possible value of each rollable ID for this item.
*
* @param {String} parent_id the document id of the parent element
* @param {String} item expandedItem object
* @param {String} amp the level of corkian amplifier used. 0 means no amp, 1 means Corkian Amplifier I, etc. [0,3]
*/
function displayIDProbabilities(parent_id, item, amp) {
if (item.has("fixID") && item.get("fixID")) {return}
let parent_elem = document.getElementById(parent_id);
parent_elem.style.display = "";
parent_elem.innerHTML = "";
let title_elem = document.createElement("p");
title_elem.textContent = "Identification Probabilities";
title_elem.id = "ID_PROB_TITLE";
title_elem.classList.add("Legendary");
title_elem.classList.add("title");
parent_elem.appendChild(title_elem);
let disclaimer_elem = document.createElement("p");
disclaimer_elem.textContent = "IDs are rolled on a uniform distribution. A chance of 0% means that either the minimum or maximum possible multiplier must be rolled to get this value."
parent_elem.appendChild(disclaimer_elem);
let amp_row = document.createElement("p");
amp_row.id = "amp_row";
let amp_text = document.createElement("b");
amp_text.textContent = "Corkian Amplifier Used: "
amp_row.appendChild(amp_text);
let amp_1 = document.createElement("button");
amp_1.id = "cork_amp_1";
amp_1.textContent = "I";
amp_row.appendChild(amp_1);
let amp_2 = document.createElement("button");
amp_2.id = "cork_amp_2";
amp_2.textContent = "II";
amp_row.appendChild(amp_2);
let amp_3 = document.createElement("button");
amp_3.id = "cork_amp_3";
amp_3.textContent = "III";
amp_row.appendChild(amp_3);
amp_1.addEventListener("click", (event) => {toggleAmps(1)});
amp_2.addEventListener("click", (event) => {toggleAmps(2)});
amp_3.addEventListener("click", (event) => {toggleAmps(3)});
parent_elem.appendChild(amp_row);
if (amp != 0) {toggleButton("cork_amp_" + amp)}
let item_name = item.get("displayName");
console.log(itemMap.get(item_name))
let table_elem = document.createElement("table");
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
if (val > 0) {
let base = itemMap.get(item_name)[id];
if (reversedIDs.includes(id)) {max = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)}
else {min = Math.max( Math.round((0.3 + 0.05*amp) * base), 1)}
}
let row_title = document.createElement("tr");
//row_title.style.textAlign = "left";
let title_left = document.createElement("td");
let left_elem = document.createElement("p");
let left_val_title = document.createElement("b");
let left_val_elem = document.createElement("b");
title_left.style.textAlign = "left";
left_val_title.textContent = idPrefixes[id] + "Base ";
left_val_elem.textContent = val + idSuffixes[id];
if (val > 0 == !reversedIDs.includes(id)) {
left_val_elem.classList.add("positive");
} else if (val > 0 == reversedIDs.includes(id)) {
left_val_elem.classList.add("negative");
}
left_elem.appendChild(left_val_title);
left_elem.appendChild(left_val_elem);
title_left.appendChild(left_elem);
row_title.appendChild(title_left);
let title_right = document.createElement("td");
let title_right_text = document.createElement("b");
title_right.style.textAlign = "left";
title_right_text.textContent = "[ " + min + idSuffixes[id] + ", " + max + idSuffixes[id] + " ]";
if ( (min > 0 && max > 0 && !reversedIDs.includes(id)) || (min < 0 && max < 0 && reversedIDs.includes(id)) ) {
title_right_text.classList.add("positive");
} else if ( (min < 0 && max < 0 && !reversedIDs.includes(id)) || (min > 0 && max > 0 && reversedIDs.includes(id)) ) {
title_right_text.classList.add("negative");
}
title_right.appendChild(title_right_text);
let title_input = document.createElement("td");
let title_input_slider = document.createElement("input");
title_input_slider.type = "range";
title_input_slider.id = id+"-slider";
if (!reversedIDs.includes(id)) {
title_input_slider.step = 1;
title_input_slider.min = `${min}`;
title_input_slider.max = `${max}`;
title_input_slider.value = `${max}`;
} else {
title_input_slider.step = 1;
title_input_slider.min = `${-1*min}`;
title_input_slider.max = `${-1*max}`;
title_input_slider.value = `${-1*max}`;
}
let title_input_textbox = document.createElement("input");
title_input_textbox.type = "text";
title_input_textbox.value = `${max}`;
title_input_textbox.id = id+"-textbox";
title_input_textbox.classList.add("small-input");
title_input.appendChild(title_input_slider);
title_input.appendChild(title_input_textbox);
row_title.appendChild(title_left);
row_title.appendChild(title_right);
row_title.appendChild(title_input);
let row_chances = document.createElement("tr");
let chance_cdf = document.createElement("td");
let chance_pdf = document.createElement("td");
let cdf_p = document.createElement("p");
cdf_p.id = id+"-cdf";
let pdf_p = document.createElement("p");
pdf_p.id = id+"-pdf";
chance_cdf.appendChild(cdf_p);
chance_pdf.appendChild(pdf_p);
row_chances.appendChild(chance_cdf);
row_chances.appendChild(chance_pdf);
table_elem.appendChild(row_title);
table_elem.appendChild(row_chances);
stringPDF(id, max, val, amp); //val is base roll
stringCDF(id, max, val, amp); //val is base roll
title_input_slider.addEventListener("change", (event) => {
let id_name = event.target.id.split("-")[0];
let textbox_elem = document.getElementById(id_name+"-textbox");
if (reversedIDs.includes(id_name)) {
if (event.target.value < -1*min) { event.target.value = -1*min}
if (event.target.value > -1*max) { event.target.value = -1*max}
stringPDF(id_name, -1*event.target.value, val, amp); //val is base roll
stringCDF(id_name, -1*event.target.value, val, amp); //val is base roll
} else {
if (event.target.value < min) { event.target.value = min}
if (event.target.value > max) { event.target.value = max}
stringPDF(id_name, 1*event.target.value, val, amp); //val is base roll
stringCDF(id_name, 1*event.target.value, val, amp); //val is base roll
}
if (textbox_elem && textbox_elem.value !== event.target.value) {
if (reversedIDs.includes(id_name)) {
textbox_elem.value = -event.target.value;
} else {
textbox_elem.value = event.target.value;
}
}
});
title_input_textbox.addEventListener("change", (event) => {
let id_name = event.target.id.split("-")[0];
if (reversedIDs.includes(id_name)) {
if (event.target.value > min) { event.target.value = min}
if (event.target.value < max) { event.target.value = max}
} else {
if (event.target.value < min) { event.target.value = min}
if (event.target.value > max) { event.target.value = max}
}
let slider_elem = document.getElementById(id_name+"-slider");
if (slider_elem.value !== event.target.value) {
slider_elem.value = -event.target.value;
}
stringPDF(id_name, 1*event.target.value, val, amp);
stringCDF(id_name, 1*event.target.value, val, amp);
});
}
}
}
//helper functions. id - the string of the id's name, val - the value of the id, base - the base value of the item for this id
function stringPDF(id,val,base,amp) {
/** [0.3b,1.3b] positive normal
* [1.3b,0.3b] positive reversed
* [1.3b,0.7b] negative normal
* [0.7b,1.3b] negative reversed
*
* [0.3, 1.3] minr, maxr [0.3b, 1.3b] min, max
* the minr/maxr decimal roll that corresponds to val -> minround, maxround
*/
let p; let min; let max; let minr; let maxr; let minround; let maxround;
if (base > 0) {
minr = 0.3 + 0.05*amp; maxr = 1.3;
min = Math.max(1, Math.round(minr*base)); max = Math.max(1, Math.round(maxr*base));
minround = (min == max) ? (minr) : ( Math.max(minr, (val-0.5) / base) );
maxround = (min == max) ? (maxr) : ( Math.min(maxr, (val+0.5) / base) );
} else {
minr = 1.3; maxr = 0.7;
min = Math.min(-1, Math.round(minr*base)); max = Math.min(-1, Math.round(maxr*base));
minround = (min == max) ? (minr) : ( Math.min(minr, (val-0.5) / base) );
maxround = (min == max) ? (maxr) : ( Math.max(maxr, (val+0.5) / base) );
}
p = Math.abs(maxround-minround)/Math.abs(maxr-minr)*100;
p = p.toFixed(3);
let b1 = document.createElement("b");
b1.textContent = "Roll exactly ";
let b2 = document.createElement("b");
b2.textContent = val + idSuffixes[id];
if (val > 0 == !reversedIDs.includes(id)) {b2.classList.add("positive")}
if (val > 0 == reversedIDs.includes(id)) {b2.classList.add("negative")}
let b3 = document.createElement("b");
b3.textContent = ": " + p + "%";
document.getElementById(id + "-pdf").innerHTML = "";
document.getElementById(id + "-pdf").appendChild(b1);
document.getElementById(id + "-pdf").appendChild(b2);
document.getElementById(id + "-pdf").appendChild(b3);
}
function stringCDF(id,val,base,amp) {
let p; let min; let max; let minr; let maxr; let minround; let maxround;
if (base > 0) {
minr = 0.3 + 0.05*amp; maxr = 1.3;
min = Math.max(1, Math.round(minr*base)); max = Math.max(1, Math.round(maxr*base));
minround = (min == max) ? (minr) : ( Math.max(minr, (val-0.5) / base) );
maxround = (min == max) ? (maxr) : ( Math.min(maxr, (val+0.5) / base) );
} else {
minr = 1.3; maxr = 0.7;
min = Math.min(-1, Math.round(minr*base)); max = Math.min(-1, Math.round(maxr*base));
minround = (min == max) ? (minr) : ( Math.min(minr, (val-0.5) / base) );
maxround = (min == max) ? (maxr) : ( Math.max(maxr, (val+0.5) / base) );
}
if (reversedIDs.includes(id)) {
p = Math.abs(minr-maxround)/Math.abs(maxr-minr)*100;
} else {
p = Math.abs(maxr-minround)/Math.abs(maxr-minr)*100;
}
p = p.toFixed(3);
let b1 = document.createElement("b");
b1.textContent = "Roll ";
let b2 = document.createElement("b");
b2.textContent = val + idSuffixes[id];
if (val > 0 == !reversedIDs.includes(id)) {b2.classList.add("positive")}
if (val > 0 == reversedIDs.includes(id)) {b2.classList.add("negative")}
let b3 = document.createElement("b");
b3.textContent= " or better: " + p + "%";
document.getElementById(id + "-cdf").innerHTML = "";
document.getElementById(id + "-cdf").appendChild(b1);
document.getElementById(id + "-cdf").appendChild(b2);
document.getElementById(id + "-cdf").appendChild(b3);
}

View file

@ -56,62 +56,18 @@ const translate_mappings = {
"Soul Point Regen": "spRegen",
"Stealing": "eSteal",
"Raw Health Regen": "hprRaw",
"Spell Damage Raw": "sdRaw",
"Elem. Spell Damage Raw": "rSdRaw",
"Neut. Spell Damage Raw": "nSdRaw",
"Earth Spell Damage Raw": "eSdRaw",
"Thunder Spell Damage Raw": "tSdRaw",
"Water Spell Damage Raw": "wSdRaw",
"Fire Spell Damage Raw": "fSdRaw",
"Air Spell Damage Raw": "aSdRaw",
"Spell Damage %": "sdPct",
"Elem. Spell Damage %": "rSdPct",
"Neut. Spell Damage %": "nSdPct",
"Earth Spell Damage %": "eSdPct",
"Thunder Spell Damage %": "tSdPct",
"Water Spell Damage %": "wSdPct",
"Fire Spell Damage %": "fSdPct",
"Air Spell Damage %": "aSdPct",
"Melee Damage Raw": "mdRaw",
"Elem. Melee Damage Raw": "rMdRaw",
"Neut. Melee Damage Raw": "nMdRaw",
"Earth Melee Damage Raw": "eMdRaw",
"Thunder Melee Damage Raw": "tMdRaw",
"Water Melee Damage Raw": "wMdRaw",
"Fire Melee Damage Raw": "fMdRaw",
"Air Melee Damage Raw": "aMdRaw",
"Melee Damage %": "mdPct",
"Elem. Melee Damage %": "rMdPct",
"Neut. Melee Damage %": "nMdPct",
"Earth Melee Damage %": "eMdPct",
"Thunder Melee Damage %": "tMdPct",
"Water Melee Damage %": "wMdPct",
"Fire Melee Damage %": "fMdPct",
"Air Melee Damage %": "aMdPct",
"Damage Raw": "damRaw",
"Elemental Damage Raw": "rDamRaw",
"Neutral Damage Raw": "nDamRaw",
"Earth Damage Raw": "eDamRaw",
"Thunder Damage Raw": "tDamRaw",
"Water Damage Raw": "wDamRaw",
"Fire Damage Raw": "fDamRaw",
"Air Damage Raw": "aDamRaw",
"Damage %": "damPct",
"Elemental Damage %": "rDamPct",
"Neutral Damage %": "nDamPct",
"Earth Damage %": "eDamPct",
"Thunder Damage %": "tDamPct",
"Water Damage %": "wDamPct",
"Fire Damage %": "fDamPct",
"Air Damage %": "aDamPct",
"Raw Spell": "sdRaw",
"Raw Melee": "mdRaw",
"% Fire Damage": "fDamPct",
"% Water Damage": "wDamPct",
"% Air Damage": "aDamPct",
"% Thunder Damage": "tDamPct",
"% Earth Damage": "eDamPct",
"% Fire Defense": "fDefPct",
"% Water Defense": "wDefPct",
"% Air Defense": "aDefPct",
"% Thunder Defense": "tDefPct",
"% Earth Defense": "eDefPct",
"% Elemental Defense": "rDefPct",
"1st Spell Cost %": "-spPct1",
"1st Spell Cost Raw": "-spRaw1",
"2nd Spell Cost %": "-spPct2",
@ -120,16 +76,13 @@ const translate_mappings = {
"3rd Spell Cost Raw": "-spRaw3",
"4th Spell Cost %": "-spPct4",
"4th Spell Cost Raw": "-spRaw4",
"Rainbow Spell Damage Raw": "rainbowRaw",
"Sprint": "sprint",
"Sprint Regen": "sprintReg",
"Jump Height": "jh",
"Loot Quality": "lq",
"Gather XP Bonus": "gXp",
"Gather Speed Bonus": "gSpd",
"Healing Efficiency": "healPct",
"Knockback": "kb",
"Weaken Enemy": "weakenEnemy",
"Slow Enemy": "slowEnemy"
"Gather Speed Bonus": "gSpd"
};
const special_mappings = {
@ -140,6 +93,7 @@ const special_mappings = {
"Base DPS": "(nDam+fDam+wDam+aDam+tDam+eDam) * atkspdmod(atkspd)"
};
let item_filters = [];
for (let x in translate_mappings) {
item_filters.push(x);
}
@ -147,17 +101,18 @@ for (let x in special_mappings) {
item_filters.push(x);
}
types = {bow: false, spear: false, wand: false, dagger: false, relik: false, helmet: false, chestplate: false, leggings: false, boots: false, ring: false, bracelet: false, necklace: false};
search_tiers = {normal: true, unique: true, set: true, rare: true, legendary: true, fabled: true, mythic: true};
let item_categories = ["armor", "accessory", "weapon"];
function display(items_copy) {
const types = {bow: false, spear: false, wand: false, dagger: false, relik: false, helmet: false, chestplate: false, leggings: false, boots: false, ring: false, bracelet: false, necklace: false};
const rarities = {normal: true, unique: true, set: true, rare: true, legendary: true, fabled: true, mythic: true};
const filters = [], excludes = [];
let filter_id_counter = 0;
function displayItems(items_copy) {
let items_parent = document.getElementById("search-results");
for (let i in items_copy) {
if (i > 200) {break;}
let item = items_copy[i].itemExp;
let box = make_elem('div', ['col-lg-3', 'col-sm-6', 'p-2'], {id: 'item'+i});
let bckgrdbox = make_elem("div", ["dark-7", "rounded", "px-2", "col-auto"], {id: 'item'+i+'b'});
@ -171,7 +126,19 @@ function display(items_copy) {
}
}
function filter_types_tiers(queries) {
let search_db;
let expr_parser;
function do_item_search() {
document.getElementById("summary").style.color = "red"; // to display errors, changed to white if search successful
window.scrollTo(0, 0);
let queries = [];
// name
if (document.getElementById("item-name-choice").value != "") {
queries.push("f:name?=\"" + document.getElementById("item-name-choice").value.trim() + "\"");
}
// types
let allTypes = true, noTypes = true;
let typeQuery = "f:("
@ -185,7 +152,7 @@ function filter_types_tiers(queries) {
}
if (noTypes) {
document.getElementById("summary").innerHTML = "Error: Cannot search without at least 1 type selected";
return false;
return;
} else if (!allTypes) {
queries.push(typeQuery.substring(0, typeQuery.length - 1) + ")");
}
@ -193,8 +160,8 @@ function filter_types_tiers(queries) {
// rarities
let allRarities = true, noRarities = true;
let rarityQuery = "f:("
for (const rarity of Object.keys(search_tiers)) {
if (search_tiers[rarity]) {
for (const rarity of Object.keys(rarities)) {
if (rarities[rarity]) {
rarityQuery += "tiername=\"" + rarity + "\"|";
noRarities = false;
} else {
@ -203,20 +170,353 @@ function filter_types_tiers(queries) {
}
if (noRarities) {
document.getElementById("summary").innerHTML = "Error: Cannot search without at least 1 rarity selected";
return false;
return;
} else if (!allRarities) {
queries.push(rarityQuery.substring(0, rarityQuery.length - 1) + ")");
}
// filters
for (const filter of filters) {
let min = parseInt(filter.min_elem.value);
let max = parseInt(filter.max_elem.value);
if (min > max) {
document.getElementById("summary").innerHTML = "Error: The minimum of filter " + filter.input_elem.value + " (" + min + ") is greater than its maximum (" + max + ")";
return;
}
let zero_in_min_max = (isNaN(min) || min < 0) && (isNaN(max) || max > 0);
return true;
let raw_name = filter.input_elem.value;
if (raw_name == "") {
continue; // empty
}
let filter_name = translate_mappings[raw_name];
if (filter_name === undefined) {
filter_name = special_mappings[raw_name];
if (filter_name === undefined) {
document.getElementById("summary").innerHTML = "Error: The filter \"" + filter.input_elem.value + "\" is not recognized";
return;
}
filter_name = "(" + filter_name + ")";
}
if (!isNaN(min)) {
queries.push("f:" + filter_name + ">=" + min);
}
if (!isNaN(max)) {
queries.push("f:" + filter_name + "<=" + max);
}
if (zero_in_min_max) {
queries.push("f:" + filter_name + "!=0");
}
queries.push("s:" + (filter.ascending ? "0-" : "") + filter_name);
}
// excludes
for (const exclude of excludes) {
let raw_name = exclude.input_elem.value;
if (raw_name == "") {
continue; // empty
}
let filter_name = translate_mappings[raw_name];
if (filter_name === undefined) {
filter_name = special_mappings[raw_name];
if (filter_name === undefined) {
document.getElementById("summary").innerHTML = "Error: The excluded filter \"" + exclude.input_elem.value + "\" is not recognized";
return;
}
filter_name = "(" + filter_name + ")";
}
queries.push("f:" + filter_name + "=0");
}
let filter_query = "true";
let sort_queries = [];
console.log(queries);
for (const query of queries) {
if (query.startsWith("s:")) {
sort_queries.push(query.slice(2));
}
else if (query.startsWith("f:")) {
filter_query = filter_query + "&" + query.slice(2);
}
}
document.getElementById("search-results").textContent = "";
let results = [];
try {
const filter_expr = expr_parser.parse(filter_query);
const sort_exprs = sort_queries.map(q => expr_parser.parse(q));
for (let i = 0; i < search_db.length; ++i) {
const item = search_db[i][0];
const itemExp = search_db[i][1];
if (checkBool(filter_expr.resolve(item, itemExp))) {
results.push({ item, itemExp, sortKeys: sort_exprs.map(e => e.resolve(item, itemExp)) });
}
}
results.sort((a, b) => {
return compareLexico(a.item, a.sortKeys, b.item, b.sortKeys);
});
} catch (e) {
document.getElementById("summary").textContent = e.message;
return;
}
document.getElementById("summary").textContent = results.length + " results:";
document.getElementById("summary").style.color = "white";
displayItems(results);
}
function init_values() {
function init_items() {
search_db = items.filter( i => ! i.remapID ).map( i => [i, expandItem(i, [])] );
expr_parser = new ExprParser(itemQueryProps, queryFuncs);
expr_parser = new ExprParser(itemQueryProps, itemQueryFuncs);
// init type buttons
for (const type of Object.keys(types)) {
document.getElementById("type-" + type).addEventListener("click", function() {
types[type] = !types[type];
this.classList.toggle("type-selected");
});
}
document.getElementById("all-types").addEventListener("click", function() {
for (const type of Object.keys(types)) {
types[type] = true;
document.getElementById("type-" + type).classList.add("type-selected");
}
});
document.getElementById("none-types").addEventListener("click", function() {
for (const type of Object.keys(types)) {
types[type] = false;
document.getElementById("type-" + type).classList.remove("type-selected");
}
});
// init rarity buttons
for (const rarity of Object.keys(rarities)) {
document.getElementById("rarity-" + rarity).addEventListener("click", function() {
rarities[rarity] = !rarities[rarity];
this.classList.toggle("rarity-selected");
});
}
document.getElementById("all-rarities").addEventListener("click", function() {
for (const rarity of Object.keys(rarities)) {
rarities[rarity] = true;
document.getElementById("rarity-" + rarity).classList.add("rarity-selected");
}
});
document.getElementById("none-rarities").addEventListener("click", function() {
for (const rarity of Object.keys(rarities)) {
rarities[rarity] = false;
document.getElementById("rarity-" + rarity).classList.remove("rarity-selected");
}
});
// filters
document.getElementById("add-filter").addEventListener("click", create_filter);
document.getElementById("add-exclude").addEventListener("click", create_exclude);
create_filter();
filters[0].input_elem.value = "Combat Level";
init_filter_drag();
}
function reset_item_search() {
document.getElementById("item-name-choice").value = "";
document.getElementById("all-types").click();
document.getElementById("all-rarities").click();
}
function create_filter() {
let data = {ascending: false};
let row = make_elem("div", ["row", "filter-row"], {});
let col = make_elem("div", ["col"], {});
row.appendChild(col);
data.div = row;
let reorder_img = make_elem("img", ["reorder-filter"], {src: "../media/icons/3-lines.svg", draggable: "true"});
col.appendChild(reorder_img);
let filter_input = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "filter-input"],
{id: "filter-input-" + filter_id_counter, type: "text", placeholder: "Filter"}
);
filter_id_counter++;
col.appendChild(filter_input);
data.input_elem = filter_input;
let asc_desc = make_elem("div", [], {style: "cursor: pointer; display: inline-block;"});
asc_desc.appendChild(make_elem("img", ["desc-icon", "asc-sel"], {src: "../media/icons/triangle.svg"}));
asc_desc.appendChild(make_elem("img", ["asc-icon"], {src: "../media/icons/triangle.svg"}));
asc_desc.addEventListener("click", function() {
data.ascending = !data.ascending;
asc_desc.children[0].classList.toggle("asc-sel");
asc_desc.children[1].classList.toggle("asc-sel");
});
col.appendChild(asc_desc);
let min = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "min-max-input"],
{type: "number", placeholder: "-\u221E"}
);
col.appendChild(min);
data.min_elem = min;
let to = make_elem("span", [], {innerHTML: "&nbsp;to&nbsp;"});
col.appendChild(to);
let max = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "min-max-input"],
{type: "number", placeholder: "\u221E"}
);
col.appendChild(max);
data.max_elem = max;
let trash = make_elem("img", ["delete-filter"], {src: "../media/icons/trash.svg"});
trash.addEventListener("click", function() {
filters.splice(Array.from(row.parentElement.children).indexOf(row) - 1, 1);
row.remove();
});
col.appendChild(trash);
document.getElementById("filter-container").insertBefore(row, document.getElementById("add-filter").parentElement);
filters.push(data);
init_filter_dropdown(data);
}
let currently_dragging = null;
function init_filter_drag() {
let container = document.getElementById("filter-container");
container.addEventListener("dragstart", function(e) {
if (e.path[0].classList.contains("reorder-filter")) {
currently_dragging = filters[Array.from(e.path[3].children).indexOf(e.path[2]) - 1];
} else {
e.preventDefault();
}
});
container.addEventListener("dragenter", function(e) {
e.preventDefault();
});
container.addEventListener("dragleave", function(e) {
e.preventDefault();
});
container.addEventListener("dragend", function(e) {
e.preventDefault();
for (const el of document.getElementsByClassName("filter-dragged-over")) {
el.classList.remove("filter-dragged-over");
}
currently_dragging = null;
});
container.addEventListener("dragover", function(e) {
e.preventDefault();
for (const el of document.getElementsByClassName("filter-dragged-over")) {
el.classList.remove("filter-dragged-over");
}
if (!e.path.includes(currently_dragging.div)) {
for (let i = 0; i < e.path.length; i++) {
if (e.path[i].classList.contains("filter-row")) {
e.path[i].classList.add("filter-dragged-over");
break;
}
}
}
});
container.addEventListener("drop", function(e) {
e.preventDefault();
for (const el of document.getElementsByClassName("filter-dragged-over")) {
el.classList.remove("filter-dragged-over");
}
if (!e.path.includes(currently_dragging.div)) {
for (let i = 0; i < e.path.length; i++) {
if (e.path[i].classList.contains("filter-row")) {
let old_index = filters.indexOf(currently_dragging);
let new_index = Array.from(e.path[i + 1].children).indexOf(e.path[i]) - 1;
filters.splice(old_index, 1);
filters.splice(new_index, 0, currently_dragging);
currently_dragging.div.remove();
container.insertBefore(currently_dragging.div, container.children[new_index + 1]);
break;
}
}
}
currently_dragging = null;
});
}
function create_exclude() {
let data = {};
let row = make_elem("div", ["row", "filter-row"], {});
let col = make_elem("div", ["col"], {});
row.appendChild(col);
data.div = row;
let filter_input = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "filter-input"],
{id: "filter-input-" + filter_id_counter, type: "text", placeholder: "Excluded Filter"}
);
filter_id_counter++;
col.appendChild(filter_input);
data.input_elem = filter_input;
let trash = make_elem("img", ["delete-filter"], {src: "../media/icons/trash.svg"});
trash.addEventListener("click", function() {
excludes.splice(Array.from(row.parentElement.children).indexOf(row) - 1, 1);
row.remove();
});
col.appendChild(trash);
document.getElementById("exclude-container").insertBefore(row, document.getElementById("add-exclude").parentElement);
excludes.push(data);
init_filter_dropdown(data);
}
function init_filter_dropdown(filter) {
let field_choice = filter.input_elem;
field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));};
filter.autoComplete = new autoComplete({
data: {
src: item_filters,
},
threshold: 0,
selector: "#" + field_choice.id,
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 = field_choice.getBoundingClientRect();
list.style.top = position.bottom + window.scrollY +"px";
list.style.left = position.x+"px";
list.style.width = position.width+"px";
list.style.maxHeight = position.height * 4 +"px";
if (!data.results.length) {
const message = make_elem('li', ['scaled-font'], {textContent: "No results found!"});
list.prepend(message);
};
},
},
resultItem: {
class: "scaled-font search-item",
selected: "dark-5",
},
events: {
input: {
selection: (event) => {
if (event.detail.selection.value) {
event.target.value = event.detail.selection.value;
};
},
},
}
});
}
(async function() {
await Promise.resolve(load_init(), load_major_id_data(wynn_version_names[WYNN_VERSION_LATEST]));
init_search();
await Promise.resolve(load_init());
init_items();
})();

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