parent 3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354753 -0700 parent3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354749 -0700 parent3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354744 -0700 parent3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354739 -0700 parent3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354735 -0700 parent3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354730 -0700 parent3e725eded8
author hppeng <hppeng> 1699417872 -0800 committer hppeng <hppeng> 1720354688 -0700 Update recipes.json (#265) Change ratio of gems to oil as it has been updated in 2.0.4 > Updated the Jeweling Recipe Changes (Bracelet- 2:1 gems:oil, Necklaces- 3:1 gems:oil) https://forums.wynncraft.com/threads/2-0-4-full-changelog-new-bank-lootruns-more.310535/ Finish updating recipes.json why are there 4 versions of this file active at any given time Fix damage calculation for rainbow raw wow this bug has been here for a LONG time also bump version for ing db Bunch of bugfixes - new major ID - divine honor: reduce earth damage - radiance: don't boost tomes, xp/loot bonuses atree: - parry: minor typo - death magnet: marked dep - nightcloak knife: 15s desc Api v3 (#267) * Tweak ordering to be consistent internally * v3 items (#266) * item_wrapper script for updating item data with v3 endpoint * metadata from v3 * v3 item format For the purpose of wynnbuilder, additional mapping might be needed. * v3 item format additional mapping might be needed for wb * v3 compressed item json * clean item json v3 format * Update translate map to api v3 partially... we will need to redo scripts to flatmap all the items * Fix items for 2.0.4.3 finally * New ingredients (and parse script update) just realized I forgot to commit the parse script this whole time * Forgot to commit data files, and bump ing db version * Sketchily reverse translate major ids internalname and separate lookup table lol * Forgot to update data files todo: script should update all files at once * Bump wynn version number already outdated... * Forgot to update 2.0.4.3 major ids --------- Co-authored-by: hppeng <hppeng> Co-authored-by: RawFish69 <108964215+RawFish69@users.noreply.github.com> Add missing fields to ingreds missing ids and consumableIDs tags in some ingreds Fix missing properties in item search setup these should be unified maybe to avoid duplicated code Fix sacshrine dependency on fluid healing also: fix ": " in item searcher I managed to mess up all major ids note: major ids min file is generated along with atree. it uses numeric ids, not just json compress 2.0.4.4 update (#269) * 2.0.4.4 update Fix v3 item api debug script Implement hellfire (discombob disallow not happening yet) * Fix boiling blood implementation slightly more intuitive also, janky first pass implementation for hellfire * Atree default update Allow sliders to specify a default value, for puppet and boiling blood for now * Fix rainbow def display on items and build stats Calculate into raw def correctly * Atree backend improvements Allow major ids to have dependencies Implement cherry bomb new ver. (wooo replace_spell just works out of the box!) Add comments to atree.js * Fix name of normal items don't you love it when wynn api makes breaking changes for no reason * Misc bugfix Reckless abandon req Tempest new damage ID in search * Fix major id search and temblor desc * Fix blockers on mage * Fix flaming uppercut implementation * Force base dps display to display less digits * Tomes finally pulling from the API but still with alias feature enabled! * Lootrun tomes (finally?) cool? maybe? * Fix beachside set set bonus --------- Co-authored-by: hppeng <hppeng> Fix rainbow def display on items and build stats Calculate into raw def correctly Fix major id search and temblor desc Force base dps display to display less digits Fix beachside set set bonus Fix build decode error reading only 7 tome fields no matter what Give NONE tomes correct ids in load_tome i hate this system so much Allow searching for max/min of ranges Fix crafted item damage display in the process, also update powder calculation logic! Should be fully correct now... TL;DR: Weapon damage is floating point; item display is wrong; ingame displays (damage floaters and compass) are floored. Fluid healing now multiplicative with heal efficiency ID NOTE: this breaks backwards compatibility with older atree jsons. Do we care about this? Realizing how much of a nightmare it will be (and already is) to keep atree fully backwards compatible. Maybe that will be something left to `git clone` instead. fix (#274)
|
@ -27,6 +27,8 @@
|
|||
<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">
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
<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">
|
||||
|
@ -73,6 +75,14 @@
|
|||
<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>
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
<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">
|
||||
|
@ -73,6 +75,14 @@
|
|||
<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>
|
||||
|
@ -82,7 +92,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 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 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>
|
||||
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3 gx-0">
|
||||
<div class="col-xl-6">
|
||||
|
@ -827,6 +837,30 @@
|
|||
</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;">
|
||||
|
@ -1328,5 +1362,11 @@
|
|||
<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>
|
||||
|
|
181278
clean.json
|
@ -29,6 +29,8 @@
|
|||
<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">
|
||||
|
@ -68,6 +70,14 @@
|
|||
<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>
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
<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,6 +72,14 @@
|
|||
<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>
|
||||
|
|
0
data/2.0.3.1/majid.json
Executable file → Normal file
2
data/2.0.4.1/majid.json
Executable file → Normal file
1
data/2.0.4.3/atree.json
Normal file
1
data/2.0.4.3/ingreds.json
Normal file
1
data/2.0.4.3/items.json
Normal file
1
data/2.0.4.3/majid.json
Normal file
1
data/2.0.4.3/recipes.json
Normal file
1069
data/2.0.4.3/tomes.json
Normal file
1
data/2.0.4.4/atree.json
Normal file
1
data/2.0.4.4/ingreds.json
Normal file
1
data/2.0.4.4/items.json
Normal file
1
data/2.0.4.4/majid.json
Normal file
1
data/2.0.4.4/recipes.json
Normal file
1
data/2.0.4.4/tomes.json
Normal file
0
dev/builder_colorcode.png
Executable file → Normal file
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 178 KiB |
0
dev/compute_graph.svg
Executable file → Normal file
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
@ -43,6 +43,8 @@
|
|||
<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">
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
<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">
|
||||
|
@ -60,11 +62,19 @@
|
|||
<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>g
|
||||
</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>
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
<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">
|
||||
|
|
59668
ingreds_clean.json
|
@ -30,6 +30,8 @@
|
|||
<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>
|
||||
|
||||
|
@ -56,6 +58,7 @@
|
|||
</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>
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
<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">
|
||||
|
@ -69,6 +71,14 @@
|
|||
<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>
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
<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>
|
||||
|
@ -152,7 +154,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.'],
|
||||
|
|
196538
items_clean.json
Normal file
1
items_compress.json
Normal file
|
@ -60,7 +60,15 @@ 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'];
|
||||
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 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
|
||||
|
@ -307,8 +315,8 @@ function idRound(id){
|
|||
* stupid stupid multiplicative stats
|
||||
*/
|
||||
function merge_stat(stats, name, value) {
|
||||
const start = name.slice(0, 7);
|
||||
if (start === 'damMult' || start === 'defMult') {
|
||||
const start = name.split('.', limit=1)[0];
|
||||
if (start === 'damMult' || start === 'defMult' || start === 'healMult') {
|
||||
if (!stats.has(start)) {
|
||||
stats.set(start, new Map());
|
||||
}
|
||||
|
@ -319,7 +327,7 @@ function merge_stat(stats, name, value) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
merge_stat(map, name.slice(8), value);
|
||||
merge_stat(map, name.slice(name.indexOf('.')+1), value);
|
||||
return;
|
||||
}
|
||||
if (stats.has(name)) {
|
||||
|
|
|
@ -76,22 +76,23 @@ 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
|
||||
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[float] // affected by behavior
|
||||
slider_max: Optional[int] // affected by behavior
|
||||
slider_default: Optional[int] // affected by 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:
|
||||
|
@ -99,12 +100,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
|
||||
max: float // Hardcap on this effect (slider value * slider_step). Can be negative if scaling is negative
|
||||
}
|
||||
scaling_target: {
|
||||
"type": "stat" | "prop",
|
||||
"abil": Optional[int],
|
||||
"name": str
|
||||
type: "stat" | "prop",
|
||||
abil: Optional[int],
|
||||
name: str
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -165,6 +166,64 @@ 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)
|
||||
*
|
||||
|
@ -176,43 +235,7 @@ 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
|
||||
|
||||
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;
|
||||
return get_sorted_class_atree(atrees, player_class);
|
||||
}
|
||||
})();
|
||||
|
||||
|
@ -476,14 +499,42 @@ const atree_merge = new (class extends ComputeNode {
|
|||
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) {
|
||||
if (abil["class"] === build_class) { merge_abil(abil); }
|
||||
|
||||
// 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');
|
||||
|
@ -540,20 +591,17 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
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, slider_step } = effect;
|
||||
const { slider_name, behavior = 'merge', slider_max = 0, slider_step, slider_default = 0 } = effect;
|
||||
if (slider_map.has(slider_name)) {
|
||||
if (slider_max !== undefined) {
|
||||
const slider_info = slider_map.get(slider_name);
|
||||
slider_info.max += slider_max;
|
||||
}
|
||||
else {
|
||||
unprocessed.push([effect, abil_id, ability]);
|
||||
}
|
||||
const slider_info = slider_map.get(slider_name);
|
||||
slider_info.max += slider_max;
|
||||
slider_info.default_val += slider_default;
|
||||
}
|
||||
else if (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
|
||||
|
|
|
@ -116,8 +116,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,7 +132,8 @@ class Build {
|
|||
}
|
||||
statMap.set("poisonPct", 0);
|
||||
statMap.set("critDamPct", 0);
|
||||
statMap.set("healMult", 0);
|
||||
statMap.set("healMult", new Map());
|
||||
statMap.get('healMult').set('item', statMap.get('healPct'));
|
||||
|
||||
// The stuff relevant for damage calculation!!! @ferricles
|
||||
statMap.set("atkSpd", this.weapon.statMap.get("atkSpd"));
|
||||
|
|
|
@ -33,7 +33,9 @@ const wynn_version_names = [
|
|||
'2.0.2.1',
|
||||
'2.0.2.3',
|
||||
'2.0.3.1',
|
||||
'2.0.4.1'
|
||||
'2.0.4.1',
|
||||
'2.0.4.3',
|
||||
'2.0.4.4'
|
||||
];
|
||||
const WYNN_VERSION_LATEST = wynn_version_names.length - 1;
|
||||
// Default to the newest version.
|
||||
|
@ -65,7 +67,7 @@ async function parse_hash(url_tag) {
|
|||
}
|
||||
//default values
|
||||
let equipment = [null, null, null, null, null, null, null, null, null];
|
||||
let tomes = [null, null, null, null, null, null, null];
|
||||
let tomes = [null, null, null, null, null, null, null, null];
|
||||
let powdering = ["", "", "", "", ""];
|
||||
let info = url_tag.split("_");
|
||||
let version = info[0];
|
||||
|
@ -120,6 +122,7 @@ async function parse_hash(url_tag) {
|
|||
await Promise.all(load_promises);
|
||||
}
|
||||
}
|
||||
|
||||
if (wynn_version_id == WYNN_VERSION_LATEST) {
|
||||
await Promise.all(default_load_promises);
|
||||
}
|
||||
|
@ -150,7 +153,7 @@ async function parse_hash(url_tag) {
|
|||
}
|
||||
data_str = info_str.slice(start_idx);
|
||||
}
|
||||
else if (version_number <= 8) {
|
||||
else if (version_number <= 9) {
|
||||
let info_str = data_str;
|
||||
let start_idx = 0;
|
||||
for (let i = 0; i < 9; ++i ) {
|
||||
|
@ -191,7 +194,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 <= 8){
|
||||
} else if (version_number <= 9){
|
||||
level = Base64.toInt(data_str.slice(10,12));
|
||||
setValue("level-choice",level);
|
||||
save_skp = true;
|
||||
|
@ -210,7 +213,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 in tomes) {
|
||||
for (let i = 0; i < 7; ++i) {
|
||||
let tome_str = data_str.charAt(i);
|
||||
let tome_name = getTomeNameFromID(Base64.toInt(tome_str));
|
||||
setValue(tomeInputs[i], tome_name);
|
||||
|
@ -219,12 +222,21 @@ async function parse_hash(url_tag) {
|
|||
}
|
||||
else {
|
||||
// 2chr tome encoding to allow for more tomes.
|
||||
for (let i in 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) {
|
||||
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(14);
|
||||
data_str = data_str.slice(num_tomes*2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +268,8 @@ function encodeBuild(build, powders, skillpoints, atree, atree_state) {
|
|||
//V6 encoding - Tomes
|
||||
//V7 encoding - ATree
|
||||
//V8 encoding - wynn version
|
||||
build_version = 8;
|
||||
//V9 encoding - lootrun tome
|
||||
build_version = 9;
|
||||
build_string = "";
|
||||
tome_string = "";
|
||||
|
||||
|
@ -269,10 +282,10 @@ function encodeBuild(build, powders, skillpoints, atree, atree_state) {
|
|||
build_string += "CR-"+encodeCraft(item);
|
||||
} else if (item.statMap.get("category") === "tome") {
|
||||
let tome_id = item.statMap.get("id");
|
||||
if (tome_id <= 60) {
|
||||
//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);
|
||||
} else {
|
||||
build_string += Base64.fromIntN(item.statMap.get("id"), 3);
|
||||
|
@ -331,8 +344,8 @@ 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[15].statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]\n";
|
||||
for (let tomeslots = 8; tomeslots < 15; tomeslots++) {
|
||||
"> "+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;
|
||||
|
|
|
@ -230,10 +230,13 @@ function init_autocomplete() {
|
|||
}
|
||||
let tome_alias = tome_obj['alias'];
|
||||
tome_arr.push(tome_name);
|
||||
tome_arr.push(tome_alias);
|
||||
tome_aliases.set(tome_alias, tome_name);
|
||||
if (tome_alias) {
|
||||
tome_arr.push(tome_alias);
|
||||
tome_aliases.set(tome_alias, tome_name);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(tome_arr);
|
||||
// create dropdown
|
||||
dropdowns.set(eq, new autoComplete({
|
||||
data: {
|
||||
|
|
|
@ -60,6 +60,7 @@ let tome_fields = [
|
|||
"armorTome3",
|
||||
"armorTome4",
|
||||
"guildTome1",
|
||||
"lootrunTome1"
|
||||
]
|
||||
let equipment_names = [
|
||||
"Helmet",
|
||||
|
@ -99,7 +100,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'];
|
||||
let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1', 'lootrunTome1'];
|
||||
|
||||
let spell_disp = ['build-melee-stats', 'spell0-info', 'spell1-info', 'spell2-info', 'spell3-info'];
|
||||
let other_disp = ['build-order', 'set-info', 'int-info'];
|
||||
|
|
|
@ -413,8 +413,10 @@ class BuildAssembleNode extends ComputeNode {
|
|||
input_map.get('armorTome2'),
|
||||
input_map.get('armorTome3'),
|
||||
input_map.get('armorTome4'),
|
||||
input_map.get('guildTome1')
|
||||
input_map.get('guildTome1'),
|
||||
input_map.get('lootrunTome1')
|
||||
];
|
||||
console.log(equipments);
|
||||
let weapon = input_map.get('weapon');
|
||||
let level = parseInt(input_map.get('level-input'));
|
||||
if (isNaN(level)) {
|
||||
|
@ -537,7 +539,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")/100.);
|
||||
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), (stats.get(skp_elements[i] + "DefPct") + stats.get("rDefPct"))/100.);
|
||||
}
|
||||
defenseStats.push(eledefs);
|
||||
|
||||
|
@ -576,8 +578,21 @@ class SpellDamageCalcNode extends ComputeNode {
|
|||
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
|
||||
|
@ -593,65 +608,68 @@ class SpellDamageCalcNode extends ComputeNode {
|
|||
}
|
||||
} else if ('power' in part) {
|
||||
// TODO: wynn2 formula
|
||||
let heal_additive = stats.get('healPct');
|
||||
if (stats.has('healPct:'+part_id)) {
|
||||
heal_additive += stats.get('healPct:'+part_id);
|
||||
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_mult = 1+(stats.get('healMult') / 100)
|
||||
let _heal_amount = part.power * getDefenseStats(stats)[0] * (1 + (heal_additive/100)) * heal_mult;
|
||||
let _heal_amount = part.power * getDefenseStats(stats)[0] * heal_mult;
|
||||
spell_result = {
|
||||
type: "heal",
|
||||
heal_amount: _heal_amount
|
||||
}
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
const {name, display = true} = part;
|
||||
spell_result.name = name;
|
||||
spell_result.display = 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";
|
||||
}
|
||||
} 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 {
|
||||
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;
|
||||
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.heal_amount += subpart.heal_amount * 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
const {name, display = true} = part;
|
||||
spell_result.name = name;
|
||||
spell_result.display = 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;
|
||||
}
|
||||
|
@ -827,9 +845,13 @@ 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",*/
|
||||
"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",*/ // 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",
|
||||
|
||||
// wynn2 damages.
|
||||
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw",//"eDamAddMin","eDamAddMax",
|
||||
|
@ -868,12 +890,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 {
|
||||
|
@ -1068,7 +1090,7 @@ 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]])) {
|
||||
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]])) {
|
||||
let input_field = document.getElementById(eq+"-choice");
|
||||
let item_image = document.getElementById(eq+"-img");
|
||||
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
{
|
||||
"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",
|
||||
|
@ -150,24 +160,31 @@
|
|||
},
|
||||
"CHERRY_BOMBS": {
|
||||
"displayName": "Cherry Bombs",
|
||||
"description": "Your Smoke Bombs explode instantly, and increase their Neutral Damage by +90%",
|
||||
"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": "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 }
|
||||
}
|
||||
]
|
||||
"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": {
|
||||
|
@ -214,13 +231,13 @@
|
|||
"abilities": []
|
||||
},
|
||||
"DESC_FESTIVESPIRIT": {
|
||||
"displayName": "Festive Spirits",
|
||||
"displayName": "Festive Spirit",
|
||||
"description": "Plays wintery tunes",
|
||||
"abilities": []
|
||||
},
|
||||
"TEMBLOR": {
|
||||
"displayName": "Temblor",
|
||||
"description": "Bash gains +1 Area of Effect and is 20% faster.",
|
||||
"description": "Bash gains +1 Area of Effect and is 25% faster.",
|
||||
"abilities": [{
|
||||
"class": "Warrior",
|
||||
"base_abil": "Bash",
|
||||
|
@ -230,37 +247,31 @@
|
|||
},
|
||||
"RECKLESS_ABANDON": {
|
||||
"displayName": "Reckless Abandon",
|
||||
"description": "Tempest deals +15% Fire damage and gains one additional charge. War Scream no longer grants a defence bonus.",
|
||||
"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, 0, 0, 0, 15, 0]
|
||||
"multipliers": [0, 5, 0, 0, 15, 5]
|
||||
},
|
||||
{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 4,
|
||||
"target_part": "Tempest Total Damage",
|
||||
"behavior": "modify",
|
||||
"hits": { "Tempest": 1 }
|
||||
},
|
||||
{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 4,
|
||||
"target_part": "Total Damage",
|
||||
"behavior": "modify",
|
||||
"hits": { "Tempest": 1 }
|
||||
"hits": { "Tempest": 2 }
|
||||
}
|
||||
]
|
||||
}]
|
||||
},
|
||||
"ALTEREGO": {
|
||||
"displayName": "Alterego",
|
||||
"displayName": "Alter Ego",
|
||||
"description": "Awakened can be activated after saving 40% less mana, but its duration is reduced by 25%.",
|
||||
"abilities": []
|
||||
},
|
||||
|
@ -296,7 +307,7 @@
|
|||
}]
|
||||
},
|
||||
"SOUL_EATER": {
|
||||
"displayName": "Soul eater",
|
||||
"displayName": "Soul Eater",
|
||||
"description": "Devour and Harvester grant double mana, but your maximum Marks are decreased by 1.",
|
||||
"abilities": [{
|
||||
"class": "Assassin",
|
||||
|
@ -398,7 +409,7 @@
|
|||
},
|
||||
"DIVINE_HONOR": {
|
||||
"displayName": "Divine Honor",
|
||||
"description": "Increase the bonus from Radiance by 5%. Decrease the Neutral damage of Bash by -15%.",
|
||||
"description": "Increase the bonus from Radiance by 5%. Decrease the Earth damage of Bash by -15%.",
|
||||
"abilities": [{
|
||||
"class": "Warrior",
|
||||
"base_abil": "Bash",
|
||||
|
@ -408,7 +419,7 @@
|
|||
"base_spell": 1,
|
||||
"target_part": "Single Hit",
|
||||
"behavior": "modify",
|
||||
"multipliers": [-15, 0, 0, 0, 0, 0]
|
||||
"multipliers": [0, -15, 0, 0, 0, 0]
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
@ -456,5 +467,135 @@
|
|||
}
|
||||
]
|
||||
}]
|
||||
},
|
||||
"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]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
let recipeTypes = ["HELMET","CHESTPLATE","LEGGINGS","BOOTS","RELIK","WAND","SPEAR","DAGGER","BOW","RING","NECKLACE","BRACELET","SCROLL","FOOD","POTION"];
|
||||
let recipeTypes = ["HELMET","CHESTPLATE","LEGGINGS","BOOTS","RELIK","WAND","SPEAR","DAGGER","BOW","RING","NECKLACE","BRACELET","POTION", "SCROLL","FOOD"];
|
||||
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",]
|
||||
|
||||
function encodeCraft(craft) {
|
||||
|
@ -180,7 +180,6 @@ 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];
|
||||
|
|
|
@ -144,8 +144,8 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
|
|||
// Collect total damage post %boost
|
||||
}
|
||||
|
||||
let total_elem_min = total_min - damages[0][0];
|
||||
let total_elem_max = total_max - damages[0][1];
|
||||
let total_elem_min = total_min - save_prop[0][0];
|
||||
let total_elem_max = total_max - save_prop[0][1];
|
||||
|
||||
// 5.2: Raw application.
|
||||
let prop_raw = stats.get(specific_boost_str.toLowerCase()+'Raw') + stats.get('damRaw');
|
||||
|
|
|
@ -309,11 +309,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.round(x));
|
||||
damages = damages.map(x => Math.floor(x));
|
||||
item.set(id, damages[0]+"-"+damages[1]);
|
||||
}
|
||||
else {
|
||||
damages = damages.map(x => x.map(y => Math.round(y)));
|
||||
damages = damages.map(x => x.map(y => Math.floor(y)));
|
||||
item.set(id, damages[0][0]+"-"+damages[0][1]+"\u279c"+damages[1][0]+"-"+damages[1][1]);
|
||||
}
|
||||
}
|
||||
|
@ -466,7 +466,6 @@ 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");
|
||||
|
@ -496,7 +495,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);
|
||||
base_dps_elem.textContent = "Base DPS: "+(total_damages.toFixed(3));
|
||||
}
|
||||
parent_div.append(make_elem("p"), base_dps_elem);
|
||||
}
|
||||
|
@ -1181,14 +1180,6 @@ 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") + statMap.get('rDefPct'))/100;
|
||||
if (defRaw < 0) {
|
||||
defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct;
|
||||
} else {
|
||||
defPct >= 0 ? defPct = "+ " + defPct: defPct = "- " + defPct;
|
||||
}
|
||||
eledefElemRow.appendChild(boost);
|
||||
|
||||
if (insertSummary) {
|
||||
|
|
|
@ -335,7 +335,9 @@ let build_detailed_display_commands = [
|
|||
"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",
|
||||
|
@ -429,6 +431,8 @@ let sq2_item_display_commands = [
|
|||
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
|
||||
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
|
||||
"!elemental",
|
||||
"rDefPct",
|
||||
|
||||
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
|
||||
"sprint", "sprintReg",
|
||||
"jh",
|
||||
|
|
|
@ -47,54 +47,54 @@ 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",
|
||||
"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",
|
||||
|
|
|
@ -60,7 +60,8 @@ function toggleAmps(button_id) {
|
|||
|
||||
|
||||
(async function() {
|
||||
let load_promises = [ load_init() ];
|
||||
let latest_ver_name = wynn_version_names[WYNN_VERSION_LATEST];
|
||||
let load_promises = [ load_init(), load_major_id_data(latest_ver_name) ];
|
||||
await Promise.all(load_promises);
|
||||
init_itempage();
|
||||
})();
|
||||
|
|
414
js/item_display.js
Normal file
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
96
js/items.js
|
@ -56,54 +56,54 @@ 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",
|
||||
"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",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const DB_VERSION = 129;
|
||||
const DB_VERSION = 133;
|
||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
||||
|
||||
let db;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const ING_DB_VERSION = 28;
|
||||
const ING_DB_VERSION = 32;
|
||||
|
||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const TOME_DB_VERSION = 7;
|
||||
const TOME_DB_VERSION = 9;
|
||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
||||
|
||||
let tdb;
|
||||
|
@ -134,9 +134,10 @@ async function load_tome_init() {
|
|||
}
|
||||
|
||||
const none_tomes_info = [
|
||||
["tome", "weaponTome", "No Weapon Tome"],
|
||||
["tome", "armorTome", "No Armor Tome"],
|
||||
["tome", "guildTome", "No Guild Tome"]
|
||||
["tome", "weaponTome", "No Weapon Tome", 61],
|
||||
["tome", "armorTome", "No Armor Tome", 62],
|
||||
["tome", "guildTome", "No Guild Tome", 63],
|
||||
["tome", "lootrunTome", "No Lootrun Tome", 93]
|
||||
];
|
||||
let none_tomes;
|
||||
|
||||
|
@ -152,7 +153,7 @@ function init_tome_maps() {
|
|||
}
|
||||
|
||||
none_tomes = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let tome = Object();
|
||||
tome.slots = 0;
|
||||
tome.category = none_tomes_info[i][0];
|
||||
|
@ -166,7 +167,7 @@ function init_tome_maps() {
|
|||
tome.reqs = [0, 0, 0, 0, 0];
|
||||
tome.fixID = true;
|
||||
tome.tier = "Normal";
|
||||
tome.id = 61 + i; //special case!
|
||||
tome.id = none_tomes_info[i][3];
|
||||
tome.nDam = "0-0";
|
||||
tome.eDam = "0-0";
|
||||
tome.tDam = "0-0";
|
||||
|
|
|
@ -134,8 +134,9 @@ function calc_weapon_powder(weapon, damageBases) {
|
|||
//1st round - apply each as ingred, 2nd round - apply as normal
|
||||
if (weapon.get("tier") === "Crafted" && !weapon.get("custom")) {
|
||||
for (const p of powders.concat(weapon.get("ingredPowders"))) {
|
||||
let powder = powderStats[p]; //use min, max, and convert
|
||||
let element = Math.floor((p+0.01)/6); //[0,4], the +0.01 attempts to prevent division error
|
||||
const powder = powderStats[p]; //use min, max, and convert
|
||||
// Bitwise to force conversion to integer (integer division).
|
||||
const element = (p/6) | 0;
|
||||
let diff = Math.floor(damageBases[0] * powder.convert/100);
|
||||
damageBases[0] -= diff;
|
||||
damageBases[element+1] += diff + Math.floor( (powder.min + powder.max) / 2 );
|
||||
|
|
80
js/query.js
|
@ -46,13 +46,34 @@ const itemQueryProps = (function() {
|
|||
prop(names, 'number', (i, ie) => ie.get('minRolls').get(idKey) || 0);
|
||||
}
|
||||
|
||||
function rangeAvg(names, getProp) {
|
||||
function rangeAll(names, getProp) {
|
||||
// Max
|
||||
prop(names.map((s) => s+'max'), 'number', (i, ie) => {
|
||||
const range = getProp(i, ie);
|
||||
if (!range) return 0;
|
||||
const ndx = range.indexOf('-');
|
||||
return parseInt(range.substring(ndx + 1), 10);
|
||||
});
|
||||
// Min
|
||||
prop(names.map((s) => s+'min'), 'number', (i, ie) => {
|
||||
const range = getProp(i, ie);
|
||||
if (!range) return 0;
|
||||
const ndx = range.indexOf('-');
|
||||
return parseInt(range.substring(0, ndx), 10);
|
||||
});
|
||||
// Average
|
||||
prop(names, 'number', (i, ie) => {
|
||||
const range = getProp(i, ie);
|
||||
if (!range) return 0;
|
||||
const ndx = range.indexOf('-');
|
||||
return (parseInt(range.substring(0, ndx), 10) + parseInt(range.substring(ndx + 1), 10)) / 2;
|
||||
});
|
||||
prop(names.map((s) => s+'avg'), 'number', (i, ie) => {
|
||||
const range = getProp(i, ie);
|
||||
if (!range) return 0;
|
||||
const ndx = range.indexOf('-');
|
||||
return (parseInt(range.substring(0, ndx), 10) + parseInt(range.substring(ndx + 1), 10)) / 2;
|
||||
});
|
||||
}
|
||||
|
||||
function map(names, comps, outType, f) {
|
||||
|
@ -77,6 +98,8 @@ const itemQueryProps = (function() {
|
|||
const tierIndices = { Normal: 0, Unique: 1, Set: 2, Rare: 3, Legendary: 4, Fabled: 5, Mythic: 6 };
|
||||
prop(['rarityname', 'raritystr', 'tiername', 'tierstr'], 'string', (i, ie) => i.tier);
|
||||
prop(['rarity', 'tier'], 'number', (i, ie) => tierIndices[i.tier]);
|
||||
prop(['majid', 'majorid'], 'string', (i, ie) => ((i.majorIds || [""])[0] || ""));
|
||||
prop(['majids', 'majorids'], 'number', (i, ie) => (i.majorIds || []).length);
|
||||
|
||||
prop(['level', 'lvl', 'combatlevel', 'combatlvl'], 'number', (i, ie) => i.lvl);
|
||||
prop(['strmin', 'strreq'], 'number', (i, ie) => i.strReq);
|
||||
|
@ -93,12 +116,12 @@ const itemQueryProps = (function() {
|
|||
prop('agi', 'number', (i, ie) => i.agi);
|
||||
sum(['skillpoints', 'skillpts', 'attributes', 'attrs'], props.str, props.dex, props.int, props.def, props.agi);
|
||||
|
||||
rangeAvg(['neutraldmg', 'neutraldam', 'ndmg', 'ndam'], (i, ie) => i.nDam);
|
||||
rangeAvg(['earthdmg', 'earthdam', 'edmg', 'edam'], (i, ie) => i.eDam);
|
||||
rangeAvg(['thunderdmg', 'thunderdam', 'tdmg', 'tdam'], (i, ie) => i.tDam);
|
||||
rangeAvg(['waterdmg', 'waterdam', 'wdmg', 'wdam'], (i, ie) => i.wDam);
|
||||
rangeAvg(['firedmg', 'firedam', 'fdmg', 'fdam'], (i, ie) => i.fDam);
|
||||
rangeAvg(['airdmg', 'airdam', 'admg', 'adam'], (i, ie) => i.aDam);
|
||||
rangeAll(['neutraldmg', 'neutraldam', 'ndmg', 'ndam'], (i, ie) => i.nDam);
|
||||
rangeAll(['earthdmg', 'earthdam', 'edmg', 'edam'], (i, ie) => i.eDam);
|
||||
rangeAll(['thunderdmg', 'thunderdam', 'tdmg', 'tdam'], (i, ie) => i.tDam);
|
||||
rangeAll(['waterdmg', 'waterdam', 'wdmg', 'wdam'], (i, ie) => i.wDam);
|
||||
rangeAll(['firedmg', 'firedam', 'fdmg', 'fdam'], (i, ie) => i.fDam);
|
||||
rangeAll(['airdmg', 'airdam', 'admg', 'adam'], (i, ie) => i.aDam);
|
||||
sum(['sumdmg', 'sumdam', 'totaldmg', 'totaldam'], props.ndam, props.edam, props.tdam, props.wdam, props.fdam, props.adam);
|
||||
|
||||
maxId(['earthdmg%', 'earthdam%', 'edmg%', 'edam%', 'edampct'], 'eDamPct');
|
||||
|
@ -106,13 +129,43 @@ const itemQueryProps = (function() {
|
|||
maxId(['waterdmg%', 'waterdam%', 'wdmg%', 'wdam%', 'wdampct'], 'wDamPct');
|
||||
maxId(['firedmg%', 'firedam%', 'fdmg%', 'fdam%', 'fdampct'], 'fDamPct');
|
||||
maxId(['airdmg%', 'airdam%', 'admg%', 'adam%', 'adampct'], 'aDamPct');
|
||||
sum(['sumdmg%', 'sumdam%', 'totaldmg%', 'totaldam%', 'sumdampct', 'totaldampct'], props.edampct, props.tdampct, props.wdampct, props.fdampct, props.adampct);
|
||||
//sum(['sumdmg%', 'sumdam%', 'totaldmg%', 'totaldam%', 'sumdampct', 'totaldampct'], props.edampct, props.tdampct, props.wdampct, props.fdampct, props.adampct);
|
||||
|
||||
maxId(['mainatkdmg', 'mainatkdam', 'mainatkdmg%', 'mainatkdam%', 'meleedmg', 'meleedam', 'meleedmg%', 'meleedam%', 'mdpct'], 'mdPct');
|
||||
maxId(['emdpct'], 'eMdPct');
|
||||
maxId(['tmdpct'], 'tMdPct');
|
||||
maxId(['wmdpct'], 'wMdPct');
|
||||
maxId(['fmdpct'], 'fMdPct');
|
||||
maxId(['amdpct'], 'aMdPct');
|
||||
maxId(['nmdpct'], 'nMdPct');
|
||||
maxId(['rmdpct'], 'rMdPct');
|
||||
|
||||
maxId(['mainatkrawdmg', 'mainatkrawdam', 'mainatkneutraldmg', 'mainatkneutraldam', 'meleerawdmg', 'meleerawdam', 'meleeneutraldmg', 'meleeneutraldam', 'mdraw'], 'mdRaw');
|
||||
maxId(['emdraw'], 'eMdRaw');
|
||||
maxId(['tmdraw'], 'tMdRaw');
|
||||
maxId(['wmdraw'], 'wMdRaw');
|
||||
maxId(['fmdraw'], 'fMdRaw');
|
||||
maxId(['amdraw'], 'aMdRaw');
|
||||
maxId(['nmdraw'], 'nMdRaw');
|
||||
maxId(['rmdraw'], 'rMdRaw');
|
||||
|
||||
maxId(['spelldmg', 'spelldam', 'spelldmg%', 'spelldam%', 'sdpct'], 'sdPct');
|
||||
maxId(['esdpct'], 'eSdPct');
|
||||
maxId(['tsdpct'], 'tSdPct');
|
||||
maxId(['wsdpct'], 'wSdPct');
|
||||
maxId(['fsdpct'], 'fSdPct');
|
||||
maxId(['asdpct'], 'aSdPct');
|
||||
maxId(['nsdpct'], 'nSdPct');
|
||||
maxId(['rsdpct'], 'rSdPct');
|
||||
|
||||
maxId(['spellrawdmg', 'spellrawdam', 'spellneutraldmg', 'spellneutraldam', 'sdraw'], 'sdRaw');
|
||||
maxId(['rainbowraw'], 'rSdRaw');
|
||||
maxId(['esdraw'], 'eSdRaw');
|
||||
maxId(['tsdraw'], 'tSdRaw');
|
||||
maxId(['wsdraw'], 'wSdRaw');
|
||||
maxId(['fsdraw'], 'fSdRaw');
|
||||
maxId(['asdraw'], 'aSdRaw');
|
||||
maxId(['nsdraw'], 'nSdRaw');
|
||||
maxId(['rainbowraw', 'rsdraw'], 'rSdRaw');
|
||||
|
||||
const atkSpdIndices = { SUPER_SLOW: -3, VERY_SLOW: -2, SLOW: -1, NORMAL: 0, FAST: 1, VERY_FAST: 2, SUPER_FAST: 3 };
|
||||
prop(['attackspeed', 'atkspd'], 'string', (i, ie) => i.atkSpd ? atkSpdIndices[i.atkSpd] : 0);
|
||||
|
@ -131,6 +184,7 @@ const itemQueryProps = (function() {
|
|||
maxId(['waterdef%', 'wdef%', 'wdefpct'], 'wDefPct');
|
||||
maxId(['firedef%', 'fdef%', 'fdefpct'], 'fDefPct');
|
||||
maxId(['airdef%', 'adef%', 'adefpct'], 'aDefPct');
|
||||
maxId(['eledef%', 'rdef%', 'rdefpct'], 'rDefPct');
|
||||
sum(['sumdef%', 'totaldef%', 'sumdefpct', 'totaldefpct'], props.edefpct, props.tdefpct, props.wdefpct, props.fdefpct, props.adefpct);
|
||||
|
||||
prop(['health', 'hp'], 'number', (i, ie) => i.hp || 0);
|
||||
|
@ -167,6 +221,14 @@ const itemQueryProps = (function() {
|
|||
maxId(['lootbonus', 'lb'], 'lb');
|
||||
maxId(['xpbonus', 'xpb', 'xb'], 'xpb');
|
||||
maxId(['stealing', 'esteal'], 'eSteal');
|
||||
maxId(['lq', 'quality'], 'lq');
|
||||
maxId('gxp', 'gXp');
|
||||
maxId('gspd', 'gSpd');
|
||||
maxId(['healeff', 'healpct'], 'healPct');
|
||||
maxId('kb', 'kb');
|
||||
maxId('weakenenemy', 'weakenEnemy');
|
||||
maxId('slowenemy', 'slowEnemy');
|
||||
|
||||
prop(['powderslots', 'powders', 'slots', 'sockets'], 'number', (i, ie) => i.slots || 0);
|
||||
|
||||
return props;
|
||||
|
|
|
@ -206,6 +206,7 @@ class ExprField {
|
|||
try {
|
||||
this.output = this.compiler(this.text);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.errorText.innerText = e.message;
|
||||
this.output = null;
|
||||
}
|
||||
|
@ -303,6 +304,7 @@ function init_items_adv() {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
searchFilterField.errorText.innerText = e.message;
|
||||
return;
|
||||
}
|
||||
|
@ -320,6 +322,7 @@ function init_items_adv() {
|
|||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
searchSortField.errorText.innerText = e.message;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
<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">
|
||||
|
@ -82,6 +84,14 @@
|
|||
<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>
|
||||
|
|
BIN
media/icons/new/nori_build.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
media/icons/new/nori_recipe.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
media/icons/old/nori_build.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
media/icons/old/nori_recipe.png
Normal file
After Width: | Height: | Size: 10 KiB |
|
@ -6,71 +6,73 @@ given [atree_constants.js] .js form of the Ability Tree with reference as string
|
|||
"""
|
||||
import json
|
||||
|
||||
def translate_id(id_data, atree_data):
|
||||
def translate_spell_part(id_data, part):
|
||||
if 'hits' in part: # Translate parametrized hits...
|
||||
hits_mapping = part['hits']
|
||||
keys = list(hits_mapping.keys())
|
||||
for k in keys:
|
||||
v = hits_mapping[k]
|
||||
if isinstance(v, str):
|
||||
abil_id, propname = v.split('.')
|
||||
hits_mapping[k] = str(id_data[abil_id])+'.'+propname
|
||||
|
||||
def translate_effect(id_data, effect):
|
||||
if effect["type"] == "raw_stat":
|
||||
for bonus in effect["bonuses"]:
|
||||
if "abil" in bonus and bonus["abil"] in id_data:
|
||||
bonus["abil"] = id_data[bonus["abil"]]
|
||||
elif effect["type"] == "replace_spell":
|
||||
for part in effect['parts']:
|
||||
translate_spell_part(id_data, part)
|
||||
elif effect["type"] == "add_spell_prop":
|
||||
translate_spell_part(id_data, effect)
|
||||
elif effect["type"] == "stat_scaling":
|
||||
if "inputs" in effect: # Might not exist for sliders
|
||||
for _input in effect["inputs"]:
|
||||
if "abil" in _input and _input["abil"] in id_data:
|
||||
_input["abil"] = id_data[_input["abil"]]
|
||||
if "output" in effect:
|
||||
if isinstance(effect["output"], list):
|
||||
for output in effect["output"]:
|
||||
if "abil" in output and output["abil"] in id_data:
|
||||
output["abil"] = id_data[output["abil"]]
|
||||
else:
|
||||
if "abil" in effect["output"] and effect["output"]["abil"] in id_data:
|
||||
effect["output"]["abil"] = id_data[effect["output"]["abil"]]
|
||||
|
||||
def translate_abil(id_data, abil, tree=True):
|
||||
def translate(path, ref):
|
||||
ref_dict = abil
|
||||
for x in path:
|
||||
ref_dict = ref_dict[x]
|
||||
ref_dict[ref] = id_data[ref_dict[ref]]
|
||||
|
||||
for optional_key in ["parents", "dependencies", "blockers"]:
|
||||
if optional_key not in abil:
|
||||
if tree:
|
||||
print(f"WARNING: atree node missing required key [{optional_key}]")
|
||||
continue
|
||||
for ref in range(len(abil[optional_key])):
|
||||
translate([optional_key], ref)
|
||||
|
||||
if "base_abil" in abil:
|
||||
base_abil_name = abil["base_abil"]
|
||||
if base_abil_name in id_data:
|
||||
translate([], "base_abil")
|
||||
|
||||
if "effects" not in abil:
|
||||
print("WARNING: abil missing 'effects' tag")
|
||||
print(abil)
|
||||
abil["effects"] = []
|
||||
for effect in abil["effects"]:
|
||||
translate_effect(id_data, effect)
|
||||
|
||||
def translate_all(id_data, atree_data):
|
||||
for _class, info in atree_data.items():
|
||||
def translate(path, ref):
|
||||
ref_dict = info
|
||||
for x in path:
|
||||
ref_dict = ref_dict[x]
|
||||
ref_dict[ref] = id_data[_class][ref_dict[ref]]
|
||||
|
||||
for abil in range(len(info)):
|
||||
info[abil]["id"] = id_data[_class][info[abil]["display_name"]]
|
||||
for ref in range(len(info[abil]["parents"])):
|
||||
translate([abil, "parents"], ref)
|
||||
for abil in info:
|
||||
abil["id"] = id_data[_class][abil["display_name"]]
|
||||
translate_abil(id_data[_class], abil)
|
||||
|
||||
for ref in range(len(info[abil]["dependencies"])):
|
||||
translate([abil, "dependencies"], ref)
|
||||
|
||||
for ref in range(len(info[abil]["blockers"])):
|
||||
translate([abil, "blockers"], ref)
|
||||
|
||||
if "base_abil" in info[abil]:
|
||||
base_abil_name = info[abil]["base_abil"]
|
||||
if base_abil_name in id_data[_class]:
|
||||
translate([abil], "base_abil")
|
||||
|
||||
if "effects" not in info[abil]:
|
||||
print("WARNING: abil missing 'effects' tag")
|
||||
print(info[abil])
|
||||
info[abil]["effects"] = []
|
||||
for effect in info[abil]["effects"]:
|
||||
if effect["type"] == "raw_stat":
|
||||
for bonus in effect["bonuses"]:
|
||||
if "abil" in bonus and bonus["abil"] in id_data[_class]:
|
||||
bonus["abil"] = id_data[_class][bonus["abil"]]
|
||||
elif effect["type"] == "replace_spell":
|
||||
for part in effect['parts']:
|
||||
if 'hits' in part: # Translate parametrized hits...
|
||||
hits_mapping = part['hits']
|
||||
keys = list(hits_mapping.keys())
|
||||
for k in keys:
|
||||
v = hits_mapping[k]
|
||||
if isinstance(v, str):
|
||||
abil_id, propname = v.split('.')
|
||||
hits_mapping[k] = str(id_data[_class][abil_id])+'.'+propname
|
||||
elif effect["type"] == "add_spell_prop":
|
||||
if 'hits' in effect: # Translate parametrized hits...
|
||||
hits_mapping = effect['hits']
|
||||
keys = list(hits_mapping.keys())
|
||||
for k in keys:
|
||||
v = hits_mapping[k]
|
||||
if isinstance(v, str):
|
||||
abil_id, propname = v.split('.')
|
||||
hits_mapping[k] = str(id_data[_class][abil_id])+'.'+propname
|
||||
elif effect["type"] == "stat_scaling":
|
||||
if "inputs" in effect: # Might not exist for sliders
|
||||
for _input in effect["inputs"]:
|
||||
if "abil" in _input and _input["abil"] in id_data[_class]:
|
||||
_input["abil"] = id_data[_class][_input["abil"]]
|
||||
if "output" in effect:
|
||||
if isinstance(effect["output"], list):
|
||||
for output in effect["output"]:
|
||||
if "abil" in output and output["abil"] in id_data[_class]:
|
||||
output["abil"] = id_data[_class][output["abil"]]
|
||||
else:
|
||||
if "abil" in effect["output"] and effect["output"]["abil"] in id_data[_class]:
|
||||
effect["output"]["abil"] = id_data[_class][effect["output"]["abil"]]
|
||||
|
||||
abilDict = {}
|
||||
with open("atree_constants.js") as f:
|
||||
|
@ -87,15 +89,14 @@ with open("atree_constants.js") as f:
|
|||
with open("atree_ids.json", "w", encoding='utf-8') as id_dest:
|
||||
json.dump(abilDict, id_dest, ensure_ascii=False, indent=4)
|
||||
|
||||
translate_id(abilDict, data)
|
||||
translate_all(abilDict, data)
|
||||
|
||||
with open("major_ids_clean.json") as maj_id_file:
|
||||
maj_id_dat = json.load(maj_id_file)
|
||||
for k, v in maj_id_dat.items():
|
||||
for abil in v['abilities']:
|
||||
clazz = abil['class']
|
||||
base_abil = abil['base_abil']
|
||||
abil['base_abil'] = abilDict[clazz][base_abil]
|
||||
translate_abil(abilDict[clazz], abil, tree=False)
|
||||
with open("major_ids_min.json", "w", encoding='utf-8') as maj_id_out:
|
||||
json.dump(maj_id_dat, maj_id_out, ensure_ascii=False, separators=(',', ':'))
|
||||
|
||||
|
|
|
@ -3847,5 +3847,28 @@
|
|||
"Pain Cycle": 3845,
|
||||
"Psionic Pretense": 3846,
|
||||
"Propeller Hat": 3847,
|
||||
"Tremorcaller": 3848
|
||||
"Tremorcaller": 3848,
|
||||
"Black Skull": 3849,
|
||||
"Replica Hallowynn Mask": 3850,
|
||||
"Esteemed Bronze Mask of Legendary Victory": 3851,
|
||||
"Swashbuckler's Brogues": 3852,
|
||||
"Outlandish Replica Face Mask of Legendary Victory": 3853,
|
||||
"Mystical Tags": 3854,
|
||||
"Future Shock Plating": 3855,
|
||||
"Spider Leg Suit": 3856,
|
||||
"Ceremonial Skirt": 3857,
|
||||
"Runebound Chains": 3858,
|
||||
"Ice-cold Robe": 3859,
|
||||
"Blob Monster Mask": 3860,
|
||||
"Psychopomp's Pileus": 3861,
|
||||
"Revered Silver Mask of Legendary Victory": 3862,
|
||||
"Treasured Diamond Mask of Legendary Victory": 3863,
|
||||
"Venerated Gold Mask of Legendary Victory": 3864,
|
||||
"Beige Wynnter Sweater": 3865,
|
||||
"Scarlet Wynnter Sweater": 3866,
|
||||
"Orange Wynnter Sweater": 3867,
|
||||
"Pine Wynnter Sweater": 3868,
|
||||
"Indigo Wynnter Sweater": 3869,
|
||||
"Air In A Can": 3870,
|
||||
"Iosis": 3871
|
||||
}
|
234
py_script/item_metadata.json
Normal file
|
@ -0,0 +1,234 @@
|
|||
{
|
||||
"identifications": [
|
||||
"rawMainAttackDamage",
|
||||
"rawSpellDamage",
|
||||
"healthRegenRaw",
|
||||
"manaSteal",
|
||||
"walkSpeed",
|
||||
"thunderDamage",
|
||||
"rawStrength",
|
||||
"rawDexterity",
|
||||
"rawIntelligence",
|
||||
"rawDefence",
|
||||
"rawAgility",
|
||||
"lootBonus",
|
||||
"fireDefence",
|
||||
"airDefence",
|
||||
"mainAttackDamage",
|
||||
"spellDamage",
|
||||
"exploding",
|
||||
"airDamage",
|
||||
"rawHealth",
|
||||
"reflection",
|
||||
"earthDefence",
|
||||
"earthDamage",
|
||||
"waterDamage",
|
||||
"waterDefence",
|
||||
"healthRegen",
|
||||
"manaRegen",
|
||||
"fireDamage",
|
||||
"lifeSteal",
|
||||
"rawAttackSpeed",
|
||||
"xpBonus",
|
||||
"thunderDefence",
|
||||
"thorns",
|
||||
"soulPointRegen",
|
||||
"stealing",
|
||||
"1stSpellCost",
|
||||
"2ndSpellCost",
|
||||
"raw1stSpellCost",
|
||||
"raw3rdSpellCost",
|
||||
"jumpHeight",
|
||||
"airSpellDamage",
|
||||
"poison",
|
||||
"elementalDamage",
|
||||
"healingEfficiency",
|
||||
"raw4thSpellCost",
|
||||
"raw2ndSpellCost",
|
||||
"sprintRegen",
|
||||
"slowEnemy",
|
||||
"3rdSpellCost",
|
||||
"sprint",
|
||||
"elementalSpellDamage",
|
||||
"rawNeutralSpellDamage",
|
||||
"4thSpellCost",
|
||||
"knockback",
|
||||
"waterSpellDamage",
|
||||
"fireSpellDamage",
|
||||
"rawAirMainAttackDamage",
|
||||
"rawAirSpellDamage",
|
||||
"earthSpellDamage",
|
||||
"rawThunderDamage",
|
||||
"rawWaterDamage",
|
||||
"rawElementalDamage",
|
||||
"rawEarthSpellDamage",
|
||||
"elementalDefence",
|
||||
"rawThunderMainAttackDamage",
|
||||
"thunderSpellDamage",
|
||||
"rawThunderSpellDamage",
|
||||
"rawFireMainAttackDamage",
|
||||
"weakenEnemy",
|
||||
"rawWaterSpellDamage",
|
||||
"earthMainAttackDamage",
|
||||
"rawFireSpellDamage",
|
||||
"rawElementalSpellDamage",
|
||||
"healing",
|
||||
"rawElementalMainAttackDamage",
|
||||
"airMainAttackDamage",
|
||||
"thunderMainAttackDamage",
|
||||
"leveledLootBonus",
|
||||
"damageFromMobs",
|
||||
"leveledXpBonus",
|
||||
"elementalDefense",
|
||||
"rawAirDamage",
|
||||
"rawEarthDamage",
|
||||
"rawFireDamage",
|
||||
"rawNeutralDamage",
|
||||
"lootQuality",
|
||||
"gatherXpBonus",
|
||||
"gatherSpeed",
|
||||
"rawWaterMainAttackDamage",
|
||||
"rawEarthMainAttackDamage"
|
||||
],
|
||||
"majorIds": [
|
||||
"Divine Honor",
|
||||
"Greed",
|
||||
"Magnet",
|
||||
"Plague",
|
||||
"Saviour\u2019s Sacrifice",
|
||||
"Sorcery",
|
||||
"Soul Eater",
|
||||
"Cherry Bombs",
|
||||
"Expunge",
|
||||
"Flashfreeze",
|
||||
"Gentle Glow",
|
||||
"Gruesome Knots",
|
||||
"Peaceful Effigy",
|
||||
"Rally",
|
||||
"Reckless Abandon",
|
||||
"Gravity Well",
|
||||
"Perfect Recall",
|
||||
"Cavalryman",
|
||||
"Entropy",
|
||||
"Escape Route",
|
||||
"Lightweight",
|
||||
"Snowy Steps",
|
||||
"Taunt",
|
||||
"Dead Weight",
|
||||
"Furious Effigy",
|
||||
"Geocentrism",
|
||||
"Strings of Fate",
|
||||
"Alter Ego",
|
||||
"Explosive Impact",
|
||||
"Festive Spirit",
|
||||
"Freerunner",
|
||||
"Hawkeye",
|
||||
"Temblor",
|
||||
"Windsurf",
|
||||
"Juggle",
|
||||
"Roving Assassin",
|
||||
"Transcendence",
|
||||
"Fission",
|
||||
"Forest's Blessing",
|
||||
"Heart of the Pack",
|
||||
"Coagulate",
|
||||
"Guardian",
|
||||
"Hellfire",
|
||||
"Overwhelm",
|
||||
"Lunge",
|
||||
"Madness"
|
||||
],
|
||||
"filters": {
|
||||
"type": [
|
||||
"weapons",
|
||||
"armour",
|
||||
"accessories",
|
||||
"tomes",
|
||||
"charms",
|
||||
"tools",
|
||||
"ingredients",
|
||||
"materials"
|
||||
],
|
||||
"advanced": {
|
||||
"attackSpeed": [
|
||||
"super_slow",
|
||||
"very_slow",
|
||||
"slow",
|
||||
"normal",
|
||||
"fast",
|
||||
"very_fast",
|
||||
"super_fast"
|
||||
],
|
||||
"weapons": [
|
||||
"bow",
|
||||
"relik",
|
||||
"wand",
|
||||
"dagger",
|
||||
"spear"
|
||||
],
|
||||
"armour": [
|
||||
"helmet",
|
||||
"chestplate",
|
||||
"leggings",
|
||||
"boots"
|
||||
],
|
||||
"accessories": [
|
||||
"necklace",
|
||||
"ring",
|
||||
"bracelet"
|
||||
],
|
||||
"tomes": [
|
||||
"weaponTome",
|
||||
"armourTome",
|
||||
"slayingTome",
|
||||
"dungeonTome",
|
||||
"gatheringTome",
|
||||
"guildTome",
|
||||
"lootrunTome"
|
||||
],
|
||||
"tools": [
|
||||
"axe",
|
||||
"pickaxe",
|
||||
"rod",
|
||||
"scythe"
|
||||
],
|
||||
"crafting": [
|
||||
"alchemism",
|
||||
"armouring",
|
||||
"cooking",
|
||||
"jeweling",
|
||||
"scribing",
|
||||
"tailoring",
|
||||
"weaponsmithing",
|
||||
"woodworking"
|
||||
],
|
||||
"gathering": [
|
||||
"mining",
|
||||
"fishing",
|
||||
"farming",
|
||||
"woodcutting"
|
||||
]
|
||||
},
|
||||
"tier": {
|
||||
"items": [
|
||||
"common",
|
||||
"fabled",
|
||||
"legendary",
|
||||
"mythic",
|
||||
"rare",
|
||||
"set",
|
||||
"unique"
|
||||
],
|
||||
"ingredients": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
},
|
||||
"levelRange": {
|
||||
"items": 110,
|
||||
"ingredients": 105
|
||||
}
|
||||
}
|
||||
}
|
114
py_script/item_wrapper.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
"""
|
||||
Description: Quick item save/search with v3 item database
|
||||
API Documentation: https://documentation.wynncraft.com/docs/
|
||||
Update item db: python item_wrapper.py update-item [file_directory]
|
||||
Item search: python item_wrapper.py search -keyword [War] -itemType [mythic] ...
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
|
||||
|
||||
class Items:
|
||||
"""v3 item wrapping - Synchronous"""
|
||||
|
||||
def fetch(self, url):
|
||||
response = requests.get(url)
|
||||
return response.json()
|
||||
|
||||
def post(self, url, data=None):
|
||||
response = requests.post(url, json=data)
|
||||
return response.json()
|
||||
|
||||
def get_all_items(self):
|
||||
api_url = "https://api.wynncraft.com/v3/item/database?fullResult=True"
|
||||
return self.fetch(api_url)
|
||||
|
||||
def get_metadata(self):
|
||||
url = "https://api.wynncraft.com/v3/item/metadata"
|
||||
return self.fetch(url)
|
||||
|
||||
def item_query(self, data=None):
|
||||
api_url = "https://api.wynncraft.com/v3/item/search?fullResult=True"
|
||||
return self.post(api_url, data)
|
||||
|
||||
|
||||
def update_items(file_path):
|
||||
data = Items().get_all_items()
|
||||
update_file(data, file_path)
|
||||
print(f"{len(data)} items updated")
|
||||
|
||||
|
||||
def update_metadata(file_path):
|
||||
data = Items().get_metadata()
|
||||
update_file(data, file_path)
|
||||
print("Metadata updated")
|
||||
|
||||
|
||||
def update_file(input, output):
|
||||
try:
|
||||
with open(output, "w") as file:
|
||||
json.dump(input, file, indent=3)
|
||||
except Exception as error:
|
||||
print(f"File update error: {error}")
|
||||
|
||||
|
||||
def item_search_param(keyword=None, itemType=None, itemTier=None, atkSpeed=None, lvlRange=None, prof=None, ids=None, majorId=None):
|
||||
payload = {
|
||||
"query": [] if keyword is None else keyword,
|
||||
"type": [] if itemType is None else itemType,
|
||||
"tier": [] if itemTier is None else itemTier,
|
||||
"attackSpeed": [] if atkSpeed is None else atkSpeed,
|
||||
"levelRange": [] if lvlRange is None else lvlRange,
|
||||
"professions": [] if prof is None else prof,
|
||||
"identifications": [] if ids is None else ids,
|
||||
"majorIds": [] if majorId is None else majorId
|
||||
}
|
||||
try:
|
||||
response = Items().item_query(payload)
|
||||
print(json.dumps(response, indent=3))
|
||||
# Save the response as needed
|
||||
|
||||
except requests.RequestException as error:
|
||||
print(f"Request error: {error}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Wynncraft Item API Script')
|
||||
subparsers = parser.add_subparsers(dest='command', help='Pick your poison')
|
||||
update_items_parser = subparsers.add_parser('update-items', help='Update all items')
|
||||
update_items_parser.add_argument('file', help='File path for saving item json')
|
||||
update_metadata_parser = subparsers.add_parser('update-metadata', help='Update metadata')
|
||||
update_metadata_parser.add_argument('file', help='File path for saving metadata json')
|
||||
|
||||
search_parser = subparsers.add_parser('search', help='Search for items with parameters')
|
||||
search_parser.add_argument('-keyword', type=str, default=None, help='Keyword for item search')
|
||||
search_parser.add_argument('-itemType', type=str, default=None, help='Item type: wand, bow, etc')
|
||||
search_parser.add_argument('-itemTier', type=str, default=None, help='Item tier: mythic, legendary, etc')
|
||||
search_parser.add_argument('-atkSpeed', type=str, default=None, help='Attack speed param')
|
||||
search_parser.add_argument('-lvlRange', nargs=2, type=int, default=None, help='Level range for: min, max')
|
||||
search_parser.add_argument('-prof', type=str, default=None, help='Professions (Ing)')
|
||||
search_parser.add_argument('-ids', type=str, default=None, help='Identifications field')
|
||||
search_parser.add_argument('-majorId', type=str, default=None, help='Major IDs')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == 'update-items':
|
||||
update_items(args.file)
|
||||
elif args.command == 'update-metadata':
|
||||
update_metadata(args.file)
|
||||
elif args.command == 'search':
|
||||
item_search_param(
|
||||
keyword=args.keyword,
|
||||
itemType=args.itemType,
|
||||
itemTier=args.itemTier,
|
||||
atkSpeed=args.atkSpeed,
|
||||
lvlRange=args.lvlRange,
|
||||
prof=args.prof,
|
||||
ids=args.ids,
|
||||
majorId=args.majorId
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,140 +1,6 @@
|
|||
translate_mappings = {
|
||||
#"name": "name",
|
||||
#"displayName": "displayName",
|
||||
#"tier": "tier",
|
||||
#"set": "set",
|
||||
"sockets": "slots",
|
||||
#"type": "type",
|
||||
#"armorType": "armorType", (deleted)
|
||||
"armorColor": "color", #(deleted)
|
||||
"addedLore": "lore", #(deleted)
|
||||
#"material": "material", (deleted)
|
||||
"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",
|
||||
#"bonusFireDamage": "fDamPct",
|
||||
#"bonusWaterDamage": "wDamPct",
|
||||
#"bonusAirDamage": "aDamPct",
|
||||
#"bonusThunderDamage": "tDamPct",
|
||||
#"bonusEarthDamage": "eDamPct",
|
||||
"fireDamageBonus": "fDamPct",
|
||||
"waterDamageBonus": "wDamPct",
|
||||
"airDamageBonus": "aDamPct",
|
||||
"thunderDamageBonus": "tDamPct",
|
||||
"earthDamageBonus": "eDamPct",
|
||||
"elementalDamageBonus": "rDamPct",
|
||||
"fireDamageBonusRaw": "fDamRaw",
|
||||
"waterDamageBonusRaw": "wDamRaw",
|
||||
"airDamageBonusRaw": "aDamRaw",
|
||||
"thunderDamageBonusRaw": "tDamRaw",
|
||||
"earthDamageBonusRaw": "eDamRaw",
|
||||
"elementalDamageBonusRaw": "rDamRaw",
|
||||
"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",
|
||||
}
|
||||
import json
|
||||
with open("translate_mappings.json", 'r') as infile:
|
||||
translate_mappings = json.load(infile)
|
||||
|
||||
delete_keys = [
|
||||
#"addedLore",
|
||||
|
@ -143,3 +9,36 @@ delete_keys = [
|
|||
#"armorColor",
|
||||
#"material"
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
# SELF TEST: compare dict keys with `item_metadata.json`.
|
||||
from item_wrapper import Items
|
||||
local_metadata_file = "item_metadata.json"
|
||||
|
||||
def debug(*args, **kwargs):
|
||||
print(*args, **kwargs)
|
||||
|
||||
try:
|
||||
debug("updating item metadata...")
|
||||
metadata_check = Items().get_metadata()
|
||||
with open(local_metadata_file, 'w') as outfile:
|
||||
json.dump(metadata_check, outfile, indent=2)
|
||||
except:
|
||||
debug("Could not update item metadata. using local wynn metadata")
|
||||
with open(local_metadata_file, 'r') as infile:
|
||||
metadata_check = json.load(infile)
|
||||
|
||||
checklist = set(x for x in translate_mappings['identifications'].keys())
|
||||
debug(f"Checking {len(checklist)} identifications")
|
||||
n = 0
|
||||
for identification in metadata_check['identifications']:
|
||||
if identification in checklist:
|
||||
checklist.remove(identification)
|
||||
else:
|
||||
print(f"WARNING: id not accounted for: {identification}")
|
||||
n += 1
|
||||
debug(f"{n} unmapped API identifications.")
|
||||
|
||||
for identification in checklist:
|
||||
print(f"WARNING: unused translate map entry {identification}")
|
||||
debug(f"{len(checklist)} unused translation entries.")
|
||||
|
|
|
@ -187,7 +187,7 @@ for ing in ings:
|
|||
"notTouching": 0
|
||||
}
|
||||
if 'itemIDs' not in ing:
|
||||
ing['consumableIDs'] = {
|
||||
ing['itemIDs'] = {
|
||||
"dura": 0,
|
||||
"strReq": 0,
|
||||
"dexReq": 0,
|
||||
|
|
|
@ -15,15 +15,16 @@ import sys
|
|||
import os
|
||||
import base64
|
||||
import argparse
|
||||
from items_common import translate_mappings, delete_keys
|
||||
#from items_common import translate_mappings, delete_keys
|
||||
|
||||
parser = argparse.ArgumentParser(description="Process raw pulled item data.")
|
||||
parser.add_argument('infile', help='input file to read data from')
|
||||
parser.add_argument('infile', help='input file to read data from', default=None, nargs='?')
|
||||
parser.add_argument('outfile', help='output file to dump clean data into')
|
||||
args = parser.parse_args()
|
||||
infile, outfile = args.infile, args.outfile
|
||||
if args.infile is None:
|
||||
print("Grabbing json data from wynn api")
|
||||
|
||||
with open(infile, "r") as in_file:
|
||||
with open(args.infile, "r") as in_file:
|
||||
data = json.loads(in_file.read())
|
||||
|
||||
|
||||
|
@ -65,8 +66,9 @@ for item in items:
|
|||
|
||||
for k, v in translate_mappings.items():
|
||||
if k in item:
|
||||
item[v] = item[k]
|
||||
tmp = item[k]
|
||||
del item[k]
|
||||
item[v] = tmp
|
||||
|
||||
if not (item["name"] in id_map):
|
||||
while max_id in used_ids:
|
||||
|
|
212
py_script/translate_mappings.json
Normal file
|
@ -0,0 +1,212 @@
|
|||
{
|
||||
"tome": {
|
||||
"name": "displayName",
|
||||
"internalName": "name",
|
||||
"tier": "CAPS;tier",
|
||||
"material": "DELETE;",
|
||||
"base": "UNWRAP;tome.base",
|
||||
"dropMeta": "DELETE;",
|
||||
"identifications": "UNWRAP;identifications",
|
||||
"requirements": "UNWRAP;requirements",
|
||||
"raidReward": "DELETE;",
|
||||
"dropRestriction": "drop",
|
||||
"restrictions": "restrict",
|
||||
"tomeType": "type",
|
||||
"tomeVariant": "DELETE;"
|
||||
},
|
||||
"tome.base": {
|
||||
"gatheringXP": "tomeGatherXP",
|
||||
"slayingXP": "tomeXP",
|
||||
"dungeonXP": "tomeDungeonXP",
|
||||
"defenceToMobs": "defMobs",
|
||||
"damageToMobs": "damMobs"
|
||||
},
|
||||
"ingredient": {
|
||||
"name": "displayName",
|
||||
"internalName": "name",
|
||||
"tier": "tier",
|
||||
"material": "DELETE;",
|
||||
"skin": "DELETE;",
|
||||
"itemOnlyIDs": "RECURSE_ingredient.itemIDs;itemIDs",
|
||||
"consumableOnlyIDs": "RECURSE_ingredient.consumableIDs;consumableIDs",
|
||||
"ingredientPositionModifiers": "RECURSE_ingredient.posMods;posMods",
|
||||
"identifications": "RECURSE_identifications;ids",
|
||||
"droppedBy": "DELETE;",
|
||||
"___droppedBy_comment:": "Deleting for now because it is way too large and we cannot use it in a meaningful way. But this is nice data for when wynnatlas ever gets updated",
|
||||
"requirements": "UNWRAP;requirements"
|
||||
},
|
||||
"ingredient.itemIDs": {
|
||||
"durabilityModifier": "dura",
|
||||
"strengthRequirement": "strReq",
|
||||
"dexterityRequirement": "dexReq",
|
||||
"intelligenceRequirement": "intReq",
|
||||
"defenceRequirement": "defReq",
|
||||
"agilityRequirement": "agiReq",
|
||||
"attackSpeedModifier": "atkSpdMod",
|
||||
"powderSlotModifier": "slotMod"
|
||||
},
|
||||
"ingredient.consumableIDs": {
|
||||
"duration": "dura",
|
||||
"charges": "charges"
|
||||
},
|
||||
"ingredient.posMods": {
|
||||
"left": "left",
|
||||
"right": "right",
|
||||
"above": "above",
|
||||
"under": "under",
|
||||
"touching": "touching",
|
||||
"notTouching": "notTouching"
|
||||
},
|
||||
"item": {
|
||||
"name": "displayName",
|
||||
"internalName": "name",
|
||||
"tier": "CAPS;tier",
|
||||
"set": "set",
|
||||
"powderSlots": "slots",
|
||||
"type": "type",
|
||||
"armorType": "armorType",
|
||||
"material": "material",
|
||||
"dropRestriction": "drop",
|
||||
"dropMeta": "dropInfo",
|
||||
"quest": "quest",
|
||||
"restrictions": "restrict",
|
||||
"accessoryType": "type",
|
||||
"identified": "fixID",
|
||||
"skin": "skin",
|
||||
"category": "category",
|
||||
"attackSpeed": "ALLCAPS;atkSpd",
|
||||
"base": "UNWRAP;item.base",
|
||||
"requirements": "UNWRAP;requirements",
|
||||
"identifications": "UNWRAP;identifications"
|
||||
},
|
||||
"item.base": {
|
||||
"damage": "STR_RANGE;nDam",
|
||||
"fireDamage": "STR_RANGE;fDam",
|
||||
"waterDamage": "STR_RANGE;wDam",
|
||||
"airDamage": "STR_RANGE;aDam",
|
||||
"thunderDamage": "STR_RANGE;tDam",
|
||||
"earthDamage": "STR_RANGE;eDam",
|
||||
"health": "hp",
|
||||
"fireDefence": "fDef",
|
||||
"waterDefence": "wDef",
|
||||
"airDefence": "aDef",
|
||||
"thunderDefence": "tDef",
|
||||
"earthDefence": "eDef",
|
||||
"averageDPS": "DELETE;"
|
||||
},
|
||||
"requirements": {
|
||||
"level": "lvl",
|
||||
"levelRange": "lvRange",
|
||||
"classRequirement": "classReq",
|
||||
"strength": "strReq",
|
||||
"dexterity": "dexReq",
|
||||
"intelligence": "intReq",
|
||||
"agility": "agiReq",
|
||||
"defence": "defReq",
|
||||
"tomeSeeking": "DELETE;"
|
||||
},
|
||||
"identifications": {
|
||||
"healthRegen": "hprPct",
|
||||
"manaRegen": "mr",
|
||||
"spellDamage": "sdPct",
|
||||
"elementalSpellDamage": "rSdPct",
|
||||
"neutralSpellDamage": "nSdPct",
|
||||
"fireSpellDamage": "fSdPct",
|
||||
"waterSpellDamage": "wSdPct",
|
||||
"airSpellDamage": "aSdPct",
|
||||
"thunderSpellDamage": "tSdPct",
|
||||
"earthSpellDamage": "eSdPct",
|
||||
"mainAttackDamage": "mdPct",
|
||||
"elementalMainAttackDamage": "rMdPct",
|
||||
"neutralMainAttackDamage": "nMdPct",
|
||||
"fireMainAttackDamage": "fMdPct",
|
||||
"waterMainAttackDamage": "wMdPct",
|
||||
"airMainAttackDamage": "aMdPct",
|
||||
"thunderMainAttackDamage": "tMdPct",
|
||||
"earthMainAttackDamage": "eMdPct",
|
||||
"lifeSteal": "ls",
|
||||
"manaSteal": "ms",
|
||||
"xpBonus": "xpb",
|
||||
"lootBonus": "lb",
|
||||
"leveledXpBonus": "lxpb",
|
||||
"leveledLootBonus": "llb",
|
||||
"reflection": "ref",
|
||||
"rawStrength": "str",
|
||||
"rawDexterity": "dex",
|
||||
"rawIntelligence": "int",
|
||||
"rawDefence": "def",
|
||||
"rawAgility": "agi",
|
||||
"thorns": "thorns",
|
||||
"poison": "poison",
|
||||
"exploding": "expd",
|
||||
"walkSpeed": "spd",
|
||||
"rawAttackSpeed": "atkTier",
|
||||
"rawHealth": "hpBonus",
|
||||
"soulPointRegen": "spRegen",
|
||||
"stealing": "eSteal",
|
||||
"healthRegenRaw": "hprRaw",
|
||||
"rawSpellDamage": "sdRaw",
|
||||
"rawElementalSpellDamage": "rSdRaw",
|
||||
"rawNeutralSpellDamage": "nSdRaw",
|
||||
"rawFireSpellDamage": "fSdRaw",
|
||||
"rawWaterSpellDamage": "wSdRaw",
|
||||
"rawAirSpellDamage": "aSdRaw",
|
||||
"rawThunderSpellDamage": "tSdRaw",
|
||||
"rawEarthSpellDamage": "eSdRaw",
|
||||
"rawMainAttackDamage": "mdRaw",
|
||||
"rawElementalMainAttackDamage": "rMdRaw",
|
||||
"rawNeutralMainAttackDamage": "nMdRaw",
|
||||
"rawFireMainAttackDamage": "fMdRaw",
|
||||
"rawWaterMainAttackDamage": "wMdRaw",
|
||||
"rawAirMainAttackDamage": "aMdRaw",
|
||||
"rawThunderMainAttackDamage": "tMdRaw",
|
||||
"rawEarthMainAttackDamage": "eMdRaw",
|
||||
"damage": "damPct",
|
||||
"neutralDamage": "nDamPct",
|
||||
"fireDamage": "fDamPct",
|
||||
"waterDamage": "wDamPct",
|
||||
"airDamage": "aDamPct",
|
||||
"thunderDamage": "tDamPct",
|
||||
"earthDamage": "eDamPct",
|
||||
"elementalDamage": "rDamPct",
|
||||
"rawDamage": "damRaw",
|
||||
"rawNeutralDamage": "nDamRaw",
|
||||
"rawFireDamage": "fDamRaw",
|
||||
"rawWaterDamage": "wDamRaw",
|
||||
"rawAirDamage": "aDamRaw",
|
||||
"rawThunderDamage": "tDamRaw",
|
||||
"rawEarthDamage": "eDamRaw",
|
||||
"rawElementalDamage": "rDamRaw",
|
||||
"fireDefence": "fDefPct",
|
||||
"waterDefence": "wDefPct",
|
||||
"airDefence": "aDefPct",
|
||||
"thunderDefence": "tDefPct",
|
||||
"earthDefence": "eDefPct",
|
||||
"elementalDefence": "rDefPct",
|
||||
|
||||
"1stSpellCost": "spPct1",
|
||||
"raw1stSpellCost": "spRaw1",
|
||||
"2ndSpellCost": "spPct2",
|
||||
"raw2ndSpellCost": "spRaw2",
|
||||
"3rdSpellCost": "spPct3",
|
||||
"raw3rdSpellCost": "spRaw3",
|
||||
"4thSpellCost": "spPct4",
|
||||
"raw4thSpellCost": "spRaw4",
|
||||
|
||||
"sprint": "sprint",
|
||||
"sprintRegen": "sprintReg",
|
||||
"jumpHeight": "jh",
|
||||
"lootQuality": "lq",
|
||||
|
||||
"gatherXpBonus": "gXp",
|
||||
"gatherSpeed": "gSpd",
|
||||
|
||||
"healingEfficiency": "healPct",
|
||||
"knockback": "kb",
|
||||
"weakenEnemy": "weakenEnemy",
|
||||
"slowEnemy": "slowEnemy",
|
||||
"elementalDefense": "rDefPct",
|
||||
|
||||
"damageFromMobs": "selfWeakPct"
|
||||
}
|
||||
}
|
286
py_script/v3_process_items.py
Normal file
|
@ -0,0 +1,286 @@
|
|||
"""
|
||||
Used to process the raw item data pulled from the API.
|
||||
|
||||
Usage:
|
||||
- python process_items.py [infile] [outfile]
|
||||
OR
|
||||
- python process_items.py [infile and outfile]
|
||||
|
||||
|
||||
NOTE: id_map.json is due for change. Should be updated manually when Wynn2.0/corresponding WB version drops.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import base64
|
||||
import argparse
|
||||
from items_common import translate_mappings
|
||||
|
||||
parser = argparse.ArgumentParser(description="Process raw pulled item data.")
|
||||
parser.add_argument('infile', help='input file to read data from', default=None, nargs='?')
|
||||
#parser.add_argument('outfile', help='output file to dump clean data into')
|
||||
args = parser.parse_args()
|
||||
if args.infile is None:
|
||||
print("Grabbing json data from wynn api")
|
||||
from item_wrapper import Items
|
||||
api_data = Items().get_all_items()
|
||||
json.dump(api_data, open('dump.json', 'w'))
|
||||
else:
|
||||
with open(args.infile, "r") as in_file:
|
||||
api_data = json.load(in_file)
|
||||
|
||||
def translate_single_item(key, entry, name, directives, accumulate):
|
||||
ret = entry
|
||||
try:
|
||||
if 'min' in entry and 'max' in entry:
|
||||
if 'raw' in entry:
|
||||
ret = entry['raw']
|
||||
else:
|
||||
ret = [entry['min'], entry['max']]
|
||||
except:
|
||||
pass
|
||||
|
||||
i = 0
|
||||
while i < len(directives):
|
||||
directive = directives[i]
|
||||
if directive == 'DELETE':
|
||||
ret = None
|
||||
elif directive == 'CAPS':
|
||||
ret = ret[0].upper() + ret[1:]
|
||||
elif directive == 'ALLCAPS':
|
||||
ret = ret.upper()
|
||||
elif directive == 'STR_RANGE':
|
||||
if 'min' in entry and 'max' in entry:
|
||||
ret = f"{entry['min']}-{entry['max']}"
|
||||
elif directive == 'UNWRAP':
|
||||
recursive_translate(entry, accumulate, name, translate_single_item)
|
||||
ret = None
|
||||
i += 1
|
||||
return ret
|
||||
|
||||
def translate_single_ing(key, entry, name, directives, accumulate):
|
||||
ret = entry
|
||||
try:
|
||||
if 'min' in entry and 'max' in entry:
|
||||
ret = {
|
||||
'minimum': entry['min'],
|
||||
'maximum': entry['max']
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
i = 0
|
||||
while i < len(directives):
|
||||
directive = directives[i]
|
||||
if directive == 'DELETE':
|
||||
ret = None
|
||||
elif directive == 'UNWRAP':
|
||||
recursive_translate(entry, accumulate, name, translate_single_ing)
|
||||
ret = None
|
||||
elif directive[:8] == 'RECURSE_':
|
||||
ret = recursive_translate(entry, {}, directive[8:], translate_single_ing)
|
||||
i += 1
|
||||
return ret
|
||||
|
||||
def recursive_translate(entry, result, path, translate_single):
|
||||
mapping = translate_mappings[path]
|
||||
|
||||
for k, v in entry.items():
|
||||
# Translate the item.
|
||||
if k in mapping:
|
||||
tmp = mapping[k].split(';')
|
||||
directives, translated_name = tmp[:-1], tmp[-1]
|
||||
res = translate_single(k, v, translated_name, directives, result)
|
||||
if res is not None:
|
||||
result[translated_name] = res
|
||||
continue
|
||||
|
||||
# pass it through unchanged.
|
||||
result[k] = v
|
||||
return result
|
||||
|
||||
armor_types = ['helmet', 'chestplate', 'leggings', 'boots']
|
||||
tome_type_translation = {
|
||||
'gatheringxp': 'gatherXpTome',
|
||||
'dungeonxp': 'dungeonXpTome',
|
||||
'slayingxp': 'mobXpTome',
|
||||
'guildtome': 'guildTome',
|
||||
'mobdefence': 'armorTome',
|
||||
'mobdamage': 'weaponTome',
|
||||
'lootrun': 'lootrunTome',
|
||||
}
|
||||
|
||||
def translate_entry(entry):
|
||||
"""
|
||||
Convert an api entry into an appropriate parsed item.
|
||||
|
||||
Returns a pair: (converted, type)
|
||||
where `type` is "item", "ingredient", "tome", "material", "charm", or None
|
||||
and converted might be None if the conversion failed.
|
||||
"""
|
||||
# sketchily infer what kind of item we're dealing with, and translate it appropriately.
|
||||
if "type" in entry:
|
||||
# only items have this field.
|
||||
res = recursive_translate(entry, {}, "item", translate_single_item)
|
||||
if res['type'] in armor_types:
|
||||
res['category'] = 'armor'
|
||||
else:
|
||||
res['category'] = 'weapon'
|
||||
for element in 'netwfa':
|
||||
damage_key = element + 'Dam'
|
||||
if damage_key not in res:
|
||||
res[damage_key] = '0-0'
|
||||
return res, 'item'
|
||||
if "accessoryType" in entry:
|
||||
# only accessories have this field.
|
||||
return recursive_translate(entry, {'category': 'accessory'}, "item", translate_single_item), "item"
|
||||
if "itemOnlyIDs" in entry:
|
||||
# only ingredients have this field.
|
||||
res = recursive_translate(entry, {}, "ingredient", translate_single_ing)
|
||||
return res, "ingredient"
|
||||
#return recursive_translate(entry, {}, "ing"), "ingredient"
|
||||
if "tomeType" in entry:
|
||||
# only tomes have this field.
|
||||
print(entry)
|
||||
res = recursive_translate(entry, {}, "tome", translate_single_item)
|
||||
res['category'] = 'tome'
|
||||
res['fixID'] = False
|
||||
res['type'] = tome_type_translation[res['type']]
|
||||
print(res)
|
||||
return res, "tome"
|
||||
if "craftable" in entry:
|
||||
return None, "material"
|
||||
|
||||
# I think the only things left are charms, we just don't classify them.
|
||||
return None, None
|
||||
|
||||
with open("id_map.json", "r") as id_map_file:
|
||||
id_map = json.load(id_map_file)
|
||||
used_ids = set([v for k, v in id_map.items()])
|
||||
max_id = 0
|
||||
|
||||
with open("ing_map.json","r") as ing_map_file:
|
||||
ing_map = json.load(ing_map_file)
|
||||
with open("../tome_map.json","r") as tome_map_file:
|
||||
tome_map = json.load(tome_map_file)
|
||||
|
||||
items = []
|
||||
ingreds = []
|
||||
tomes = []
|
||||
for name, entry in api_data.items():
|
||||
entry['name'] = name
|
||||
res, entry_type = translate_entry(entry)
|
||||
print(f"Parsed {name}, type {entry_type}")
|
||||
if res is None:
|
||||
continue
|
||||
# TODO: make this a map or smth less ugly code
|
||||
if entry_type == 'item':
|
||||
items.append(res)
|
||||
elif entry_type == 'ingredient':
|
||||
ingreds.append(res)
|
||||
elif entry_type == 'tome':
|
||||
tomes.append(res)
|
||||
|
||||
with open("../clean.json", "r") as oldfile:
|
||||
old_data = json.load(oldfile)
|
||||
old_items = old_data['items']
|
||||
with open("../ingreds_clean.json", "r") as ingfile:
|
||||
old_ingreds = json.load(ingfile)
|
||||
with open("../tomes.json", "r") as tomefile:
|
||||
old_tome_data = json.load(tomefile)
|
||||
old_tomes = old_tome_data['tomes']
|
||||
|
||||
known_item_names = set()
|
||||
known_ingred_names = set()
|
||||
known_tome_names = set()
|
||||
for item in items:
|
||||
known_item_names.add(item["name"])
|
||||
for ingred in ingreds:
|
||||
known_ingred_names.add(ingred["name"])
|
||||
for tome in tomes:
|
||||
known_tome_names.add(tome["name"])
|
||||
|
||||
tome_value_map = {}
|
||||
for item in old_items:
|
||||
if item["name"] not in known_item_names:
|
||||
print(f'Unknown old item: {item["name"]}!!!')
|
||||
for ingred in old_ingreds:
|
||||
if ingred["name"] not in known_ingred_names:
|
||||
print(f'Unknown old ingred: {ingred["name"]}!!!')
|
||||
for tome in old_tomes:
|
||||
if tome["name"] not in known_tome_names:
|
||||
print(f'Unknown old tome: {tome["name"]}!!!')
|
||||
tome_value_map[tome['name']] = tome
|
||||
|
||||
# TODO hack pull the major id file
|
||||
major_ids_filename = "../js/builder/major_ids_clean.json"
|
||||
with open(major_ids_filename, 'r') as major_ids_file:
|
||||
major_ids_map = json.load(major_ids_file)
|
||||
major_ids_reverse_map = { v['displayName'] : k for k, v in major_ids_map.items() }
|
||||
|
||||
for item in items:
|
||||
# NOTE: HACKY ITEM FIXES!
|
||||
if 'majorIds' in item:
|
||||
item['majorIds'] = [ major_ids_reverse_map[item['majorIds']['name']] ]
|
||||
if item['tier'] == 'Common':
|
||||
item['tier'] = 'Normal'
|
||||
|
||||
if not (item["name"] in id_map):
|
||||
while max_id in used_ids:
|
||||
max_id += 1
|
||||
used_ids.add(max_id)
|
||||
id_map[item["name"]] = max_id
|
||||
print(f'New item: {item["name"]} (id: {max_id})')
|
||||
item["id"] = id_map[item["name"]]
|
||||
|
||||
for ingred in ingreds:
|
||||
# HACKY ING FIXES!
|
||||
ingred['itemIDs']['dura'] = int(ingred['itemIDs']['dura'] / 1000)
|
||||
ingred['skills'] = [x.upper() for x in ingred['skills']]
|
||||
if 'ids' not in ingred:
|
||||
ingred['ids'] = dict()
|
||||
print(f"ing missing 'ids': {ingred['name']}")
|
||||
if 'consumableIDs' not in ingred:
|
||||
ingred['consumableIDs'] = {'dura': 0, 'charges': 0}
|
||||
print(f"ing missing 'consumableIDs': {ingred['name']}")
|
||||
|
||||
if not (ingred["name"] in ing_map):
|
||||
new_id = len(ing_map)
|
||||
ing_map[ingred["name"]] = new_id
|
||||
print(f'New ingred: {ingred["name"]} (id: {new_id})')
|
||||
ingred["id"] = ing_map[ingred["name"]]
|
||||
|
||||
for tome in tomes:
|
||||
if not (tome['name'] in tome_map):
|
||||
new_id = len(tome_map)
|
||||
tome_map[tome['name']] = new_id
|
||||
print(f'New tome: {tome["name"]} (id: {new_id})')
|
||||
tome['alias'] = 'NO_ALIAS'
|
||||
else:
|
||||
old_tome = tome_value_map[tome['name']]
|
||||
if 'alias' in old_tome:
|
||||
tome['alias'] = old_tome['alias']
|
||||
tome['id'] = tome_map[tome['name']]
|
||||
|
||||
#write items back into data
|
||||
old_data["items"] = items
|
||||
|
||||
#save id map
|
||||
with open("id_map.json","w") as id_map_file:
|
||||
json.dump(id_map, id_map_file, indent=2)
|
||||
with open("ing_map.json","w") as ing_map_file:
|
||||
json.dump(ing_map, ing_map_file, indent=2)
|
||||
with open("../tome_map.json","w") as tome_map_file:
|
||||
json.dump(tome_map, tome_map_file, indent=2)
|
||||
|
||||
|
||||
#write the data back to the outfile
|
||||
with open('item_out.json', "w+") as out_file:
|
||||
json.dump(old_data, out_file, ensure_ascii=False, separators=(',', ':'))
|
||||
|
||||
with open('ing_out.json', "w+") as out_file:
|
||||
json.dump(ingreds, out_file, ensure_ascii=False, separators=(',', ':'))
|
||||
|
||||
with open('tome_out.json', "w+") as out_file:
|
||||
json.dump({'tomes': tomes}, out_file, ensure_ascii=False, separators=(',', ':'))
|
0
regression_tests/v6.png
Executable file → Normal file
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
0
regression_tests/v7.png
Executable file → Normal file
Before Width: | Height: | Size: 304 KiB After Width: | Height: | Size: 304 KiB |
|
@ -60,6 +60,9 @@
|
|||
"Harvester's Tome of Weapon Mastery I": 58,
|
||||
"Harvester's Tome of Weapon Mastery II": 59,
|
||||
"Harvester's Tome of Weapon Mastery III": 60,
|
||||
"No Weapon Tome": 61,
|
||||
"No Armor": 62,
|
||||
"No Guild Tome": 63,
|
||||
"Earthbound Tome of Weapon Mastery III": 64,
|
||||
"Nimble Tome of Weapon Mastery III": 65,
|
||||
"Mystical Tome of Weapon Mastery III": 66,
|
||||
|
@ -71,5 +74,23 @@
|
|||
"Abyssal Tome of Weapon Mastery III": 72,
|
||||
"Infernal Tome of Weapon Mastery III": 73,
|
||||
"Cyclonic Tome of Weapon Mastery III": 74,
|
||||
"Astral Tome of Weapon Mastery III": 75
|
||||
}
|
||||
"Astral Tome of Weapon Mastery III": 75,
|
||||
"Tome of Gathering Mastery III": 76,
|
||||
"Mastermind's Tome of Lootrun Mastery": 77,
|
||||
"Tome of Armour Mastery I": 78,
|
||||
"Pickpocket's Tome of Lootrun Mastery": 79,
|
||||
"Tome of Slaying Mastery III": 80,
|
||||
"Tome of Slaying Mastery I": 81,
|
||||
"Tome of Gathering Mastery I": 82,
|
||||
"Scoundrel's Tome of Lootrun Mastery": 83,
|
||||
"Spelunker's Tome of Lootrun Mastery": 84,
|
||||
"Tome of Slaying Mastery II": 85,
|
||||
"Tome of Dungeoneering Mastery II": 86,
|
||||
"Tome of Gathering Mastery II": 87,
|
||||
"Tome of Dungeoneering Mastery I": 88,
|
||||
"Manipulator's Tome of Lootrun Mastery": 89,
|
||||
"Plunderer's Tome of Lootrun Mastery": 90,
|
||||
"Thief's Tome of Lootrun Mastery": 91,
|
||||
"Tome of Dungeoneering Mastery III": 92,
|
||||
"No Lootrun Tome": 93
|
||||
}
|
2162
tomes.json
|
@ -29,6 +29,8 @@
|
|||
<a href = ""><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">
|
||||
|
@ -68,6 +70,14 @@
|
|||
<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>
|
||||
|
|