merge conflict

This commit is contained in:
ferricles 2022-12-20 11:11:30 -08:00
commit 7b295e4e46
198 changed files with 55183 additions and 39157 deletions

View file

@ -1,7 +1,7 @@
# WynnBuilder # WynnBuilder
Wynncraft class building calculator & general utility site Wynncraft class building calculator & general utility site
![builder screenshot](https://user-images.githubusercontent.com/16601189/111013491-9cc8a080-836d-11eb-86fc-2e6413d0e493.png) ![Builder Screenshot](https://user-images.githubusercontent.com/110062564/192047798-d8583fe1-b188-4bc4-85a9-0eecbf10aeef.PNG)
## Features ## Features
@ -14,6 +14,9 @@ Takes a build's items as input and returns all relevant information
- Skillpoints required & left over - Skillpoints required & left over
- Defensive stats (EHP, EleDefs, etc.) - Defensive stats (EHP, EleDefs, etc.)
It also features an ability tree!
![wynnbuilder ability tree](https://user-images.githubusercontent.com/110062564/192048561-2ec91ba7-1793-4d4f-b4d5-6d7c05cfae99.PNG)
Boosts and Powder specials Boosts and Powder specials
- Spell boosts, such as Vanish and War Scream - Spell boosts, such as Vanish and War Scream
- Powder special buffs - Powder special buffs
@ -22,12 +25,18 @@ Boosts and Powder specials
and more... and more...
### WynnCrafter ### WynnCrafter
![wynncrafter screenshot](https://user-images.githubusercontent.com/110062564/192048366-5112d334-f44b-4853-b337-4184628e505e.PNG)
Crafting recipe calculator Crafting recipe calculator
### WynnAtlas
Fully featured item search ### WynnAtlas
![wynnatlas screenshot](https://user-images.githubusercontent.com/110062564/192048258-23bc0dd7-b417-4c0c-9437-4392315bf85d.PNG)
Fully featured item search!
Use different filters based on:
- Name
- Rarity
- IDs
And more, to find what item suits your build best!
### WynnCustom ### WynnCustom
@ -42,4 +51,4 @@ Custom item creator
## Documentation ## Documentation
ahhhhhhhhhhh ahhhhhhhhhhh

View file

@ -36,20 +36,71 @@
<hr/> <hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a> <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>
<div class="container-fluid me-4" style="max-width: 95%; display: none"> <div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class="container-fluid overall-box mt-lg-2" style="margin-top: 6vh; display: none">
<!-- REMOVE THIS DIV AT SOME POINT. --> <!-- REMOVE THIS DIV AT SOME POINT. -->
<div class = "row scaled-font mx-auto" id = "discord-banner-dev"> <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 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>
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3"> <div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3 gx-0">
<div class="col-xl-6"> <div class="col-xl-6">
<div class="row row-cols-1 mb-3 gy-4"> <div class="row my-2 dark-6 rounded text-center g-0 px-3 d-flex d-lg-none">
<div class="col"> <div class="col">
<div class="row row-cols-1 row-cols-xl-2 rounded gy-4 gx-5 justify-content-center"> <p class="fake-button scaled-font mb-0 selected-btn" id="equipment-inputs-btn" onclick="show_tab('equipment-inputs', ['equipment-inputs', 'adjust-id'])">Equipments</p>
<div class="col-auto rounded order-xl-0 order-0"> </div>
<div class="row h-100 dark-shadow dark-6 rounded" id="helmet-dropdown"> <div class="col">
<p class="fake-button scaled-font mb-0" id="adjust-id-btn" onclick="show_tab('adjust-id', ['equipment-inputs', 'adjust-id'])">Ability Tree</p>
</div>
</div>
<div class="row row-cols-1 mb-3 gy-4" id="equipment-inputs">
<div class="col">
<div class="row row-cols-1 row-cols-xl-2 dark-shadow dark-6 justify-content-center equipment-input rounded gy-3 gy-lg-0 mt-auto">
<div class="col rounded order-xl-0 order-0 my-0">
<div class="row h-100 px-1" id="helmet-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="helmet-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="helmet-img-loc">
<img id="helmet-img" class="img-fluid rounded" src="../media/items/new/generic-helmet.png"> <div id="helmet-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 45.45454545454546% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -73,10 +124,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1 my-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="ring1-dropdown"> <div class="row h-100 px-1" id="ring1-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring1-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring1-img-loc">
<img id="ring1-img" class="img-fluid rounded" src="../media/items/new/generic-ring.png"> <div id="ring1-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 81.81818181818181% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -94,16 +145,15 @@
<input class="equipment-input text-light form-control" id="ring1-choice" name="ring1-choice" placeholder="No ring" value="" tabindex="2"/> <input class="equipment-input text-light form-control" id="ring1-choice" name="ring1-choice" placeholder="No ring" value="" tabindex="2"/>
</div> </div>
<div class="col d-flex justify-content-end" style="height: 100%;"> <div class="col d-flex justify-content-end" style="height: 100%;">
<!-- <input class="equipment-input text-light form-control" type="text" id="ring1-powder" name="ring1-powder" placeholder="no powders" tabindex="2"/> -->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-0"> <div class="col order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="chestplate-dropdown"> <div class="row h-100 px-1" id="chestplate-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="chestplate-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="chestplate-img-loc">
<img id="chestplate-img" class="img-fluid rounded" src="../media/items/new/generic-chestplate.png"> <div id="chestplate-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 54.54545454545454% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -127,10 +177,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="ring2-dropdown"> <div class="row h-100 px-1" id="ring2-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring2-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring2-img-loc">
<img id="ring2-img" class="img-fluid rounded" src="../media/items/new/generic-ring.png"> <div id="ring2-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 81.81818181818181% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -153,10 +203,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-0"> <div class="col order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="leggings-dropdown"> <div class="row h-100 px-1" id="leggings-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="leggings-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="leggings-img-loc">
<img id="leggings-img" class="img-fluid rounded" src="../media/items/new/generic-leggings.png"> <div id="leggings-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 63.63636363636363% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -180,10 +230,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="bracelet-dropdown"> <div class="row h-100 px-1" id="bracelet-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="bracelet-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="bracelet-img-loc">
<img id="bracelet-img" class="img-fluid rounded" src="../media/items/new/generic-bracelet.png"> <div id="bracelet-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 90.90909090909092% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -207,10 +257,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-0"> <div class="col order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="boots-dropdown"> <div class="row h-100 px-1" id="boots-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="boots-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="boots-img-loc">
<img id="boots-img" class="img-fluid rounded" src="../media/items/new/generic-boots.png"> <div id="boots-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 72.72727272727272% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -234,10 +284,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="necklace-dropdown"> <div class="row h-100 px-1" id="necklace-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="necklace-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="necklace-img-loc">
<img id="necklace-img" class="img-fluid rounded" src="../media/items/new/generic-necklace.png"> <div id="necklace-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 100% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -260,10 +310,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id='weapon-dropdown'> <div class="row h-auto px-1" id='weapon-dropdown'>
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="weapon-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="weapon-img-loc">
<img id="weapon-img" class="img-fluid rounded" src="../media/items/new/generic-dagger.png"> <div id="weapon-img" class="img-fluid rounded item-display-new-toggleable" style = "background-image: url('../media/items/new.png');"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -287,29 +337,25 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1 level-input">
<div class="row h-100 dark-shadow dark-6 rounded"> <div class="row h-100 px-1">
<div class="col"> <div class="col">
<div class="row align-items-center"> <div class="row align-items-center justify-content-left">
<div class="col-3 text-nowrap fw-bold scaled-font"> <div class="col-auto text-nowrap fw-bold scaled-font">
Level: Level:
</div> </div>
<div class="col d-flex justify-content-end"> <div class="col d-flex px-1">
<input class="equipment-input text-light form-control form-control-sm" id="level-choice" name="level-choice" value="106" placeholder="Build level" value="" tabindex="2"/> <input class="equipment-input text-light form-control form-control-sm" id="level-choice" name="level-choice" value="106" placeholder="Build level" value="" tabindex="2"/>
</div> </div>
<div class="col-auto px-1 text-nowrap scaled-font"> <div class="col-auto px-1 scaled-font">
<button class="button fw-bold text-light dark-5 scaled-font rounded" id="reset-button" onclick="resetFields()">Reset</button> <button class="button py-0 fw-bold text-light dark-5 scaled-font rounded" id="reset-button" onclick="resetFields()">Reset</button>
</div> </div>
</div> </div>
<div class="row align-items-center justify-content-center my-1"> <div class="row align-items-left justify-content-left my-1">
<div class="row align-items-center"> <button class="col-auto mx-1 px-1 py-0 border-info text-light dark-5 scaled-font rounded fake-button"
<div class="col-auto px-1 text-nowrap scaled-font"> id=copy-button onclick="copyBuild()">Copy short</button>
<button class="border-dark text-light dark-5 scaled-font rounded" id=copy-button onclick="copyBuild()">Copy short</button> <button class="col-auto mx-1 px-1 py-0 border-info text-light dark-5 scaled-font rounded fake-button"
</div> id=share-button onclick="shareBuild(player_build)">Copy for sharing</button>
<div class="col-auto px-1 text-nowrap scaled-font">
<button class="border-dark text-light dark-5 scaled-font rounded" id=share-button onclick="shareBuild(player_build)">Copy for sharing</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -390,12 +436,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 text-center gx-2">
<button class = "button fw-bold text-light dark-5 rounded scaled-font" id = "optimize-strdex" onclick = "optimizeStrDex()">Optimize Str/Dex</button>
</div>
</div> </div>
</div> </div>
<div class="col text-center"> <div class="col text-center py-1">
<div id="summary-box"></div> <div id="summary-box"></div>
<div id="err-box"></div> <div id="err-box"></div>
<div id="stack-box"></div> <div id="stack-box"></div>
@ -407,188 +450,187 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col mb-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="row row-cols-1 justify-content-center">
<div class="col fw-bold dark-4 rounded-top">
Active boosts
</div>
<div class="col">
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="radiance-boost" onclick="update_radiance()">
Radiance
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
War Scream
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+30%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="totem-boost" onclick="update_boosts('totem-boost')">
Vengeful Spirit (+20%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="fortitude-boost" onclick="update_boosts('fortitude-boost')">
Fortitude (+60%)
</button>
</div>
<div class="col" id="boost-toggles">
</div>
</div>
<div class="row row-cols-1 justify-content-center" id="boost-sliders">
</div>
</div>
<div class="col mb-1">
<div class="row row-cols-1 rounded text-center dark-5 scaled-font">
<div class="row p-0 m-0 text-nowrap">
<div id = "str-boost-btn" class="col eDam dark-4u fake-button elem-boost" onclick="show_tab('str-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Earth
</div>
<div id = "dex-boost-btn" class="col tDam dark-4u fake-button elem-boost" onclick="show_tab('dex-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Thunder
</div>
<div id = "int-boost-btn" class="col wDam dark-4u fake-button elem-boost" onclick="show_tab('int-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Water
</div>
<div id = "def-boost-btn" class="col fDam dark-4u fake-button elem-boost" onclick="show_tab('def-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Fire
</div>
<div id = "agi-boost-btn" class="col aDam dark-4u fake-button elem-boost" onclick="show_tab('agi-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Air
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="str-boost" style="display: none;">
<div class="col eDam dark-5">
Quake (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1" >
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1')">
Lv.4 [e4e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2')">
Lv.4.5 [e5e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3')">
Lv.5 [e5e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4')">
Lv.5.5 [e6e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5')">
Lv.6 [e6e6]
</button>
</div>
<div class="col eDam">
Rage (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="dex-boost" style="display: none;">
<div class="col tDam dark-5">
Chain Lightning (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1')">
Lv.4 [t4t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2')">
Lv.4.5 [t5t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3')">
Lv.5 [t5t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4')">
Lv.5.5 [t6t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5')">
Lv.6 [t6t6]
</button>
</div>
<div class="col tDam">
Kill Streak (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="int-boost">
<div class="col wDam dark-5">
Curse (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1')">
Lv.4 [w4w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2')">
Lv.4.5 [w5w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3')">
Lv.5 [w5w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4')">
Lv.5.5 [w6w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5')">
Lv.6 [w6w6]
</button>
</div>
<div class="col wDam">
Concentration (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="def-boost" style="display: none;">
<div class="col fDam dark-5">
Courage (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1')">
Lv.4 [f4f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2')">
Lv.4.5 [f5f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3')">
Lv.5 [f5f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4')">
Lv.5.5 [f6f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5')">
Lv.6 [f6f6]
</button>
</div>
<div class="col fDam">
Endurance (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="agi-boost" style="display: none;">
<div class="col aDam dark-5">
Wind Prison (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-1" onclick = "updatePowderSpecials('Wind_Prison-1')">
Lv.4 [a4a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-2" onclick = "updatePowderSpecials('Wind_Prison-2')">
Lv.4.5 [a5a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-3" onclick = "updatePowderSpecials('Wind_Prison-3')">
Lv.5 [a5a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-4" onclick = "updatePowderSpecials('Wind_Prison-4')">
Lv.5.5 [a6a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-5" onclick = "updatePowderSpecials('Wind_Prison-5')">
Lv.6 [a6a6]
</button>
</div>
<div class="col aDam">
Dodge (Passive)
</div>
</div>
</div>
</div>
</div>
<div class="row row-cols-1 d-lg-flex" id="adjust-id" style="display: none;">
<div class="col"> <div class="col">
<div class="row row-cols-1 gy-4"> <div class="row row-cols-1 gy-3">
<div class="col mb-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="row row-cols-1 justify-content-center">
<div class="col fw-bold dark-4 rounded-top">
Active boosts
</div>
<div class="col">
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="vanish-boost" onclick="update_boosts('vanish-boost')">
Vanish (+80%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
War Scream
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+30%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="yourtotem-boost" onclick="update_boosts('yourtotem-boost')">
Your Totem (+35%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="allytotem-boost" onclick="update_boosts('allytotem-boost')">
Ally Totem (+15%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="fortitude-boost" onclick="update_boosts('fortitude-boost')">
Fortitude (+60%)
</button>
</div>
<div class="col" id="boost-toggles">
</div>
</div>
<div class="row row-cols-1 justify-content-center" id="boost-sliders">
</div>
</div>
<div class="col mb-1">
<div class="row row-cols-1 rounded text-center dark-5 scaled-font">
<div class="row p-0 m-0 text-nowrap">
<div id = "str-boost-tab" class="col eDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('str')">
Earth
</div>
<div id = "dex-boost-tab" class="col tDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('dex')">
Thunder
</div>
<div id = "int-boost-tab" class="col wDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('int')">
Water
</div>
<div id = "def-boost-tab" class="col fDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('def')">
Fire
</div>
<div id = "agi-boost-tab" class="col aDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('agi')">
Air
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="str-boost" style="display: none;">
<div class="col eDam dark-5">
Quake (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1" >
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1')">
Lv.4 [e4e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2')">
Lv.4.5 [e5e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3')">
Lv.5 [e5e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4')">
Lv.5.5 [e6e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5')">
Lv.6 [e6e6]
</button>
</div>
<div class="col eDam">
Rage (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="dex-boost" style="display: none;">
<div class="col tDam dark-5">
Chain Lightning (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1')">
Lv.4 [t4t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2')">
Lv.4.5 [t5t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3')">
Lv.5 [t5t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4')">
Lv.5.5 [t6t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5')">
Lv.6 [t6t6]
</button>
</div>
<div class="col tDam">
Kill Streak (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="int-boost">
<div class="col wDam dark-5">
Curse (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1')">
Lv.4 [w4w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2')">
Lv.4.5 [w5w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3')">
Lv.5 [w5w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4')">
Lv.5.5 [w6w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5')">
Lv.6 [w6w6]
</button>
</div>
<div class="col wDam">
Concentration (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="def-boost" style="display: none;">
<div class="col fDam dark-5">
Courage (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1')">
Lv.4 [f4f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2')">
Lv.4.5 [f5f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3')">
Lv.5 [f5f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4')">
Lv.5.5 [f6f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5')">
Lv.6 [f6f6]
</button>
</div>
<div class="col fDam">
Endurance (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="agi-boost" style="display: none;">
<div class="col aDam dark-5">
Wind Prison (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-1" onclick = "updatePowderSpecials('Wind_Prison-1')">
Lv.4 [a4a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-2" onclick = "updatePowderSpecials('Wind_Prison-2')">
Lv.4.5 [a5a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-3" onclick = "updatePowderSpecials('Wind_Prison-3')">
Lv.5 [a5a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-4" onclick = "updatePowderSpecials('Wind_Prison-4')">
Lv.5.5 [a6a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-5" onclick = "updatePowderSpecials('Wind_Prison-5')">
Lv.6 [a6a6]
</button>
</div>
<div class="col aDam">
Dodge (Passive)
</div>
</div>
</div>
</div>
<div class="col mb-1"> <div class="col mb-1">
<div class="row row-cols-1 row-cols-1 text-center scaled-font dark-5 rounded dark-shadow"> <div class="row row-cols-1 row-cols-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="col fw-bold dark-4 rounded-top"> <div class="col fw-bold dark-4 rounded-top">
@ -620,7 +662,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='weaponTome1-dropdown'> <div class="row h-100 dark-shadow rounded" id='weaponTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome1-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome1-img-loc">
<img id="weaponTome1-img" class="img-fluid rounded" src="../media/items/new/generic-weaponTome.png"> <div id="weaponTome1-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -644,7 +686,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='weaponTome2-dropdown'> <div class="row h-100 dark-shadow rounded" id='weaponTome2-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome2-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome2-img-loc">
<img id="weaponTome2-img" class="img-fluid rounded" src="../media/items/new/generic-weaponTome.png"> <div id="weaponTome2-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -668,7 +710,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome1-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome1-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome1-img-loc">
<img id="armorTome1-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome1-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -692,7 +734,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome2-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome2-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome2-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome2-img-loc">
<img id="armorTome2-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome2-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -716,7 +758,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome3-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome3-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome3-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome3-img-loc">
<img id="armorTome3-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome3-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -740,7 +782,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome4-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome4-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome4-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome4-img-loc">
<img id="armorTome4-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome4-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -764,7 +806,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='guildTome1-dropdown'> <div class="row h-100 dark-shadow rounded" id='guildTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="guildTome1-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="guildTome1-img-loc">
<img id="guildTome1-img" class="img-fluid rounded" src="../media/items/new/generic-guildTome.png"> <div id="guildTome1-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -789,14 +831,16 @@
</div> </div>
<div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;"> <div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;">
<div class="row row-cols-1 row-cols-xl-2"> <div class="row row-cols-1 row-cols-xl-2">
<div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 90vh; overflow-y: auto; overflow-x: hidden;"> <div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 90vh; overflow-y: auto;">
</div> </div>
<div class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs"> <div class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs">
<div class="col mx-auto" style="height: 2em; overflow-y: auto;" id="atree-header"> <div class="col mx-auto" style="height: 2em; overflow-y: auto;" id="atree-header">
</div> </div>
<div class="col mx-auto" style="overflow-y: auto;" id="atree-active"> <div class="col mx-auto" style="" id="atree-warning">
</div> </div>
<div class="col mx-auto" style="overflow-y: auto;" id="atree-active">
</div>
</div> </div>
</div> </div>
</div> </div>
@ -1131,24 +1175,20 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-xl-3 mb-3"> <div class="col-xl-3 mb-3 order-2 order-lg-0">
<div class="row row-cols-1 rounded dark-shadow dark-6 scaled-font"> <div class="row row-cols-1 rounded dark-shadow dark-6 scaled-font">
<div class="col rounded-top"> <div class="col rounded-top">
<div class="row"> <div class="row">
<div id="tab-offensive-btn" class="col-4 text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7" onclick="show_tab('offensive-stats')"> <div id="detailed-stats-btn" class="col text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7" onclick="show_tab('detailed-stats', ['detailed-stats', 'summary-stats'])">
Offense Detailed
</div> </div>
<div id="tab-defensive-btn" class="col-4 text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7" onclick="show_tab('defensive-stats')"> <div id="summary-stats-btn" class="col text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7 selected-btn" onclick="show_tab('summary-stats', ['detailed-stats', 'summary-stats'])">
Defense Summary
</div>
<div id="tab-overall-btn" class="col-4 text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7 selected-btn" onclick="show_tab('overall-stats')">
Overall
</div> </div>
</div> </div>
</div> </div>
<div style="display: none;" id="offensive-stats" class="col text-nowrap"></div> <div style="display: none;" id="detailed-stats" class="col text-nowrap"></div>
<div style="display: none;" id="defensive-stats" class="col text-nowrap"></div> <div id="summary-stats" class="col text-nowrap"></div>
<div id="overall-stats" class="col text-nowrap"></div>
</div> </div>
</div> </div>
<div class="col-xl-3 mb-3 px-0"> <div class="col-xl-3 mb-3 px-0">
@ -1262,13 +1302,11 @@
<script type="text/javascript" src="../js/utils.js"></script> <script type="text/javascript" src="../js/utils.js"></script>
<script type="text/javascript" src="../js/build_utils.js"></script> <script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/computation_graph.js"></script> <script type="text/javascript" src="../js/computation_graph.js"></script>
<script type="text/javascript">COMPUTE_GRAPH_DEBUG=true;</script> <script type="text/javascript">COMPUTE_GRAPH_DEBUG=true</script>
<!-- <script type="text/javascript" src="../js/icons.js"></script> --> <script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="../js/sq2icons.js"></script>
<script type="text/javascript" src="../js/powders.js"></script> <script type="text/javascript" src="../js/powders.js"></script>
<script type="text/javascript" src="../js/skillpoints.js"></script> <script type="text/javascript" src="../js/skillpoints.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script> <script type="text/javascript" src="../js/damage_calc.js"></script>
<script type="text/javascript" src="../js/atree_constants_min.js"></script>
<script type="text/javascript" src="../js/display_constants.js"></script> <script type="text/javascript" src="../js/display_constants.js"></script>
<script type="text/javascript" src="../js/display.js"></script> <script type="text/javascript" src="../js/display.js"></script>
<script type="text/javascript" src="../js/load.js"></script> <script type="text/javascript" src="../js/load.js"></script>
@ -1276,13 +1314,13 @@
<script type="text/javascript" src="../js/load_tome.js"></script> <script type="text/javascript" src="../js/load_tome.js"></script>
<script type="text/javascript" src="../js/custom.js"></script> <script type="text/javascript" src="../js/custom.js"></script>
<script type="text/javascript" src="../js/craft.js"></script> <script type="text/javascript" src="../js/craft.js"></script>
<script type="text/javascript" src="../js/build.js"></script> <script type="text/javascript" src="../js/builder/atree_constants_min.js"></script>
<script type="text/javascript" src="../js/build_constants.js"></script> <script type="text/javascript" src="../js/builder/build.js"></script>
<script type="text/javascript" src="../js/build_encode_decode.js"></script> <script type="text/javascript" src="../js/builder/builder_constants.js"></script>
<script type="text/javascript" src="../js/atree.js"></script> <script type="text/javascript" src="../js/builder/build_encode_decode.js"></script>
<script type="text/javascript" src="../js/builder.js"></script> <script type="text/javascript" src="../js/builder/atree.js"></script>
<script type="text/javascript" src="../js/builder_graph.js"></script> <script type="text/javascript" src="../js/builder/builder.js"></script>
<script type="text/javascript" src="../js/optimize.js"></script> <script type="text/javascript" src="../js/builder/builder_graph.js"></script>
<div id="graph_body" style="max-width: 100%; height: 100vh"> <div id="graph_body" style="max-width: 100%; height: 100vh">
<button id="saveButton">JANKY Export SVG</button> <button id="saveButton">JANKY Export SVG</button>

File diff suppressed because one or more lines are too long

View file

@ -36,20 +36,71 @@
<hr/> <hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a> <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>
<div class="container-fluid me-4" style="max-width: 95%;"> <div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class="container-fluid overall-box mt-lg-2" style="margin-top: 6vh;">
<!-- REMOVE THIS DIV AT SOME POINT. --> <!-- REMOVE THIS DIV AT SOME POINT. -->
<div class = "row scaled-font mx-auto" id = "discord-banner-dev"> <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 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>
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3"> <div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3 gx-0">
<div class="col-xl-6"> <div class="col-xl-6">
<div class="row row-cols-1 mb-3 gy-4"> <div class="row my-2 dark-6 rounded text-center g-0 px-3 d-flex d-lg-none">
<div class="col"> <div class="col">
<div class="row row-cols-1 row-cols-xl-2 rounded gy-4 gx-5 justify-content-center"> <p class="fake-button scaled-font mb-0 selected-btn" id="equipment-inputs-btn" onclick="show_tab('equipment-inputs', ['equipment-inputs', 'adjust-id'])">Equipments</p>
<div class="col-auto rounded order-xl-0 order-0"> </div>
<div class="row h-100 dark-shadow dark-6 rounded" id="helmet-dropdown"> <div class="col">
<p class="fake-button scaled-font mb-0" id="adjust-id-btn" onclick="show_tab('adjust-id', ['equipment-inputs', 'adjust-id'])">Ability Tree</p>
</div>
</div>
<div class="row row-cols-1 mb-3 gy-4" id="equipment-inputs">
<div class="col">
<div class="row row-cols-1 row-cols-xl-2 dark-shadow dark-6 justify-content-center equipment-input rounded gy-3 gy-lg-0 mt-auto">
<div class="col rounded order-xl-0 order-0 my-0">
<div class="row h-100 px-1" id="helmet-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="helmet-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="helmet-img-loc">
<img id="helmet-img" class="img-fluid rounded" src="../media/items/new/generic-helmet.png"> <div id="helmet-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 45.45454545454546% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -73,10 +124,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1 my-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="ring1-dropdown"> <div class="row h-100 px-1" id="ring1-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring1-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring1-img-loc">
<img id="ring1-img" class="img-fluid rounded" src="../media/items/new/generic-ring.png"> <div id="ring1-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 81.81818181818181% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -94,16 +145,15 @@
<input class="equipment-input text-light form-control" id="ring1-choice" name="ring1-choice" placeholder="No ring" value="" tabindex="2"/> <input class="equipment-input text-light form-control" id="ring1-choice" name="ring1-choice" placeholder="No ring" value="" tabindex="2"/>
</div> </div>
<div class="col d-flex justify-content-end" style="height: 100%;"> <div class="col d-flex justify-content-end" style="height: 100%;">
<!-- <input class="equipment-input text-light form-control" type="text" id="ring1-powder" name="ring1-powder" placeholder="no powders" tabindex="2"/> -->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-0"> <div class="col order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="chestplate-dropdown"> <div class="row h-100 px-1" id="chestplate-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="chestplate-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="chestplate-img-loc">
<img id="chestplate-img" class="img-fluid rounded" src="../media/items/new/generic-chestplate.png"> <div id="chestplate-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 54.54545454545454% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -127,10 +177,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="ring2-dropdown"> <div class="row h-100 px-1" id="ring2-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring2-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="ring2-img-loc">
<img id="ring2-img" class="img-fluid rounded" src="../media/items/new/generic-ring.png"> <div id="ring2-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 81.81818181818181% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -153,10 +203,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-0"> <div class="col order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="leggings-dropdown"> <div class="row h-100 px-1" id="leggings-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="leggings-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="leggings-img-loc">
<img id="leggings-img" class="img-fluid rounded" src="../media/items/new/generic-leggings.png"> <div id="leggings-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 63.63636363636363% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -180,10 +230,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="bracelet-dropdown"> <div class="row h-100 px-1" id="bracelet-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="bracelet-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="bracelet-img-loc">
<img id="bracelet-img" class="img-fluid rounded" src="../media/items/new/generic-bracelet.png"> <div id="bracelet-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 90.90909090909092% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -207,10 +257,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-0"> <div class="col order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="boots-dropdown"> <div class="row h-100 px-1" id="boots-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="boots-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="boots-img-loc">
<img id="boots-img" class="img-fluid rounded" src="../media/items/new/generic-boots.png"> <div id="boots-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 72.72727272727272% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -234,10 +284,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="necklace-dropdown"> <div class="row h-100 px-1" id="necklace-dropdown">
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="necklace-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="necklace-img-loc">
<img id="necklace-img" class="img-fluid rounded" src="../media/items/new/generic-necklace.png"> <div id="necklace-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 100% 0;"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -260,10 +310,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id='weapon-dropdown'> <div class="row h-auto px-1" id='weapon-dropdown'>
<div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="weapon-img-loc"> <div class="col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon" id="weapon-img-loc">
<img id="weapon-img" class="img-fluid rounded" src="../media/items/new/generic-dagger.png"> <div id="weapon-img" class="img-fluid rounded item-display-new-toggleable" style = "background-image: url('../media/items/new.png');"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -287,29 +337,25 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto order-xl-0 order-1"> <div class="col order-xl-0 order-1 level-input">
<div class="row h-100 dark-shadow dark-6 rounded"> <div class="row h-100 px-1">
<div class="col"> <div class="col">
<div class="row align-items-center"> <div class="row align-items-center justify-content-left">
<div class="col-3 text-nowrap fw-bold scaled-font"> <div class="col-auto text-nowrap fw-bold scaled-font">
Level: Level:
</div> </div>
<div class="col d-flex justify-content-end"> <div class="col d-flex px-1">
<input class="equipment-input text-light form-control form-control-sm" id="level-choice" name="level-choice" value="106" placeholder="Build level" value="" tabindex="2"/> <input class="equipment-input text-light form-control form-control-sm" id="level-choice" name="level-choice" value="106" placeholder="Build level" value="" tabindex="2"/>
</div> </div>
<div class="col-auto px-1 text-nowrap scaled-font"> <div class="col-auto px-1 scaled-font">
<button class="button fw-bold text-light dark-5 scaled-font rounded" id="reset-button" onclick="resetFields()">Reset</button> <button class="button py-0 fw-bold text-light dark-5 scaled-font rounded" id="reset-button" onclick="resetFields()">Reset</button>
</div> </div>
</div> </div>
<div class="row align-items-center justify-content-center my-1"> <div class="row align-items-left justify-content-left my-1">
<div class="row align-items-center"> <button class="col-auto mx-1 px-1 py-0 border-info text-light dark-5 scaled-font rounded fake-button"
<div class="col-auto px-1 text-nowrap scaled-font"> id=copy-button onclick="copyBuild()">Copy short</button>
<button class="border-dark text-light dark-5 scaled-font rounded" id=copy-button onclick="copyBuild()">Copy short</button> <button class="col-auto mx-1 px-1 py-0 border-info text-light dark-5 scaled-font rounded fake-button"
</div> id=share-button onclick="shareBuild(player_build)">Copy for sharing</button>
<div class="col-auto px-1 text-nowrap scaled-font">
<button class="border-dark text-light dark-5 scaled-font rounded" id=share-button onclick="shareBuild(player_build)">Copy for sharing</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -390,12 +436,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 text-center gx-2">
<button class = "button fw-bold text-light dark-5 rounded scaled-font" id = "optimize-strdex" onclick = "optimizeStrDex()">Optimize Str/Dex</button>
</div>
</div> </div>
</div> </div>
<div class="col text-center"> <div class="col text-center py-1">
<div id="summary-box"></div> <div id="summary-box"></div>
<div id="err-box"></div> <div id="err-box"></div>
<div id="stack-box"></div> <div id="stack-box"></div>
@ -407,188 +450,187 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col mb-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="row row-cols-1 justify-content-center">
<div class="col fw-bold dark-4 rounded-top">
Active boosts
</div>
<div class="col">
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="radiance-boost" onclick="update_radiance()">
Radiance
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
War Scream
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+30%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="totem-boost" onclick="update_boosts('totem-boost')">
Vengeful Spirit (+20%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="fortitude-boost" onclick="update_boosts('fortitude-boost')">
Fortitude (+60%)
</button>
</div>
<div class="col" id="boost-toggles">
</div>
</div>
<div class="row row-cols-1 justify-content-center" id="boost-sliders">
</div>
</div>
<div class="col mb-1">
<div class="row row-cols-1 rounded text-center dark-5 scaled-font">
<div class="row p-0 m-0 text-nowrap">
<div id = "str-boost-btn" class="col eDam dark-4u fake-button elem-boost" onclick="show_tab('str-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Earth
</div>
<div id = "dex-boost-btn" class="col tDam dark-4u fake-button elem-boost" onclick="show_tab('dex-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Thunder
</div>
<div id = "int-boost-btn" class="col wDam dark-4u fake-button elem-boost" onclick="show_tab('int-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Water
</div>
<div id = "def-boost-btn" class="col fDam dark-4u fake-button elem-boost" onclick="show_tab('def-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Fire
</div>
<div id = "agi-boost-btn" class="col aDam dark-4u fake-button elem-boost" onclick="show_tab('agi-boost', ['str-boost', 'dex-boost', 'int-boost', 'def-boost', 'agi-boost'])">
Air
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="str-boost" style="display: none;">
<div class="col eDam dark-5">
Quake (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1" >
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1')">
Lv.1
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2')">
Lv.2
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3')">
Lv.3
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4')">
Lv.4
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5')">
Lv.5
</button>
</div>
<div class="col eDam">
Rage (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="dex-boost" style="display: none;">
<div class="col tDam dark-5">
Chain Lightning (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1')">
Lv.1
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2')">
Lv.2
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3')">
Lv.3
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4')">
Lv.4
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5')">
Lv.5
</button>
</div>
<div class="col tDam">
Kill Streak (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="int-boost">
<div class="col wDam dark-5">
Curse (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1')">
Lv.1
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2')">
Lv.2
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3')">
Lv.3
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4')">
Lv.4
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5')">
Lv.5
</button>
</div>
<div class="col wDam">
Concentration (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="def-boost" style="display: none;">
<div class="col fDam dark-5">
Courage (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1')">
Lv.1
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2')">
Lv.2
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3')">
Lv.3
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4')">
Lv.4
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5')">
Lv.5
</button>
</div>
<div class="col fDam">
Endurance (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="agi-boost" style="display: none;">
<div class="col aDam dark-5">
Wind Prison (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-1" onclick = "updatePowderSpecials('Wind_Prison-1')">
Lv.1
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-2" onclick = "updatePowderSpecials('Wind_Prison-2')">
Lv.2
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-3" onclick = "updatePowderSpecials('Wind_Prison-3')">
Lv.3
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-4" onclick = "updatePowderSpecials('Wind_Prison-4')">
Lv.4
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-5" onclick = "updatePowderSpecials('Wind_Prison-5')">
Lv.5
</button>
</div>
<div class="col aDam">
Dodge (Passive)
</div>
</div>
</div>
</div>
</div>
<div class="row row-cols-1 d-lg-flex" id="adjust-id" style="display: none;">
<div class="col"> <div class="col">
<div class="row row-cols-1 gy-4"> <div class="row row-cols-1 gy-3">
<div class="col mb-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="row row-cols-1 justify-content-center">
<div class="col fw-bold dark-4 rounded-top">
Active boosts
</div>
<div class="col">
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="vanish-boost" onclick="update_boosts('vanish-boost')">
Vanish (+80%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
War Scream
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
Ragnarokkr (+30%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="yourtotem-boost" onclick="update_boosts('yourtotem-boost')">
Your Totem (+35%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="allytotem-boost" onclick="update_boosts('allytotem-boost')">
Ally Totem (+15%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="fortitude-boost" onclick="update_boosts('fortitude-boost')">
Fortitude (+60%)
</button>
</div>
<div class="col" id="boost-toggles">
</div>
</div>
<div class="row row-cols-1 justify-content-center" id="boost-sliders">
</div>
</div>
<div class="col mb-1">
<div class="row row-cols-1 rounded text-center dark-5 scaled-font">
<div class="row p-0 m-0 text-nowrap">
<div id = "str-boost-tab" class="col eDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('str')">
Earth
</div>
<div id = "dex-boost-tab" class="col tDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('dex')">
Thunder
</div>
<div id = "int-boost-tab" class="col wDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('int')">
Water
</div>
<div id = "def-boost-tab" class="col fDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('def')">
Fire
</div>
<div id = "agi-boost-tab" class="col aDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('agi')">
Air
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="str-boost" style="display: none;">
<div class="col eDam dark-5">
Quake (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1" >
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-1" onclick = "updatePowderSpecials('Quake-1')">
Lv.4 [e4e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-2" onclick = "updatePowderSpecials('Quake-2')">
Lv.4.5 [e5e4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-3" onclick = "updatePowderSpecials('Quake-3')">
Lv.5 [e5e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-4" onclick = "updatePowderSpecials('Quake-4')">
Lv.5.5 [e6e5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Quake-5" onclick = "updatePowderSpecials('Quake-5')">
Lv.6 [e6e6]
</button>
</div>
<div class="col eDam">
Rage (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="dex-boost" style="display: none;">
<div class="col tDam dark-5">
Chain Lightning (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-1" onclick = "updatePowderSpecials('Chain_Lightning-1')">
Lv.4 [t4t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-2" onclick = "updatePowderSpecials('Chain_Lightning-2')">
Lv.4.5 [t5t4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-3" onclick = "updatePowderSpecials('Chain_Lightning-3')">
Lv.5 [t5t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-4" onclick = "updatePowderSpecials('Chain_Lightning-4')">
Lv.5.5 [t6t5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Chain_Lightning-5" onclick = "updatePowderSpecials('Chain_Lightning-5')">
Lv.6 [t6t6]
</button>
</div>
<div class="col tDam">
Kill Streak (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="int-boost">
<div class="col wDam dark-5">
Curse (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-1" onclick = "updatePowderSpecials('Curse-1')">
Lv.4 [w4w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-2" onclick = "updatePowderSpecials('Curse-2')">
Lv.4.5 [w5w4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-3" onclick = "updatePowderSpecials('Curse-3')">
Lv.5 [w5w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-4" onclick = "updatePowderSpecials('Curse-4')">
Lv.5.5 [w6w5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Curse-5" onclick = "updatePowderSpecials('Curse-5')">
Lv.6 [w6w6]
</button>
</div>
<div class="col wDam">
Concentration (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="def-boost" style="display: none;">
<div class="col fDam dark-5">
Courage (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-1" onclick = "updatePowderSpecials('Courage-1')">
Lv.4 [f4f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-2" onclick = "updatePowderSpecials('Courage-2')">
Lv.4.5 [f5f4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-3" onclick = "updatePowderSpecials('Courage-3')">
Lv.5 [f5f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-4" onclick = "updatePowderSpecials('Courage-4')">
Lv.5.5 [f6f5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Courage-5" onclick = "updatePowderSpecials('Courage-5')">
Lv.6 [f6f6]
</button>
</div>
<div class="col fDam">
Endurance (Passive)
</div>
</div>
<div class="row row-cols-1 p-0 m-0" id="agi-boost" style="display: none;">
<div class="col aDam dark-5">
Wind Prison (Active)
</div>
<div class="col skp-tooltip dark-6 rounded-bottom my-3 my-xl-1">
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-1" onclick = "updatePowderSpecials('Wind_Prison-1')">
Lv.4 [a4a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-2" onclick = "updatePowderSpecials('Wind_Prison-2')">
Lv.4.5 [a5a4]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-3" onclick = "updatePowderSpecials('Wind_Prison-3')">
Lv.5 [a5a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-4" onclick = "updatePowderSpecials('Wind_Prison-4')">
Lv.5.5 [a6a5]
</button>
<button class = "button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id = "Wind_Prison-5" onclick = "updatePowderSpecials('Wind_Prison-5')">
Lv.6 [a6a6]
</button>
</div>
<div class="col aDam">
Dodge (Passive)
</div>
</div>
</div>
</div>
<div class="col mb-1"> <div class="col mb-1">
<div class="row row-cols-1 row-cols-1 text-center scaled-font dark-5 rounded dark-shadow"> <div class="row row-cols-1 row-cols-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="col fw-bold dark-4 rounded-top"> <div class="col fw-bold dark-4 rounded-top">
@ -620,7 +662,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='weaponTome1-dropdown'> <div class="row h-100 dark-shadow rounded" id='weaponTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome1-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome1-img-loc">
<img id="weaponTome1-img" class="img-fluid rounded" src="../media/items/new/generic-weaponTome.png"> <div id="weaponTome1-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -644,7 +686,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='weaponTome2-dropdown'> <div class="row h-100 dark-shadow rounded" id='weaponTome2-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome2-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="weaponTome2-img-loc">
<img id="weaponTome2-img" class="img-fluid rounded" src="../media/items/new/generic-weaponTome.png"> <div id="weaponTome2-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -668,7 +710,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome1-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome1-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome1-img-loc">
<img id="armorTome1-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome1-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -692,7 +734,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome2-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome2-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome2-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome2-img-loc">
<img id="armorTome2-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome2-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -716,7 +758,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome3-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome3-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome3-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome3-img-loc">
<img id="armorTome3-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome3-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -740,7 +782,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='armorTome4-dropdown'> <div class="row h-100 dark-shadow rounded" id='armorTome4-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome4-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="armorTome4-img-loc">
<img id="armorTome4-img" class="img-fluid rounded" src="../media/items/new/generic-armorTome.png"> <div id="armorTome4-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -764,7 +806,7 @@
<div class="col-auto rounded"> <div class="col-auto rounded">
<div class="row h-100 dark-shadow rounded" id='guildTome1-dropdown'> <div class="row h-100 dark-shadow rounded" id='guildTome1-dropdown'>
<div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="guildTome1-img-loc"> <div class="col-auto g-0 rounded-end my-auto text-center scaled-item-icon" id="guildTome1-img-loc">
<img id="guildTome1-img" class="img-fluid rounded" src="../media/items/new/generic-guildTome.png"> <div id="guildTome1-img" class="img-fluid rounded tome-image"></div>
</div> </div>
<div class="col-3"> <div class="col-3">
<div class="row row-cols-1 h-100 align-items-center"> <div class="row row-cols-1 h-100 align-items-center">
@ -789,14 +831,16 @@
</div> </div>
<div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;"> <div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;">
<div class="row row-cols-1 row-cols-xl-2"> <div class="row row-cols-1 row-cols-xl-2">
<div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 90vh; overflow-y: auto; overflow-x: hidden;"> <div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 90vh; overflow-y: auto;">
</div> </div>
<div class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs"> <div class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs">
<div class="col mx-auto" style="height: 2em; overflow-y: auto;" id="atree-header"> <div class="col mx-auto" style="height: 2em; overflow-y: auto;" id="atree-header">
</div> </div>
<div class="col mx-auto" style="overflow-y: auto;" id="atree-active"> <div class="col mx-auto" style="" id="atree-warning">
</div> </div>
<div class="col mx-auto" style="overflow-y: auto;" id="atree-active">
</div>
</div> </div>
</div> </div>
</div> </div>
@ -1131,24 +1175,20 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-xl-3 mb-3"> <div class="col-xl-3 mb-3 order-2 order-lg-0">
<div class="row row-cols-1 rounded dark-shadow dark-6 scaled-font"> <div class="row row-cols-1 rounded dark-shadow dark-6 scaled-font">
<div class="col rounded-top"> <div class="col rounded-top">
<div class="row"> <div class="row">
<div id="tab-offensive-btn" class="col-4 text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7" onclick="show_tab('offensive-stats')"> <div id="detailed-stats-btn" class="col text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7" onclick="show_tab('detailed-stats', ['detailed-stats', 'summary-stats'])">
Offense Detailed
</div> </div>
<div id="tab-defensive-btn" class="col-4 text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7" onclick="show_tab('defensive-stats')"> <div id="summary-stats-btn" class="col text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7 selected-btn" onclick="show_tab('summary-stats', ['detailed-stats', 'summary-stats'])">
Defense Summary
</div>
<div id="tab-overall-btn" class="col-4 text-center fake-button border-bottom border-dark rounded-top dark-4u border border-2 border-dark-7 selected-btn" onclick="show_tab('overall-stats')">
Overall
</div> </div>
</div> </div>
</div> </div>
<div style="display: none;" id="offensive-stats" class="col text-nowrap"></div> <div style="display: none;" id="detailed-stats" class="col text-nowrap"></div>
<div style="display: none;" id="defensive-stats" class="col text-nowrap"></div> <div id="summary-stats" class="col text-nowrap"></div>
<div id="overall-stats" class="col text-nowrap"></div>
</div> </div>
</div> </div>
<div class="col-xl-3 mb-3 px-0"> <div class="col-xl-3 mb-3 px-0">
@ -1195,7 +1235,7 @@
<div class="col-12 dark-5 scaled-font"> <div class="col-12 dark-5 scaled-font">
<footer class="text-center"> <footer class="text-center">
<div id="header2"> <div id="header2">
<p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, and <b>reschan</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p> <p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, <b>reschan</b>, and <b>blankman</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p> <p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
</div> </div>
<div id="credits"> <div id="credits">
@ -1262,11 +1302,10 @@
<script type="text/javascript" src="../js/utils.js"></script> <script type="text/javascript" src="../js/utils.js"></script>
<script type="text/javascript" src="../js/build_utils.js"></script> <script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/computation_graph.js"></script> <script type="text/javascript" src="../js/computation_graph.js"></script>
<script type="text/javascript" src="../js/sq2icons.js"></script> <script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="../js/powders.js"></script> <script type="text/javascript" src="../js/powders.js"></script>
<script type="text/javascript" src="../js/skillpoints.js"></script> <script type="text/javascript" src="../js/skillpoints.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script> <script type="text/javascript" src="../js/damage_calc.js"></script>
<script type="text/javascript" src="../js/atree_constants_min.js"></script>
<script type="text/javascript" src="../js/display_constants.js"></script> <script type="text/javascript" src="../js/display_constants.js"></script>
<script type="text/javascript" src="../js/display.js"></script> <script type="text/javascript" src="../js/display.js"></script>
<script type="text/javascript" src="../js/load.js"></script> <script type="text/javascript" src="../js/load.js"></script>
@ -1274,13 +1313,12 @@
<script type="text/javascript" src="../js/load_tome.js"></script> <script type="text/javascript" src="../js/load_tome.js"></script>
<script type="text/javascript" src="../js/custom.js"></script> <script type="text/javascript" src="../js/custom.js"></script>
<script type="text/javascript" src="../js/craft.js"></script> <script type="text/javascript" src="../js/craft.js"></script>
<script type="text/javascript" src="../js/build.js"></script> <script type="text/javascript" src="../js/builder/build.js"></script>
<script type="text/javascript" src="../js/build_constants.js"></script> <script type="text/javascript" src="../js/builder/builder_constants.js"></script>
<script type="text/javascript" src="../js/build_encode_decode.js"></script> <script type="text/javascript" src="../js/builder/build_encode_decode.js"></script>
<script type="text/javascript" src="../js/atree.js"></script> <script type="text/javascript" src="../js/builder/atree.js"></script>
<script type="text/javascript" src="../js/builder.js"></script> <script type="text/javascript" src="../js/builder/builder_graph.js"></script>
<script type="text/javascript" src="../js/builder_graph.js"></script> <script type="text/javascript" src="../js/builder/builder.js"></script>
<script type="text/javascript" src="../js/optimize.js"></script> <!--script type="text/javascript" src="../js/builder/optimize.js"></script-->
</body> </body>
</html> </html>

File diff suppressed because it is too large Load diff

74202
clean.json

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -31,7 +31,50 @@
<hr/> <hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a> <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>
<div class="container mt-5"> <div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class="container mt-lg-2" style="margin-top: 6vh;">
<div class="row row-cols-1 row-cols-lg-3 gy-5"> <div class="row row-cols-1 row-cols-lg-3 gy-5">
<div class="col col-lg-5"> <div class="col col-lg-5">
<!--crafter ui--> <!--crafter ui-->
@ -39,7 +82,7 @@
<div class="col" id="recipe-dropdown"> <div class="col" id="recipe-dropdown">
<div class="row dark-shadow dark-5 rounded"> <div class="row dark-shadow dark-5 rounded">
<div id = "recipe-img-loc" class = "col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon"> <div id = "recipe-img-loc" class = "col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon">
<img id = "recipe-img" class = "img-fluid rounded Crafted-shadow" src = "../media/items/new/generic-potion.png"> <div id = "recipe-img" class = "img-fluid rounded Crafted-shadow" style = "background-image: url('../media/items/common.png'); background-size: 500% 100%; image-rendering: pixelated; background-position: 25% 0; aspect-ratio: 1/1;"></div>
</div> </div>
<div class = "col ps-3"> <div class = "col ps-3">
<div class = "row row-cols-2 align-items-center"> <div class = "row row-cols-2 align-items-center">
@ -279,11 +322,9 @@
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript" src="../js/query.js"></script>
<script type="text/javascript" src="../js/query_2.js"></script>
<script type="text/javascript" src="../js/utils.js"></script> <script type="text/javascript" src="../js/utils.js"></script>
<script type="text/javascript" src="../js/build_utils.js"></script> <script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/sq2icons.js"></script> <script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="../js/powders.js"></script> <script type="text/javascript" src="../js/powders.js"></script>
<script type="text/javascript" src="../js/skillpoints.js"></script> <script type="text/javascript" src="../js/skillpoints.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script> <script type="text/javascript" src="../js/damage_calc.js"></script>
@ -293,6 +334,5 @@
<script type="text/javascript" src="../js/load.js"></script> <script type="text/javascript" src="../js/load.js"></script>
<script type="text/javascript" src="../js/craft.js"></script> <script type="text/javascript" src="../js/craft.js"></script>
<script type="text/javascript" src="../js/crafter.js"></script> <script type="text/javascript" src="../js/crafter.js"></script>
<script type="text/javascript" src="../js/expr_parser.js"></script>
</body> </body>
</html> </html>

View file

@ -1,32 +1,105 @@
.searchbox { /* type selectors */
grid-column: 1;
padding: 0%; #type-box {
display: grid; cursor: pointer;
grid-template-columns: repeat(4, 1fr); overflow: auto;
width: 100%; }
gap: 5px; #type-box > div {
display: inline-block;
width: calc(100% / 12);
float: left;
} }
.items { .type-selected {
grid-column: 1; background-color: #0a0;
padding: 0%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
width: 100%;
gap: 5px;
grid-template-rows: masonry;
} }
.itemsearch { /* rarity selectors */
padding: 0%;
display: grid; #rarity-box {
grid-template-columns: 1fr; cursor: pointer;
width: 100%; width: 58.33%;
gap: 5px; text-align: center;
}
#rarity-box > div {
width: calc(100% / 7);
aspect-ratio: 1/1;
position: relative;
display: inline-block;
}
#rarity-box > div > b {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#rarity-box > div.rarity-selected > b {
color: black;
} }
.searchinput { #rarity-normal { color: #FFFFFF; }
width: 100%; #rarity-normal.rarity-selected { background-color: #FFFFFF; }
max-width: 200px; #rarity-unique { color: #FFFF55; }
#rarity-unique.rarity-selected { background-color: #FFFF55; }
#rarity-set { color: #55FF55; }
#rarity-set.rarity-selected { background-color: #55FF55; }
#rarity-rare { color: #FF55FF; }
#rarity-rare.rarity-selected { background-color: #FF55FF; }
#rarity-legendary { color: #55FFFF; }
#rarity-legendary.rarity-selected { background-color: #55FFFF; }
#rarity-fabled { color: #FF5555; }
#rarity-fabled.rarity-selected { background-color: #FF5555; }
#rarity-mythic { color: #AA00AA; }
#rarity-mythic.rarity-selected { background-color: #AA00AA; }
/* filters */
.filter-row {
border: 1px solid #121212;
}
.filter-dragged-over {
border: 1px solid white;
} }
.reorder-filter {
height: 2ch;
cursor: pointer;
}
input.filter-input {
width: 27ch!important;
max-width: 90%;
display: inline-block;
}
.asc-icon {
opacity: 0.25;
width: 1.75ch;
}
.desc-icon {
opacity: 0.25;
width: 1.75ch;
transform: rotate(180deg);
}
img.asc-sel {
opacity: 0.85;
}
input.min-max-input {
width: 6ch!important;
display: inline-block;
}
.delete-filter {
cursor: pointer;
}
#filter-container > div > div > *, #exclude-container > div > div > * {
margin: 0 2px;
}
/* item display */
@media (min-width: 576px) and (max-width: 991px) {
div.col-sm-6 {
width: 100%;
}
}

View file

@ -7,7 +7,6 @@
} }
/* builder containers */ /* builder containers */
.slider { .slider {
-webkit-appearance: none; -webkit-appearance: none;
background: #AAAAAA; background: #AAAAAA;
@ -46,7 +45,6 @@ input[type=range]:focus {
outline: none; /* Removes the border. */ outline: none; /* Removes the border. */
} }
/* equipment field specifics */ /* equipment field specifics */
/* inputs and dropdowns */ /* inputs and dropdowns */
.form-control { .form-control {
@ -63,6 +61,7 @@ ul.search-box {
position: absolute; position: absolute;
padding: 0; padding: 0;
overflow: auto; overflow: auto;
z-index: 10;
} }
li.search-item { li.search-item {
@ -102,10 +101,15 @@ input.equipment-input {
font-weight: bold; font-weight: bold;
background-color: hsl(0, 0%, 21%) !important; background-color: hsl(0, 0%, 21%) !important;
border-radius: 0.375rem !important; border-radius: 0.375rem !important;
border-color: rgba(33, 37, 41, 1) !important;
min-height: calc(1.2 * var(--scaled-fontsize) + 2px); min-height: calc(1.2 * var(--scaled-fontsize) + 2px);
padding: 0rem 0.5rem; padding: 0rem 0.5rem;
font-size: var(--scaled-fontsize); font-size: var(--scaled-fontsize);
--bs-gutter-y: 1rem;
--bs-gutter-x: 3rem
}
input.equipment-input:not(.is-invalid) {
border-color: rgba(33, 37, 41, 1) !important;
} }
.my-container { .my-container {
@ -174,11 +178,11 @@ input.equipment-input {
} }
.scaled-item-icon { .scaled-item-icon {
width: 8rem; width: 6rem;
} }
.scaled-item-icon img { .scaled-item-icon img {
width: 6.5rem; width: 6rem;
} }
.scaled-bckgrd { .scaled-bckgrd {
@ -187,13 +191,82 @@ input.equipment-input {
} }
.scaled-bckgrd img { .scaled-bckgrd img {
width: 6.5rem; width: 7rem;
}
.overall-box {
max-width: 95%;
}
@media screen and (orientation: landscape) and (max-width: 1199px) {
:root {
--scaled-fontsize: max(1rem, 16px);
}
.scaled-font {
font-size: max(1rem, 16px);
}
.box-title {
font-size: 1rem;
}
.item-title {
font-size: 1.2rem;
}
.big-title {
font-size: 1.5rem;
}
.skp-tooltip {
font-size: 1rem;
}
.spellcost-tooltip b {
font-size: .625rem !important;
}
.scaled-item-icon {
width: 3.2rem;
}
.scaled-item-icon img {
width: 2.8rem;
}
.scaled-bckgrd {
width: 4rem;
height: 4rem;
}
.scaled-bckgrd img {
width: 2.8rem;
}
.warning {
font-size: .7rem;
}
.spell-display b {
font-size: 1rem;
}
} }
@media screen and (min-width: 1200px) and (max-width: 1400px) { @media screen and (min-width: 1200px) and (max-width: 1400px) {
:root { :root {
--scaled-fontsize: 1rem; --scaled-fontsize: 1rem;
} }
.overall-box {
padding-left: var(--sidebar-width);
max-width: 100%;
}
.equipment-input {
--bs-gutter-y: 0.5rem;
--bs-gutter-x: 1.5rem;
}
.level-input {
margin-top: 0
}
.scaled-font { .scaled-font {
font-size: 1rem; font-size: 1rem;
} }
@ -219,11 +292,11 @@ input.equipment-input {
} }
.scaled-item-icon { .scaled-item-icon {
width: 3.2rem; width: 2.6rem;
} }
.scaled-item-icon img { .scaled-item-icon img {
width: 2.8rem; width: 2.2rem;
} }
.scaled-bckgrd { .scaled-bckgrd {
@ -248,6 +321,17 @@ input.equipment-input {
:root { :root {
--scaled-fontsize: 1rem; --scaled-fontsize: 1rem;
} }
.overall-box {
padding-left: var(--sidebar-width);
max-width: 100%;
}
.equipment-input {
--bs-gutter-y: 0.5rem;
--bs-gutter-x: 1.5rem
}
.level-input {
margin-top: 0
}
.scaled-font { .scaled-font {
font-size: 1rem; font-size: 1rem;
} }
@ -273,11 +357,11 @@ input.equipment-input {
} }
.scaled-item-icon { .scaled-item-icon {
width: 4rem; width: 3.2rem;
} }
.scaled-item-icon img { .scaled-item-icon img {
width: 3.5rem; width: 2.7rem;
} }
.scaled-bckgrd { .scaled-bckgrd {
@ -289,7 +373,6 @@ input.equipment-input {
width: 3.5rem; width: 3.5rem;
} }
.warning { .warning {
font-size: .8rem; font-size: .8rem;
} }
@ -460,3 +543,16 @@ a:hover {
.ferricles{ .ferricles{
color: #5be553; color: #5be553;
} }
.item-display-new-toggleable {
image-rendering: pixelated;
background-size: 1200% 100%;
aspect-ratio: 1/1;
}
.tome-image {
background-image: url('../media/items/common.png');
image-rendering: pixelated;
background-size: 500% 100%;
background-position: 100% 0;
aspect-ratio: 1/1;
}

View file

@ -33,7 +33,50 @@
<hr/> <hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a> <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>
<div class = "container row py-5 vh-100 mx-0 mx-lg-auto scaled-font"> <div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class = "container row py-5 vh-100 mx-0 mx-lg-auto scaled-font mt-lg-2" style="margin-top: 6vh;">
<div class = "col-lg-3 col-sm-12"> <div class = "col-lg-3 col-sm-12">
<div class = "col px-1"> <div class = "col px-1">
<div class = "row border border-dark border-3 mb-1 p-1 rounded dark-7"> <div class = "row border border-dark border-3 mb-1 p-1 rounded dark-7">

1
data/2.0.1.1/atree.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
data/2.0.1.1/items.json Normal file

File diff suppressed because one or more lines are too long

809
data/2.0.1.1/tomes.json Normal file
View file

@ -0,0 +1,809 @@
{
"tomes": [
{
"name": "Retaliating Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defmobs": 3,
"thorns": 6,
"ref": 6,
"hpBonus": 120,
"fixID": false,
"id": 0,
"alias": "Thorns I"
},
{
"name": "Retaliating Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"thorns": 8,
"ref": 8,
"fixID": false,
"id": 1,
"alias": "Thorns II"
},
{
"name": "Destructive Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defMobs": 3,
"exploding": 5,
"mdPct": 5,
"hpBonus": 120,
"fixID": false,
"id": 2,
"alias": "Melee I"
},
{
"name": "Destructive Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"exploding": 6,
"mdPct": 6,
"fixID": false,
"id": 3,
"alias": "Melee II"
},
{
"name": "Sorcerer's Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defMobs": 3,
"sdPct": 5,
"hpBonus": 120,
"fixID": false,
"id": 4,
"alias": "Spell Damage I"
},
{
"name": "Sorcerer's Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"sdPct": 6,
"fixID": false,
"id": 5,
"alias": "Spell Damage II"
},
{
"name": "Everlasting Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defMobs": 3,
"hprRaw": 15,
"hpBonus": 120,
"fixID": false,
"id": 6,
"alias": "Health Regen I"
},
{
"name": "Everlasting Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"hprRaw": 60,
"fixID": false,
"id": 7,
"alias": "Health Regen II"
},
{
"name": "Vampiric Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defMobs": 3,
"ls": 25,
"hpBonus": 120,
"fixID": false,
"id": 8,
"alias": "Life Steal I"
},
{
"name": "Vampiric Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"ls": 85,
"fixID": false,
"id": 9,
"alias": "Life Steal II"
},
{
"name": "Greedy Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defMobs": 3,
"lb": 5,
"hpBonus": 120,
"fixID": false,
"id": 10,
"alias": "Loot Bonus I"
},
{
"name": "Greedy Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"lb": 6,
"fixID": false,
"id": 11,
"alias": "Loot Bonus II"
},
{
"name": "Weightless Tome of Armour Mastery I",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"defMobs": 3,
"spd": 5,
"hpBonus": 120,
"fixID": false,
"id": 12,
"alias": "Walk Speed I"
},
{
"name": "Weightless Tome of Armour Mastery II",
"tier": "Fabled",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 5,
"spd": 6,
"fixID": false,
"id": 13,
"alias": "Walk Speed II"
},
{
"name": "Blooming Tome of Armour Mastery II",
"tier": "Mythic",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 8,
"eDefPct": 10,
"hpBonus": 150,
"fixID": false,
"id": 14,
"alias": "Earth Defense II"
},
{
"name": "Pulsing Tome of Armour Mastery II",
"tier": "Mythic",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 8,
"tDefPct": 10,
"hpBonus": 150,
"fixID": false,
"id": 15,
"alias": "Thunder Defense II"
},
{
"name": "Oceanic Tome of Armour Mastery II",
"tier": "Mythic",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 8,
"wDefPct": 10,
"hpBonus": 150,
"fixID": false,
"id": 16,
"alias": "Water Defense II"
},
{
"name": "Courageous Tome of Armour Mastery II",
"tier": "Mythic",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 8,
"fDefPct": 10,
"hpBonus": 150,
"fixID": false,
"id": 17,
"alias": "Fire Defense II"
},
{
"name": "Clouded Tome of Armour Mastery II",
"tier": "Mythic",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 8,
"aDefPct": 10,
"hpBonus": 150,
"fixID": false,
"id": 18,
"alias": "Air Defense II"
},
{
"name": "Radiant Tome of Armour Mastery II",
"tier": "Mythic",
"type": "armorTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 100,
"defMobs": 8,
"eDefPct": 6,
"tDefPct": 6,
"wDefPct": 6,
"fDefPct": 6,
"aDefPct": 6,
"hpBonus": 150,
"fixID": false,
"id": 19,
"alias": "Rainbow Defense II"
},
{
"name": "Tome of Weapon Mastery I",
"tier": "Legendary",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 60,
"damMobs": 6,
"fixID": false,
"id": 20,
"alias": "Weapon Mastery I"
},
{
"name": "Earthbound Tome of Weapon Mastery I",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 7,
"str": 3,
"fixID": false,
"id": 21,
"alias": "Strength I"
},
{
"name": "Earthbound Tome of Weapon Mastery II",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 8,
"str": 3,
"fixID": false,
"id": 22,
"alias": "Strength II"
},
{
"name": "Nimble Tome of Weapon Mastery I",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 7,
"dex": 3,
"fixID": false,
"id": 23,
"alias": "Dexterity I"
},
{
"name": "Nimble Tome of Weapon Mastery II",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 8,
"dex": 3,
"fixID": false,
"id": 24,
"alias": "Dexterity II"
},
{
"name": "Mystical Tome of Weapon Mastery I",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 7,
"int": 3,
"fixID": false,
"id": 25,
"alias": "Intelligence I"
},
{
"name": "Mystical Tome of Weapon Mastery II",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 8,
"int": 3,
"fixID": false,
"id": 26,
"alias": "Intelligence II"
},
{
"name": "Warding Tome of Weapon Mastery I",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 7,
"def": 3,
"fixID": false,
"id": 27,
"alias": "Defense I"
},
{
"name": "Warding Tome of Weapon Mastery II",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 8,
"def": 3,
"fixID": false,
"id": 28,
"alias": "Defense II"
},
{
"name": "Athletic Tome of Weapon Mastery I",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 7,
"agi": 3,
"fixID": false,
"id": 29,
"alias": "Agility I"
},
{
"name": "Athletic Tome of Weapon Mastery II",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 8,
"agi": 3,
"fixID": false,
"id": 30,
"alias": "Agility II"
},
{
"name": "Cosmic Tome of Weapon Mastery I",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 7,
"str": 1,
"dex": 1,
"int": 1,
"def": 1,
"agi": 1,
"fixID": false,
"id": 31,
"alias": "Rainbow Skillpoint I"
},
{
"name": "Cosmic Tome of Weapon Mastery II",
"tier": "Fabled",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 8,
"str": 1,
"dex": 1,
"int": 1,
"def": 1,
"agi": 1,
"fixID": false,
"id": 32,
"alias": "Rainbow Skillpoint II"
},
{
"name": "Seismic Tome of Weapon Mastery II",
"tier": "Mythic",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 12,
"eDamPct": 7,
"fixID": false,
"id": 33,
"alias": "Earth Damage II"
},
{
"name": "Voltaic Tome of Weapon Mastery II",
"tier": "Mythic",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 12,
"tDamPct": 7,
"fixID": false,
"id": 34,
"alias": "Thunder Damage II"
},
{
"name": "Abyssal Tome of Weapon Mastery II",
"tier": "Mythic",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 12,
"wDamPct": 7,
"fixID": false,
"id": 35,
"alias": "Water Damage II"
},
{
"name": "Infernal Tome of Weapon Mastery II",
"tier": "Mythic",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 12,
"fDamPct": 7,
"fixID": false,
"id": 36,
"alias": "Fire Damage II"
},
{
"name": "Cyclonic Tome of Weapon Mastery II",
"tier": "Mythic",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 12,
"aDamPct": 7,
"fixID": false,
"id": 37,
"alias": "Air Damage II"
},
{
"name": "Astral Tome of Weapon Mastery II",
"tier": "Mythic",
"type": "weaponTome",
"category": "tome",
"drop": "never",
"restrict": "Soulbound Item",
"lvl": 80,
"damMobs": 12,
"eDamPct": 6,
"tDamPct": 6,
"wDamPct": 6,
"fDamPct": 6,
"aDamPct": 6,
"fixID": false,
"id": 38,
"alias": "Rainbow Damage II"
},
{
"name": "Brute's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"str": 3,
"eDamPct": 2,
"fixID": false,
"id": 39,
"alias": "Strength"
},
{
"name": "Sadist's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"dex": 3,
"tDamPct": 2,
"fixID": false,
"id": 40,
"alias": "Dexterity"
},
{
"name": "Mastermind's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"int": 3,
"wDamPct": 2,
"fixID": false,
"id": 41,
"alias": "Intelligence"
},
{
"name": "Arsonist's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"def": 3,
"fDamPct": 2,
"fixID": false,
"id": 42,
"alias": "Defense"
},
{
"name": "Ghost's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"agi": 3,
"aDamPct": 2,
"fixID": false,
"id": 43,
"alias": "Agility"
},
{
"name": "Psychopath's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"str": 2,
"dex": 2,
"fixID": false,
"id": 44,
"alias": "ET"
},
{
"name": "Loner's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"str": 2,
"int": 2,
"fixID": false,
"id": 45,
"alias": "EW"
},
{
"name": "Warlock's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"dex": 2,
"int": 2,
"fixID": false,
"id": 46,
"alias": "TW"
},
{
"name": "Destroyer's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"str": 2,
"def": 2,
"fixID": false,
"id": 47,
"alias": "EF"
},
{
"name": "Devil's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"dex": 2,
"def": 2,
"fixID": false,
"id": 48,
"alias": "TF"
},
{
"name": "Alchemist's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"int": 2,
"def": 2,
"fixID": false,
"id": 49,
"alias": "WF"
},
{
"name": "Barbarian's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"str": 2,
"agi": 2,
"fixID": false,
"id": 50,
"alias": "EA"
},
{
"name": "Freelancer's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"dex": 2,
"agi": 2,
"fixID": false,
"id": 51,
"alias": "TA"
},
{
"name": "Sycophant's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"int": 2,
"agi": 2,
"fixID": false,
"id": 52,
"alias": "WA"
},
{
"name": "Fanatic's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"def": 2,
"agi": 2,
"fixID": false,
"id": 53,
"alias": "FA"
},
{
"name": "Assimilator's Tome of Allegiance",
"tier": "Legendary",
"type": "guildTome",
"category": "tome",
"drop": "never",
"restrict": "Untradable",
"lvl": 100,
"str": 1,
"dex": 1,
"int": 1,
"def": 1,
"agi": 1,
"fixID": false,
"id": 54,
"alias": "Rainbow"
}
]
}

1
data/2.0.1.2/atree.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
data/2.0.1.2/items.json Normal file

File diff suppressed because one or more lines are too long

1069
data/2.0.1.2/tomes.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -143,11 +143,82 @@
</div> </div>
<p> <p>
All build links will end in "#[version number]_[build hash]". All build links starting from Version 8 may look like "?v=[wynn version number]#[version number]_[build hash]".
The query section is optional.
</p> </p>
<p>
All build links older than Version 8 look like "#[version number]_[build hash]".
</p>
<div class="row section" title="Version 8">
<p>
Version 8 was made to account for an oversight made when designing the tome encoding, and to signal the beginning of versions that support wynn version history. Version 8 and higher links may start with a query indicating the wynn version to use.
</p>
<p>
Additionally, Version 8 uses 2 characters instead of 1 character to encode the ID of each tome. (The IDs representing NONE tomes does not change.)
</p>
</div>
<div class="row section" title="Version 7">
<p>
Version 7 was made to account for ability trees in wynncraft 2.0. Version 7 introduces a new field at the end of the build hash, for ability tree data. Atree data is compressed using a depth first search algorithm into a binary blob, which is then base-64 encoded and appended to the V6 hash. The binry blob is decompressed by using the corresponding traversal.
</p>
<p>
The tree structure defining the ability tree can be found in a json file (by default, <code>js/builder/atree_constants_min.json</code>). The file defines connections between the ability tree nodes: Each node has an unordered list of parent nodes, and an ordered list of child nodes.
</p>
<p>
A reference implementation of the encoding/decoding algorithms can be found in <code>js/builder/build_encode_decode.js</code>. A pseudocode description of the algorithms is given here:
</p>
<code style="white-space: pre">
// Encode an ability tree configuration into a binary blob.
// NOTE: this algorithm only works for "connected" (valid) ability trees.
// Its behavior is not well defined otherwise.
//
// Parameters:
// tree_data: Object containing ability tree structure.
// tree_state: Object containing info about which abilities are selected.
function encode(tree_data, tree_state):
return_vector = BitVector()
visited = Set()
function recursive_traverse(head_node):
for each child of head_node, in order:
if child is not in visited:
add child to visited set
if tree_state.is_active(child):
append bit 1 to return_vector
recursive_traverse(child)
else:
append bit 0 to return_vector
recursive_traverse(tree_data.root)
return return_vector
// Decode a binary blob into an ability tree configuration
//
// Parameters:
// tree_data: Object containing ability tree structure.
// tree_state: Object containing info about which abilities are selected.
// tree_bitvector: raw binary data, accessed one bit at a time
function decode(tree_data, tree_state, tree_bitvector):
i = 0
visited = Set()
function recursive_traverse(head_node):
for each child of head_node, in order:
if child is not in visited:
add child to visited set
if tree_bitvector[i]:
child.active = True
recursive_traverse(child)
else:
child.active = False
i = i + 1
</code>
</div>
<div class="row section" title="Version 6"> <div class="row section" title="Version 6">
<p> <p>
Version 6 was made to account for the desire to save tomes in a build. As of the last version of this documentation, version 6 is used for encoding whenever there are tomes in the build. Version 6 was made to account for the desire to save tomes in a build.
</p> </p>
<div class = "row section" title = "Example 1: With Tomes"> <div class = "row section" title = "Example 1: With Tomes">
<code class="full-width"> <code class="full-width">
@ -959,7 +1030,7 @@
</div> --> </div> -->
</div> </div>
<script type="text/javascript" src="../js/dev.js"></script> <script type="text/javascript" src="../js/dev.js"></script>
<script type="text/javascript" src="../js/sq2icons.js"></script> <script type="text/javascript" src="../js/icons.js"></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -63,6 +63,7 @@
<script type="text/javascript" src="/js/load_ing.js"></script> <script type="text/javascript" src="/js/load_ing.js"></script>
<script type="text/javascript" src="/js/display_constants.js"></script> <script type="text/javascript" src="/js/display_constants.js"></script>
<script type="text/javascript" src="/js/display.js"></script> <script type="text/javascript" src="/js/display.js"></script>
<script type="text/javascript" src="/js/item_display.js"></script>
<script type="text/javascript" src="/js/item.js"></script> <script type="text/javascript" src="/js/item.js"></script>
</body> </body>
</html> </html>

View file

@ -18,6 +18,7 @@
<link rel="stylesheet" href="../css/sq2bs.css"> <link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/sidebar.css"> <link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css"> <link rel="stylesheet" href="../css/wynnstyles.css">
<link rel="stylesheet" href="../css/items.css">
</head> </head>
<body class = "text-light d-flex justify-content-center" id = "body"> <body class = "text-light d-flex justify-content-center" id = "body">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow"> <div id="main-sidebar" class="sidebar dark-7 dark-shadow">
@ -31,100 +32,127 @@
<hr/> <hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a> <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>
<div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class = "container py-5 vh-100 mx-0 mx-lg-auto scaled-font"> <div class = "container py-5 vh-100 mx-0 mx-lg-auto scaled-font">
<div class = "col"> <div class = "row">
<div class = "row"> <div class = "row" style = "margin-top: 3ch;">
<div class = "col text-end"> <div class = "col text-end">
<a href = "../items_adv/">Advanced Item Search</a> <a href = "../items_adv/">Advanced Item Search</a>
</div> </div>
</div> </div>
<div class = "row"> <div class = "col-auto" style = "width: 12.5%;"></div>
<div class = "col-lg-4">
<div class = "row"> <div class = "row">
<div class = "col-lg-3 col-sm-12"> <div class = "col-lg col-sm-12">
<div class = "col fw-bold">Name:</div> <div class = "col fw-bold">Name:</div>
<input class = "col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" type="text" id="item-name-choice" name="item-name-choice" placeholder="Item name (case insensitive)"/> <input class = "col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" style="width: 100%!important;" type="text" id="item-name-choice" name="item-name-choice" placeholder="Item name (case insensitive)"/>
<p class="error col"></p>
</div>
<div class = "col-lg-3 col-sm-12">
<div class = "col fw-bold">Category:</div>
<input class="col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="item-category-choice" name="item-category-choice" placeholder="ALL"/>
<datalist id="category-items">
<option value="ALL">
<option value="armor">
<option value="helmet">
<option value="chestplate">
<option value="leggings">
<option value="boots">
<option value="accessory">
<option value="ring">
<option value="bracelet">
<option value="necklace">
<option value="weapon">
<option value="wand">
<option value="spear">
<option value="bow">
<option value="dagger">
<option value="relik">
</datalist>
<p class="error"></p>
</div>
<div class = "col-lg-3 col-sm-12">
<div class = "col fw-bold">Rarity:</div>
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="item-rarity-choice" name="item-rarity-choice" placeholder="ANY"/>
<datalist id = "rarity-items">
<option value="ANY">
<option value="Normal">
<option value="Unique">
<option value="Set">
<option value="Rare">
<option value="Legendary">
<option value="Fabled">
<option value="Mythic">
<option value="Sane">
</datalist>
<p class="error col-auto"></p>
</div>
<div class = "col-lg-3 col-sm-12">
<div class = "col fw-bold">Level Range:</div>
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" type="text" id="item-level-choice" name="item-level-choice" placeholder = "1-106"/>
<p class="error col-auto"></p> <p class="error col-auto"></p>
</div> </div>
</div> </div>
<div class = "row"> <div class = "row">
<div class = "col-lg-3 col-sm-12"> <div class = "col-lg col-sm-12">
<div class = "col fw-bold">Filter 1:</div> <div class = "col"><span class = "fw-bold">Type:</span> <span style = "cursor: pointer; float: right;"><span id = "all-types">All</span> &nbsp; <span id = "none-types">None</span></span></div>
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter1-choice" name="filter1-choice" placeholder="ANY"/> <div id = "type-box">
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 0 0;" id = "type-bow"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 9.09091% 0;" id = "type-spear"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 18.1818% 0;" id = "type-wand"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 27.2727% 0;" id = "type-dagger"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 36.3636% 0;" id = "type-relik"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 45.4545% 0;" id = "type-helmet"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 54.5455% 0;" id = "type-chestplate"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 63.6364% 0;" id = "type-leggings"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 72.7273% 0;" id = "type-boots"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 81.8182% 0;" id = "type-ring"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 90.9091% 0;" id = "type-bracelet"></div>
<div class = "item-display-new-toggleable" style = "background-image: url('../media/items/new.png'); background-position: 100% 0;" id = "type-necklace"></div>
</div>
<p class="error col-auto"></p> <p class="error col-auto"></p>
</div> </div>
<div class = "col-lg-3 col-sm-12">
<div class = "col fw-bold">Filter 2:</div>
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter2-choice" name="filter2-choice" placeholder="ANY"/>
<p class="error col-auto"></p>
</div>
<div class = "col-lg-3 col-sm-12">
<div class = "col fw-bold">Filter 3:</div>
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter3-choice" name="filter3-choice" placeholder="ANY"/>
<p class="error col-auto"></p>
</div>
<div class = "col-lg-3 col-sm-12">
<div class = "col fw-bold">Filter 4:</div>
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter4-choice" name="filter4-choice" placeholder="ANY"/>
<p class="error col-auto"></p>
</div>
<datalist id = "filter-items"></datalist>
</div> </div>
<div class = "row"> <div class = "row">
<div class = "col-auto"> <div class = "col-lg col-sm-12">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "search-button" onclick = "doItemSearch()"> <div class = "col"><span class = "fw-bold">Rarity:</span> <span style = "cursor: pointer; float: right;"><span id = "all-rarities">All</span> &nbsp; <span id = "none-rarities">None</span></span></div>
Search! <div id = "rarity-box">
</button> <!-- unfortunately they must be stacked up like this because newlines are considered spaces and muck up the organization -->
<div id = "rarity-normal" class = "rarity-selected"><b>N</b></div><div id = "rarity-unique" class = "rarity-selected"><b>U</b></div><div id = "rarity-set" class = "rarity-selected"><b>S</b></div><div id = "rarity-rare" class = "rarity-selected"><b>R</b></div><div id = "rarity-legendary" class = "rarity-selected"><b>L</b></div><div id = "rarity-fabled" class = "rarity-selected"><b>F</b></div><div id = "rarity-mythic" class = "rarity-selected"><b>M</b></div>
</div>
<p class="error col-auto"></p>
</div> </div>
<div class = "col-auto"> </div>
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "resetItemSearch()"> </div>
Reset <div class = "col-lg-5">
</button> <div id = "filter-container" class = "col">
<div class = "col fw-bold">Filters:</div>
<div class = "row">
<div id = "add-filter" class = "col fw-bold" style = "cursor: pointer; padding-top: 5px;">
+ Add Filter
</div>
</div> </div>
</div> </div>
<br/>
<div id = "exclude-container" class = "col">
<div class = "col fw-bold">Excluded Filters:</div>
<div class = "row">
<div id = "add-exclude" class = "col fw-bold" style = "cursor: pointer; padding-top: 5px;">
+ Add Excluded Filter
</div>
</div>
</div>
</div>
<div class = "row">
<div class = "col-auto" style = "width: 12.5%;"></div>
<div class = "col-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "search-button" onclick = "do_item_search()">
Search!
</button>
</div>
<div class = "col-auto">
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "reset_item_search()">
Reset
</button>
</div>
</div> </div>
<div class = "row box-title justify-content-center" id = "summary"> <div class = "row box-title justify-content-center" id = "summary">
</div> </div>
@ -134,6 +162,7 @@
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
<script type="text/javascript" src="../js/drag_drop_touch.js"></script>
<script type="text/javascript" src="../js/utils.js"></script> <script type="text/javascript" src="../js/utils.js"></script>
<script type="text/javascript" src="../js/build_utils.js"></script> <script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/icons.js"></script> <script type="text/javascript" src="../js/icons.js"></script>
@ -141,10 +170,9 @@
<script type="text/javascript" src="../js/display_constants.js"></script> <script type="text/javascript" src="../js/display_constants.js"></script>
<script type="text/javascript" src="../js/display.js"></script> <script type="text/javascript" src="../js/display.js"></script>
<script type="text/javascript" src="../js/query.js"></script> <script type="text/javascript" src="../js/query.js"></script>
<script type="text/javascript" src="../js/query_2.js"></script>
<script type="text/javascript" src="../js/expr_parser.js"></script> <script type="text/javascript" src="../js/expr_parser.js"></script>
<script type="text/javascript" src="../js/load.js"></script> <script type="text/javascript" src="../js/load.js"></script>
<script type="text/javascript" src="../js/sq2items.js"></script> <script type="text/javascript" src="../js/items.js"></script>
<script type="text/javascript" src="../js/powders.js"></script> <script type="text/javascript" src="../js/powders.js"></script>
</body> </body>
</html> </html>

View file

@ -16,7 +16,7 @@
<link rel="stylesheet" href="../css/sq2bs.css"> <link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/items_2.css"> <link rel="stylesheet" href="../css/items_adv.css">
<link rel="stylesheet" href="../css/sidebar.css"> <link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css"> <link rel="stylesheet" href="../css/wynnstyles.css">
</head> </head>
@ -39,7 +39,7 @@
<a href="../credits.txt" class="link">Additional credits</a> <a href="../credits.txt" class="link">Additional credits</a>
</div> </div>
<div class = "col text-center" id = "help"> <div class = "col text-center" id = "help">
<a href="items_2_help.html" class="link" target="_blank">Search Guide</a> <a href="items_adv_help.html" class="link" target="_blank">Search Guide</a>
</div> </div>
<div class = "col text-end"> <div class = "col text-end">
<a href = "../items/">Basic Item Search</a> <a href = "../items/">Basic Item Search</a>
@ -77,12 +77,13 @@
<script type="text/javascript" src="/js/utils.js"></script> <script type="text/javascript" src="/js/utils.js"></script>
<script type="text/javascript" src="/js/build_utils.js"></script> <script type="text/javascript" src="/js/build_utils.js"></script>
<script type="text/javascript" src="/js/icons.js"></script> <script type="text/javascript" src="/js/icons.js"></script>
<script type="text/javascript" src="/js/powders.js"></script>
<script type="text/javascript" src="/js/damage_calc.js"></script> <script type="text/javascript" src="/js/damage_calc.js"></script>
<script type="text/javascript" src="/js/display_constants.js"></script> <script type="text/javascript" src="/js/display_constants.js"></script>
<script type="text/javascript" src="/js/display.js"></script> <script type="text/javascript" src="/js/display.js"></script>
<script type="text/javascript" src="/js/query_2.js"></script> <script type="text/javascript" src="/js/query.js"></script>
<script type="text/javascript" src="/js/expr_parser.js"></script> <script type="text/javascript" src="/js/expr_parser.js"></script>
<script type="text/javascript" src="/js/load.js"></script> <script type="text/javascript" src="/js/load.js"></script>
<script type="text/javascript" src="/js/items_2.js"></script> <script type="text/javascript" src="/js/items_adv.js"></script>
</body> </body>
</html> </html>

View file

@ -221,6 +221,6 @@
docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2])); docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2]));
} }
</script> </script>
<script type="text/javascript" src="../js/sq2icons.js"></script> <script type="text/javascript" src="../js/icons.js"></script>
</body> </body>
</html> </html>

View file

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

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,7 @@
/**
* File containing utility functions that are useful for the builder page.
*/
/*Turns the input amount of skill points into a float precision percentage. /*Turns the input amount of skill points into a float precision percentage.
* @param skp - the integer skillpoint count to be converted * @param skp - the integer skillpoint count to be converted
*/ */
@ -8,7 +12,7 @@ function skillPointsToPercentage(skp){
skp = 150; skp = 150;
} }
const r = 0.9908; const r = 0.9908;
return ((1 - Math.pow(r, skp + 1)) / (1 - r) - 1) / 100.0; return (r/(1-r)*(1 - Math.pow(r, skp))) / 100.0;
//return (-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771); //return (-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771);
//return(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771).toFixed(3); //return(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771).toFixed(3);
//return Math.min(Math.max(0.00,(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771)),.808); //return Math.min(Math.max(0.00,(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771)),.808);
@ -66,7 +70,7 @@ const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set
const all_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tome_types).map(x => x.substring(0,1).toUpperCase() + x.substring(1)); const all_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tome_types).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
//weaponTypes.push("sword"); //weaponTypes.push("sword");
//console.log(types) //console.log(types)
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tome_types); let item_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tome_types);
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ]; let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
let skpReqs = skp_order.map(x => x + "Req"); let skpReqs = skp_order.map(x => x + "Req");
@ -82,14 +86,16 @@ let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slot
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element "nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional. /*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw "rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
"critDamPct" "critDamPct",
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
]; ];
// Extra fake IDs (reserved for use in spell damage calculation) : damMult, defMult, poisonPct, activeMajorIDs // Extra fake IDs (reserved for use in spell damage calculation) : damMult, defMult, poisonPct, activeMajorIDs
let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ] let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ]
//File reading for ID translations for JSON purposes //File reading for ID translations for JSON purposes
let reversetranslations = new Map(); let reversetranslations = new Map();
let translations = new Map([["name", "name"], ["displayName", "displayName"], ["tier", "tier"], ["set", "set"], ["sockets", "slots"], ["type", "type"], ["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"], ["spellDamage", "sdPct"], ["damageBonus", "mdPct"], ["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"], ["spellDamageRaw", "sdRaw"], ["damageBonusRaw", "mdRaw"], ["bonusFireDamage", "fDamPct"], ["bonusWaterDamage", "wDamPct"], ["bonusAirDamage", "aDamPct"], ["bonusThunderDamage", "tDamPct"], ["bonusEarthDamage", "eDamPct"], ["bonusFireDefense", "fDefPct"], ["bonusWaterDefense", "wDefPct"], ["bonusAirDefense", "aDefPct"], ["bonusThunderDefense", "tDefPct"], ["bonusEarthDefense", "eDefPct"], ["type", "type"], ["identified", "fixID"], ["skin", "skin"], ["category", "category"], ["spellCostPct1", "spPct1"], ["spellCostRaw1", "spRaw1"], ["spellCostPct2", "spPct2"], ["spellCostRaw2", "spRaw2"], ["spellCostPct3", "spPct3"], ["spellCostRaw3", "spRaw3"], ["spellCostPct4", "spPct4"], ["spellCostRaw4", "spRaw4"], ["rainbowSpellDamageRaw", "rSdRaw"], ["sprint", "sprint"], ["sprintRegen", "sprintReg"], ["jumpHeight", "jh"], ["lootQuality", "lq"], ["gatherXpBonus", "gXp"], ["gatherSpeed", "gSpd"]]); let translations = new Map([["name", "name"],["displayName", "displayName"],["tier", "tier"],["set", "set"],["sockets", "slots"],["type", "type"],["armorColor", "color"],["addedLore", "lore"],["dropType", "drop"],["quest", "quest"],["restrictions", "restrict"],["damage", "nDam"],["fireDamage", "fDam"],["waterDamage", "wDam"],["airDamage", "aDam"],["thunderDamage", "tDam"],["earthDamage", "eDam"],["attackSpeed", "atkSpd"],["health", "hp"],["fireDefense", "fDef"],["waterDefense", "wDef"],["airDefense", "aDef"],["thunderDefense", "tDef"],["earthDefense", "eDef"],["level", "lvl"],["classRequirement", "classReq"],["strength", "strReq"],["dexterity", "dexReq"],["intelligence", "intReq"],["agility", "agiReq"],["defense", "defReq"],["healthRegen", "hprPct"],["manaRegen", "mr"],["spellDamageBonus", "sdPct"],["spellElementalDamageBonus", "rSdPct"],["spellNeutralDamageBonus", "nSdPct"],["spellFireDamageBonus", "fSdPct"],["spellWaterDamageBonus", "wSdPct"],["spellAirDamageBonus", "aSdPct"],["spellThunderDamageBonus", "tSdPct"],["spellEarthDamageBonus", "eSdPct"],["mainAttackDamageBonus", "mdPct"],["mainAttackElementalDamageBonus", "rMdPct"],["mainAttackNeutralDamageBonus", "nMdPct"],["mainAttackFireDamageBonus", "fMdPct"],["mainAttackWaterDamageBonus", "wMdPct"],["mainAttackAirDamageBonus", "aMdPct"],["mainAttackThunderDamageBonus", "tMdPct"],["mainAttackEarthDamageBonus", "eMdPct"],["lifeSteal", "ls"],["manaSteal", "ms"],["xpBonus", "xpb"],["lootBonus", "lb"],["reflection", "ref"],["strengthPoints", "str"],["dexterityPoints", "dex"],["intelligencePoints", "int"],["agilityPoints", "agi"],["defensePoints", "def"],["thorns", "thorns"],["exploding", "expd"],["speed", "spd"],["attackSpeedBonus", "atkTier"],["poison", "poison"],["healthBonus", "hpBonus"],["soulPoints", "spRegen"],["emeraldStealing", "eSteal"],["healthRegenRaw", "hprRaw"],["spellDamageBonusRaw", "sdRaw"],["spellElementalDamageBonusRaw", "rSdRaw"],["spellNeutralDamageBonusRaw", "nSdRaw"],["spellFireDamageBonusRaw", "fSdRaw"],["spellWaterDamageBonusRaw", "wSdRaw"],["spellAirDamageBonusRaw", "aSdRaw"],["spellThunderDamageBonusRaw", "tSdRaw"],["spellEarthDamageBonusRaw", "eSdRaw"],["mainAttackDamageBonusRaw", "mdRaw"],["mainAttackElementalDamageBonusRaw", "rMdRaw"],["mainAttackNeutralDamageBonusRaw", "nMdRaw"],["mainAttackFireDamageBonusRaw", "fMdRaw"],["mainAttackWaterDamageBonusRaw", "wMdRaw"],["mainAttackAirDamageBonusRaw", "aMdRaw"],["mainAttackThunderDamageBonusRaw", "tMdRaw"],["mainAttackEarthDamageBonusRaw", "eMdRaw"],["fireDamageBonus", "fDamPct"],["waterDamageBonus", "wDamPct"],["airDamageBonus", "aDamPct"],["thunderDamageBonus", "tDamPct"],["earthDamageBonus", "eDamPct"],["bonusFireDefense", "fDefPct"],["bonusWaterDefense", "wDefPct"],["bonusAirDefense", "aDefPct"],["bonusThunderDefense", "tDefPct"],["bonusEarthDefense", "eDefPct"],["accessoryType", "type"],["identified", "fixID"],["skin", "skin"],["category", "category"],["spellCostPct1", "spPct1"],["spellCostRaw1", "spRaw1"],["spellCostPct2", "spPct2"],["spellCostRaw2", "spRaw2"],["spellCostPct3", "spPct3"],["spellCostRaw3", "spRaw3"],["spellCostPct4", "spPct4"],["spellCostRaw4", "spRaw4"],["sprint", "sprint"],["sprintRegen", "sprintReg"],["jumpHeight", "jh"],["lootQuality", "lq"],["gatherXpBonus", "gXp"],["gatherSpeed", "gSpd"]]);
//does not include damMobs (wep tomes) and defMobs (armor tomes) //does not include damMobs (wep tomes) and defMobs (armor tomes)
for (const [k, v] of translations) { for (const [k, v] of translations) {
reversetranslations.set(v, k); reversetranslations.set(v, k);
@ -123,16 +129,7 @@ let nonRolledIDs = [
"nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_",
"majorIds", "majorIds",
"damMobs", "damMobs",
"defMobs", "defMobs"
// wynn2 damages.
"eDamAddMin","eDamAddMax",
"tDamAddMin","tDamAddMax",
"wDamAddMin","wDamAddMax",
"fDamAddMin","fDamAddMax",
"aDamAddMin","aDamAddMax",
"nDamAddMin","nDamAddMax", // neutral which is now an element
"damAddMin","damAddMax", // all
"rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral).
]; ];
let rolledIDs = [ let rolledIDs = [
"hprPct", "hprPct",
@ -161,7 +158,7 @@ let rolledIDs = [
"spPct2", "spRaw2", "spPct2", "spRaw2",
"spPct3", "spRaw3", "spPct3", "spRaw3",
"spPct4", "spRaw4", "spPct4", "spRaw4",
"pDamRaw", "rSdRaw",
"sprint", "sprint",
"sprintReg", "sprintReg",
"jh", "jh",
@ -176,7 +173,8 @@ let rolledIDs = [
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw","aDamAddMin","aDamAddMax", "aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw","aDamAddMin","aDamAddMax",
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element "nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional. /*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral). rSdRaw is rainraw "rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
]; ];
let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ]; let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ];
@ -199,7 +197,7 @@ function expandItem(item) {
let val = (item[id] || 0); let val = (item[id] || 0);
if (val > 0) { // positive rolled IDs if (val > 0) { // positive rolled IDs
if (reversedIDs.includes(id)) { if (reversedIDs.includes(id)) {
maxRolls.set(id,idRound(val*0.3)); maxRolls.set(id,idRound(val*0.7));
minRolls.set(id,idRound(val*1.3)); minRolls.set(id,idRound(val*1.3));
} else { } else {
maxRolls.set(id,idRound(val*1.3)); maxRolls.set(id,idRound(val*1.3));
@ -208,7 +206,7 @@ function expandItem(item) {
} else if (val < 0) { //negative rolled IDs } else if (val < 0) { //negative rolled IDs
if (reversedIDs.includes(id)) { if (reversedIDs.includes(id)) {
maxRolls.set(id,idRound(val*1.3)); maxRolls.set(id,idRound(val*1.3));
minRolls.set(id,idRound(val*0.7)); minRolls.set(id,idRound(val*0.3));
} }
else { else {
maxRolls.set(id,idRound(val*0.7)); maxRolls.set(id,idRound(val*0.7));
@ -231,8 +229,15 @@ function expandItem(item) {
} }
class Item { class Item {
constructor(item_obj) { constructor(item_obj = null) {
this.statMap = expandItem(item_obj); if (item_obj) { this.statMap = expandItem(item_obj); }
else { this.statMap = new Map(); }
}
copy() {
const ret = new Item();
ret.statMap = new Map(this.statMap);
return ret;
} }
} }

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,11 @@
/**
* This file defines a class representing the player Build.
*
* Keeps track of equipment list, equip order, skillpoint assignment (initial),
* Aggregates item stats into a statMap to be used in damage calculation.
*/
const classDefenseMultipliers = new Map([ ["relik",0.60], ["bow",0.70], ["wand", 0.80], ["dagger", 1.0], ["spear", 1.0]]);
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.0], ["sword", 1.10]]);
/* /*
* Class that represents a wynn player's build. * Class that represents a wynn player's build.
@ -10,16 +15,11 @@ class Build{
/** /**
* @description Construct a build. * @description Construct a build.
* @param {Number} level : Level of the player. * @param {Number} level : Level of the player.
* @param {String[]} equipment : List of equipment names that make up the build. * @param {String[]} items: List of equipment names that make up the build.
* In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck * In order: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Tomes [x7].
* * @param {Item} weapon: Weapon that this build is using.
* @param {Object[]} tomes: List of tomes.
* In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome.
* 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building)
*
* @param {Object} weapon: Weapon.
*/ */
constructor(level, equipment, tomes, weapon){ constructor(level, items, weapon){
if (level < 1) { //Should these be constants? if (level < 1) { //Should these be constants?
this.level = 1; this.level = 1;
} else if (level > 106) { } else if (level > 106) {
@ -34,15 +34,18 @@ class Build{
document.getElementById("level-choice").value = this.level; document.getElementById("level-choice").value = this.level;
this.availableSkillpoints = levelToSkillPoints(this.level); this.availableSkillpoints = levelToSkillPoints(this.level);
this.equipment = equipment; this.equipment = items;
this.tomes = tomes;
this.weapon = weapon; this.weapon = weapon;
this.items = this.equipment.concat(tomes, [this.weapon]); this.items = this.equipment.concat([this.weapon]);
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
// calc skillpoints requires statmaps only // calc skillpoints requires statmaps only
let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap); let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap);
this.equip_order = result[0]; const _equip_order = result[0].slice();
this.equip_order = [];
for (const item of _equip_order) {
if (item.get('category') === 'tome' || item.has('NONE')) { continue; }
this.equip_order.push(item);
}
// How many skillpoints the player had to assign (5 number) // How many skillpoints the player had to assign (5 number)
this.base_skillpoints = result[1]; this.base_skillpoints = result[1];
// How many skillpoints the build ended up with (5 number) // How many skillpoints the build ended up with (5 number)
@ -60,7 +63,6 @@ class Build{
return [this.equipment,this.weapon].flat(); return [this.equipment,this.weapon].flat();
} }
/* Get all stats for this build. Stores in this.statMap. /* Get all stats for this build. Stores in this.statMap.
@pre The build itself should be valid. No checking of validity of pieces is done here. @pre The build itself should be valid. No checking of validity of pieces is done here.
*/ */
@ -126,9 +128,9 @@ class Build{
} }
} }
} }
statMap.set("poisonPct", 100); statMap.set("poisonPct", 0);
statMap.set("critDamPct", 100); statMap.set("critDamPct", 0);
statMap.set("healPct", 100); statMap.set("healPct", 0);
// The stuff relevant for damage calculation!!! @ferricles // The stuff relevant for damage calculation!!! @ferricles
statMap.set("atkSpd", this.weapon.statMap.get("atkSpd")); statMap.set("atkSpd", this.weapon.statMap.get("atkSpd"));

View file

@ -1,3 +1,6 @@
/**
* File containing utility functions relevant to the builder page, as well as the setup code (at the very bottom).
*/
function populateBuildList() { function populateBuildList() {
const buildList = document.getElementById("build-choice"); const buildList = document.getElementById("build-choice");
@ -44,6 +47,8 @@ function loadBuild() {
let saveName = document.getElementById("build-name").value; let saveName = document.getElementById("build-name").value;
if (Object.keys(savedBuilds).includes(saveName)) { if (Object.keys(savedBuilds).includes(saveName)) {
// NOTE: this is broken since decodeBuild is async func.
// Doubly broken because of versioning... lets just kill this for now
decodeBuild(savedBuilds[saveName]) decodeBuild(savedBuilds[saveName])
document.getElementById("loaded-error").textContent = ""; document.getElementById("loaded-error").textContent = "";
document.getElementById("loaded-build").textContent = "Build loaded"; document.getElementById("loaded-build").textContent = "Build loaded";
@ -60,6 +65,9 @@ function resetFields(){
for (const i of equipment_inputs) { for (const i of equipment_inputs) {
setValue(i, ""); setValue(i, "");
} }
for (const i of tomeInputs) {
setValue(i, "");
}
setValue("str-skp", "0"); setValue("str-skp", "0");
setValue("dex-skp", "0"); setValue("dex-skp", "0");
setValue("int-skp", "0"); setValue("int-skp", "0");
@ -124,34 +132,6 @@ function toggleButton(button_id) {
} }
} }
// toggle tab
function toggle_tab(tab) {
if (document.querySelector("#"+tab).style.display == "none") {
document.querySelector("#"+tab).style.display = "";
} else {
document.querySelector("#"+tab).style.display = "none";
}
}
function toggle_boost_tab(tab) {
for (const i of skp_order) {
document.querySelector("#"+i+"-boost").style.display = "none";
document.getElementById(i + "-boost-tab").classList.remove("selected-btn");
}
document.querySelector("#"+tab+"-boost").style.display = "";
document.getElementById(tab + "-boost-tab").classList.add("selected-btn");
}
let tabs = ['overall-stats', 'offensive-stats', 'defensive-stats'];
function show_tab(tab) {
//hide all tabs, then show the tab of the div clicked and highlight the correct button
for (const i in tabs) {
document.querySelector("#" + tabs[i]).style.display = "none";
document.getElementById("tab-" + tabs[i].split("-")[0] + "-btn").classList.remove("selected-btn");
}
document.querySelector("#" + tab).style.display = "";
document.getElementById("tab-" + tab.split("-")[0] + "-btn").classList.add("selected-btn");
}
// autocomplete initialize // autocomplete initialize
function init_autocomplete() { function init_autocomplete() {
@ -239,8 +219,9 @@ function init_autocomplete() {
for (const eq of tome_keys) { for (const eq of tome_keys) {
// build dropdown // build dropdown
let tome_arr = []; let tome_arr = [];
for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) { let tome_aliases = new Map();
let tome_obj = tomeMap.get(tome); for (const tome_name of tomeLists.get(eq.replace(/[0-9]/g, ''))) {
let tome_obj = tomeMap.get(tome_name);
if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") { if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") {
continue; continue;
} }
@ -248,8 +229,10 @@ function init_autocomplete() {
if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) { if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) {
continue; continue;
} }
let tome_name = tome; let tome_alias = tome_obj['alias'];
tome_arr.push(tome_name); tome_arr.push(tome_name);
tome_arr.push(tome_alias);
tome_aliases.set(tome_alias, tome_name);
} }
// create dropdown // create dropdown
@ -284,14 +267,18 @@ function init_autocomplete() {
class: "scaled-font search-item", class: "scaled-font search-item",
selected: "dark-5", selected: "dark-5",
element: (tome, data) => { element: (tome, data) => {
tome.classList.add(tomeMap.get(data.value).tier); let val = data.value;
if (tome_aliases.has(val)) { val = tome_aliases.get(val); }
tome.classList.add(tomeMap.get(val).tier);
}, },
}, },
events: { events: {
input: { input: {
selection: (event) => { selection: (event) => {
if (event.detail.selection.value) { if (event.detail.selection.value) {
event.target.value = event.detail.selection.value; let val = event.detail.selection.value;
if (tome_aliases.has(val)) { val = tome_aliases.get(val); }
event.target.value = val;
} }
event.target.dispatchEvent(new Event('change')); event.target.dispatchEvent(new Event('change'));
}, },
@ -320,9 +307,8 @@ function collapse_element(elmnt) {
document.querySelector(elmnt).style.removeProperty('display'); document.querySelector(elmnt).style.removeProperty('display');
} }
function init() { async function init() {
console.log("builder.js init"); console.log("builder.js init");
init_autocomplete();
// Other "main" stuff // Other "main" stuff
// Spell dropdowns // Spell dropdowns
@ -344,34 +330,41 @@ function init() {
} }
// Masonry setup // Masonry setup
let masonry = Macy({ try {
container: "#masonry-container", let masonry = Macy({
columns: 1, container: "#masonry-container",
mobileFirst: true, columns: 1,
breakAt: { mobileFirst: true,
1200: 4, breakAt: {
}, 1200: 4,
margin: { },
x: 20, margin: {
y: 20, x: 20,
} y: 20,
}
}); });
let search_masonry = Macy({ let search_masonry = Macy({
container: "#search-results", container: "#search-results",
columns: 1, columns: 1,
mobileFirst: true, mobileFirst: true,
breakAt: { breakAt: {
1200: 4, 1200: 4,
}, },
margin: { margin: {
x: 20, x: 20,
y: 20, y: 20,
} }
});
}); } catch (e) {
decodeBuild(url_tag); console.log("Could not initialize macy components. Maybe you're offline?");
}
await parse_hash(url_tag);
try {
init_autocomplete();
} catch (e) {
console.log("Could not initialize autocomplete. Maybe you're offline?");
}
builder_graph_init(); builder_graph_init();
for (const item_node of item_nodes) { for (const item_node of item_nodes) {
if (item_node.get_value() === null) { if (item_node.get_value() === null) {
@ -379,6 +372,7 @@ function init() {
if (confirm('One or more items failed to load correctly. This could be due to a corrupted build link, or (more likely) a database load failure. Would you like to reload?')) { if (confirm('One or more items failed to load correctly. This could be due to a corrupted build link, or (more likely) a database load failure. Would you like to reload?')) {
hardReload(); hardReload();
} }
console.log(item_node);
break; break;
} }
} }
@ -390,7 +384,5 @@ window.onerror = function(message, source, lineno, colno, error) {
}; };
(async function() { (async function() {
let load_promises = [ load_init(), load_ing_init(), load_tome_init() ]; await init();
await Promise.all(load_promises);
init();
})(); })();

View file

@ -1,3 +1,7 @@
/**
* File containing compute graph structure of the builder page.
*/
let armor_powder_node = new (class extends ComputeNode { let armor_powder_node = new (class extends ComputeNode {
constructor() { super('builder-armor-powder-input'); } constructor() { super('builder-armor-powder-input'); }
@ -13,6 +17,8 @@ let armor_powder_node = new (class extends ComputeNode {
} }
})(); })();
const damageMultipliers = new Map([ ["totem", 0.2], ["warscream", 0.0], ["ragnarokkr", 0.30], ["fortitude", 0.60], ["radiance", 0.0] ]);
let boosts_node = new (class extends ComputeNode { let boosts_node = new (class extends ComputeNode {
constructor() { super('builder-boost-input'); } constructor() { super('builder-boost-input'); }
@ -23,8 +29,7 @@ let boosts_node = new (class extends ComputeNode {
let elem = document.getElementById(key + "-boost") let elem = document.getElementById(key + "-boost")
if (elem.classList.contains("toggleOn")) { if (elem.classList.contains("toggleOn")) {
damage_boost += value; damage_boost += value;
if (key === "warscream") { def_boost += .10 } if (key === "warscream") { def_boost += .20 }
if (key === "vanish") { def_boost += .15 }
} }
} }
let res = new Map(); let res = new Map();
@ -90,12 +95,10 @@ class PowderSpecialCalcNode extends ComputeNode {
for (const [special, power] of powder_specials) { for (const [special, power] of powder_specials) {
if (special["weaponSpecialEffects"].has("Damage Boost")) { if (special["weaponSpecialEffects"].has("Damage Boost")) {
let name = special["weaponSpecialName"]; let name = special["weaponSpecialName"];
if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost if (name === "Courage" || name === "Curse" || name == "Wind Prison") { // Master mod all the way
stats.set("sdPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); stats.set("damMult."+name, special.weaponSpecialEffects.get("Damage Boost")[power-1]);
stats.set("mdPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); // legacy
stats.set("poisonPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]); stats.set("poisonPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]);
} else if (name === "Wind Prison") {
stats.set("aDamPct", special.weaponSpecialEffects.get("Damage Boost")[power-1]);
} }
} }
} }
@ -121,7 +124,7 @@ class PowderSpecialDisplayNode extends ComputeNode {
/** /**
* Node for getting an item's stats from an item input field. * Node for getting an item's stats from an item input field.
* *
* Signature: ItemInputNode(powdering: Optional[list[powder]]) => Item | null * Signature: ItemInputNode() => Item | null
*/ */
class ItemInputNode extends InputNode { class ItemInputNode extends InputNode {
/** /**
@ -143,8 +146,6 @@ class ItemInputNode extends InputNode {
} }
compute_func(input_map) { compute_func(input_map) {
const powdering = input_map.get('powdering');
// built on the assumption of no one will type in CI/CR letter by letter // built on the assumption of no one will type in CI/CR letter by letter
let item_text = this.input_field.value; let item_text = this.input_field.value;
if (!item_text) { if (!item_text) {
@ -158,10 +159,6 @@ class ItemInputNode extends InputNode {
else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); } else if (tomeMap.has(item_text)) { item = new Item(tomeMap.get(item_text)); }
if (item) { if (item) {
if (powdering !== undefined) {
const max_slots = item.statMap.get('slots');
item.statMap.set('powders', powdering.slice(0, max_slots));
}
let type_match; let type_match;
if (this.category == 'weapon') { if (this.category == 'weapon') {
type_match = item.statMap.get('category') == 'weapon'; type_match = item.statMap.get('category') == 'weapon';
@ -169,12 +166,6 @@ class ItemInputNode extends InputNode {
type_match = item.statMap.get('type') == this.none_item.statMap.get('type'); type_match = item.statMap.get('type') == this.none_item.statMap.get('type');
} }
if (type_match) { if (type_match) {
if (item.statMap.get('category') == 'armor') {
applyArmorPowders(item.statMap);
}
else if (item.statMap.get('category') == 'weapon') {
apply_weapon_powders(item.statMap);
}
return item; return item;
} }
} }
@ -205,6 +196,31 @@ class ItemInputNode extends InputNode {
} }
} }
/**
* Node for updating item input fields from parsed items.
*
* Signature: ItemInputDisplayNode(item: Item, powdering: List[powder]) => Item
*/
class ItemPowderingNode extends ComputeNode {
constructor(name) { super(name); }
compute_func(input_map) {
const powdering = input_map.get('powdering');
const input_item = input_map.get('item');
const item = input_item.copy(); // TODO: performance
const max_slots = item.statMap.get('slots');
item.statMap.set('powders', powdering.slice(0, max_slots));
if (item.statMap.get('category') == 'armor') {
applyArmorPowders(item.statMap);
}
else if (item.statMap.get('category') == 'weapon') {
apply_weapon_powders(item.statMap);
}
return item;
}
}
/** /**
* Node for updating item input fields from parsed items. * Node for updating item input fields from parsed items.
* *
@ -217,7 +233,6 @@ class ItemInputDisplayNode extends ComputeNode {
this.input_field = document.getElementById(eq+"-choice"); this.input_field = document.getElementById(eq+"-choice");
this.health_field = document.getElementById(eq+"-health"); this.health_field = document.getElementById(eq+"-health");
this.level_field = document.getElementById(eq+"-lv"); this.level_field = document.getElementById(eq+"-lv");
this.powder_field = document.getElementById(eq+"-powder"); // possibly None
this.image = item_image; this.image = item_image;
this.fail_cb = true; this.fail_cb = true;
} }
@ -242,18 +257,11 @@ class ItemInputDisplayNode extends ComputeNode {
this.input_field.classList.add("is-invalid"); this.input_field.classList.add("is-invalid");
return null; return null;
} }
if (this.powder_field && item.statMap.has('powders')) {
this.powder_field.placeholder = "powders";
}
if (item.statMap.has('NONE')) { if (item.statMap.has('NONE')) {
return null; return null;
} }
if (this.powder_field && item.statMap.has('powders')) {
this.powder_field.placeholder = item.statMap.get('slots') + ' slots';
}
const tier = item.statMap.get('tier'); const tier = item.statMap.get('tier');
this.input_field.classList.add(tier); this.input_field.classList.add(tier);
if (this.health_field) { if (this.health_field) {
@ -307,7 +315,8 @@ class WeaponInputDisplayNode extends ComputeNode {
const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
const type = item.statMap.get('type'); const type = item.statMap.get('type');
this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png'); this.image.style.backgroundPosition = itemBGPositions[type];
let dps = get_base_dps(item.statMap); let dps = get_base_dps(item.statMap);
if (isNaN(dps)) { if (isNaN(dps)) {
dps = dps[1]; dps = dps[1];
@ -374,41 +383,39 @@ class URLUpdateNode extends ComputeNode {
* Create a "build" object from a set of equipments. * Create a "build" object from a set of equipments.
* Returns a new Build object, or null if all items are NONE items. * Returns a new Build object, or null if all items are NONE items.
* *
* Signature: BuildAssembleNode(helmet-input: Item, * Signature: BuildAssembleNode(helmet: Item,
* chestplate-input: Item, * chestplate: Item,
* leggings-input: Item, * leggings: Item,
* boots-input: Item, * boots: Item,
* ring1-input: Item, * ring1: Item,
* ring2-input: Item, * ring2: Item,
* bracelet-input: Item, * bracelet: Item,
* necklace-input: Item, * necklace: Item,
* weapon-input: Item, * weapon: Item,
* level-input: int) => Build | null * level: int) => Build | null
*/ */
class BuildAssembleNode extends ComputeNode { class BuildAssembleNode extends ComputeNode {
constructor() { super("builder-make-build"); } constructor() { super("builder-make-build"); }
compute_func(input_map) { compute_func(input_map) {
let equipments = [ let equipments = [
input_map.get('helmet-input'), input_map.get('helmet'),
input_map.get('chestplate-input'), input_map.get('chestplate'),
input_map.get('leggings-input'), input_map.get('leggings'),
input_map.get('boots-input'), input_map.get('boots'),
input_map.get('ring1-input'), input_map.get('ring1'),
input_map.get('ring2-input'), input_map.get('ring2'),
input_map.get('bracelet-input'), input_map.get('bracelet'),
input_map.get('necklace-input') input_map.get('necklace'),
input_map.get('weaponTome1'),
input_map.get('weaponTome2'),
input_map.get('armorTome1'),
input_map.get('armorTome2'),
input_map.get('armorTome3'),
input_map.get('armorTome4'),
input_map.get('guildTome1')
]; ];
let tomes = [ let weapon = input_map.get('weapon');
input_map.get('weaponTome1-input'),
input_map.get('weaponTome2-input'),
input_map.get('armorTome1-input'),
input_map.get('armorTome2-input'),
input_map.get('armorTome3-input'),
input_map.get('armorTome4-input'),
input_map.get('guildTome1-input')
];
let weapon = input_map.get('weapon-input');
let level = parseInt(input_map.get('level-input')); let level = parseInt(input_map.get('level-input'));
if (isNaN(level)) { if (isNaN(level)) {
level = 106; level = 106;
@ -440,15 +447,25 @@ class PlayerClassNode extends ValueCheckComputeNode {
* Read an input field and parse into a list of powderings. * Read an input field and parse into a list of powderings.
* Every two characters makes one powder. If parsing fails, NULL is returned. * Every two characters makes one powder. If parsing fails, NULL is returned.
* *
* Signature: PowderInputNode() => List[powder] | null * Signature: PowderInputNode(item: Item) => List[powder] | null
*/ */
class PowderInputNode extends InputNode { class PowderInputNode extends InputNode {
constructor(name, input_field) { super(name, input_field); } constructor(name, input_field) { super(name, input_field); this.fail_cb = true; }
compute_func(input_map) { compute_func(input_map) {
if (input_map.size !== 1) { throw "PowderInputNode accepts exactly one input (item)"; }
const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
if (item === null) {
this.input_field.placeholder = 'powders';
return [];
}
if (item.statMap.has('slots')) {
this.input_field.placeholder = item.statMap.get('slots') + ' slots';
}
// TODO: haha improve efficiency to O(n) dumb // TODO: haha improve efficiency to O(n) dumb
// also, error handling is missing
let input = this.input_field.value.trim(); let input = this.input_field.value.trim();
let powdering = []; let powdering = [];
let errorederrors = []; let errorederrors = [];
@ -456,12 +473,29 @@ class PowderInputNode extends InputNode {
let first = input.slice(0, 2); let first = input.slice(0, 2);
let powder = powderIDs.get(first); let powder = powderIDs.get(first);
if (powder === undefined) { if (powder === undefined) {
return null; if (first.length > 0) {
errorederrors.push(first);
} else {
break;
}
} else { } else {
powdering.push(powder); powdering.push(powder);
} }
input = input.slice(2); input = input.slice(2);
} }
if (this.input_field.getAttribute("placeholder") != null) {
if (item.statMap.get('slots') < powdering.length) {
errorederrors.push("Too many powders: " + powdering.length);
}
}
if (errorederrors.length) {
this.input_field.classList.add("is-invalid");
} else {
this.input_field.classList.remove("is-invalid");
}
return powdering; return powdering;
} }
} }
@ -566,8 +600,9 @@ class SpellDamageCalcNode extends ComputeNode {
// TODO: move preprocessing to separate node/node chain // TODO: move preprocessing to separate node/node chain
for (const part of spell_parts) { for (const part of spell_parts) {
let spell_result; let spell_result;
const part_id = spell.base_spell + '.' + part.name
if ('multipliers' in part) { // damage type spell if ('multipliers' in part) { // damage type spell
let results = calculateSpellDamage(stats, weapon, part.multipliers, use_spell, !use_speed, spell.base_spell + '.' + part.name); let results = calculateSpellDamage(stats, weapon, part.multipliers, use_spell, !use_speed, part_id);
spell_result = { spell_result = {
type: "damage", type: "damage",
normal_min: results[2].map(x => x[0]), normal_min: results[2].map(x => x[0]),
@ -579,7 +614,10 @@ class SpellDamageCalcNode extends ComputeNode {
} }
} else if ('power' in part) { } else if ('power' in part) {
// TODO: wynn2 formula // TODO: wynn2 formula
let _heal_amount = (part.power * getDefenseStats(stats)[0] * (stats.get('healPct')/100)); let _heal_amount = (part.power * getDefenseStats(stats)[0] * (1+stats.get('healPct')/100));
if (stats.has('healPct:'+part_id)) {
_heal_amount *= 1+(stats.get('healPct:'+part_id)/100);
}
spell_result = { spell_result = {
type: "heal", type: "heal",
heal_amount: _heal_amount heal_amount: _heal_amount
@ -588,48 +626,51 @@ class SpellDamageCalcNode extends ComputeNode {
else { else {
continue; continue;
} }
spell_result.name = part.name; const {name, display = true} = part;
spell_result.name = name;
spell_result.display = display;
spell_results.push(spell_result); spell_results.push(spell_result);
spell_result_map.set(part.name, spell_result); spell_result_map.set(name, spell_result);
} }
for (const part of spell_parts) { for (const part of spell_parts) {
if ('hits' in part) { if (!('hits' in part)) { continue; }
let spell_result = { let spell_result = {
normal_min: [0, 0, 0, 0, 0, 0], normal_min: [0, 0, 0, 0, 0, 0],
normal_max: [0, 0, 0, 0, 0, 0], normal_max: [0, 0, 0, 0, 0, 0],
normal_total: [0, 0], normal_total: [0, 0],
crit_min: [0, 0, 0, 0, 0, 0], crit_min: [0, 0, 0, 0, 0, 0],
crit_max: [0, 0, 0, 0, 0, 0], crit_max: [0, 0, 0, 0, 0, 0],
crit_total: [0, 0], crit_total: [0, 0],
heal_amount: 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 {
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;
}
}
spell_result.name = part.name;
spell_results.push(spell_result);
spell_result_map.set(part.name, spell_result);
} }
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 {
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;
spell_results.push(spell_result);
spell_result_map.set(name, spell_result);
} }
return spell_results; return spell_results;
} }
@ -674,13 +715,13 @@ class BuildDisplayNode extends ComputeNode {
compute_func(input_map) { compute_func(input_map) {
const build = input_map.get('build'); const build = input_map.get('build');
const stats = input_map.get('stats'); const stats = input_map.get('stats');
displayBuildStats('overall-stats', build, build_all_display_commands, stats); displayBuildStats('summary-stats', build, build_overall_display_commands, stats);
displayBuildStats("offensive-stats", build, build_offensive_display_commands, stats); displayBuildStats("detailed-stats", build, build_detailed_display_commands, stats);
displaySetBonuses("set-info", build); displaySetBonuses("set-info", build);
// TODO: move weapon out? // TODO: move weapon out?
displayDefenseStats(document.getElementById("defensive-stats"), stats); // displayDefenseStats(document.getElementById("defensive-stats"), stats);
displayPoisonDamage(document.getElementById("build-poison-stats"), build); displayPoisonDamage(document.getElementById("build-poison-stats"), stats);
displayEquipOrder(document.getElementById("build-order"), build.equip_order); displayEquipOrder(document.getElementById("build-order"), build.equip_order);
} }
} }
@ -705,7 +746,7 @@ class DisplayBuildWarningsNode extends ComputeNode {
input_map.get('def'), input_map.get('def'),
input_map.get('agi') input_map.get('agi')
]; ];
let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."]; let skp_effects = ["% damage","% crit","% cost red.","% resist","% dodge"];
let total_assigned = 0; let total_assigned = 0;
for (let i in skp_order){ //big bren for (let i in skp_order){ //big bren
const assigned = skillpoints[i] - base_totals[i] + min_assigned[i] const assigned = skillpoints[i] - base_totals[i] + min_assigned[i]
@ -727,20 +768,16 @@ class DisplayBuildWarningsNode extends ComputeNode {
let summarybox = document.getElementById("summary-box"); let summarybox = document.getElementById("summary-box");
summarybox.textContent = ""; summarybox.textContent = "";
let skpRow = document.createElement("p");
let remainingSkp = document.createElement("p"); let remainingSkp = make_elem("p", ['scaled-font', 'my-0']);
remainingSkp.classList.add("scaled-font"); let remainingSkpTitle = make_elem("b", [], { textContent: "Assigned " + total_assigned + " skillpoints. Remaining skillpoints: " });
let remainingSkpTitle = document.createElement("b");
remainingSkpTitle.textContent = "Assigned " + total_assigned + " skillpoints. Remaining skillpoints: ";
let remainingSkpContent = document.createElement("b"); let remainingSkpContent = document.createElement("b");
remainingSkpContent.textContent = "" + (levelToSkillPoints(build.level) - total_assigned); remainingSkpContent.textContent = "" + (levelToSkillPoints(build.level) - total_assigned);
remainingSkpContent.classList.add(levelToSkillPoints(build.level) - total_assigned < 0 ? "negative" : "positive"); remainingSkpContent.classList.add(levelToSkillPoints(build.level) - total_assigned < 0 ? "negative" : "positive");
remainingSkp.appendChild(remainingSkpTitle); remainingSkp.append(remainingSkpTitle);
remainingSkp.appendChild(remainingSkpContent); remainingSkp.append(remainingSkpContent);
summarybox.append(skpRow);
summarybox.append(remainingSkp); summarybox.append(remainingSkp);
if(total_assigned > levelToSkillPoints(build.level)){ if(total_assigned > levelToSkillPoints(build.level)){
let skpWarning = document.createElement("span"); let skpWarning = document.createElement("span");
@ -810,6 +847,72 @@ 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",
// wynn2 damages.
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw",//"eDamAddMin","eDamAddMax",
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct,"*/"tDamRaw",//"tDamAddMin","tDamAddMax",
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct,"*/"wDamRaw",//"wDamAddMin","wDamAddMax",
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct,"*/"fDamRaw",//"fDamAddMin","fDamAddMax",
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw",//"aDamAddMin","aDamAddMax",
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw",//"nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw",//"damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw",//"rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
"critDamPct",
//"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
];
/**
* Scale stats if radiance is enabled.
*/
const radiance_node = new (class extends ComputeNode {
constructor() { super('radiance-node->:('); }
compute_func(input_map) {
const [statmap] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
let elem = document.getElementById('radiance-boost');
if (elem.classList.contains("toggleOn")) {
const ret = new Map(statmap);
for (const val of radiance_affected) {
if (reversedIDs.includes(val)) {
if ((ret.get(val) || 0) < 0) {
ret.set(val, Math.floor((ret.get(val) || 0) * 1.2));
}
}
else {
if ((ret.get(val) || 0) > 0) {
ret.set(val, Math.floor((ret.get(val) || 0) * 1.2));
}
}
}
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 {
return statmap;
}
}
})();
/* Updates all spell boosts
*/
function update_radiance() {
let elem = document.getElementById('radiance-boost');
if (elem.classList.contains("toggleOn")) {
elem.classList.remove("toggleOn");
} else {
elem.classList.add("toggleOn");
}
radiance_node.mark_dirty().update();
}
/** /**
* Aggregate editable ID stats with build and weapon type. * Aggregate editable ID stats with build and weapon type.
* *
@ -856,6 +959,17 @@ class EditableIDSetterNode extends ComputeNode {
} }
} }
/**
* Overriding this to bridge the transparent gap.
*/
mark_dirty(dirty_state=2) {
super.mark_dirty(dirty_state);
for (const node of this.notify_nodes) {
node.mark_dirty(dirty_state);
}
return this;
}
notify() { notify() {
this.mark_dirty(); this.mark_dirty();
this.update(); this.update();
@ -928,9 +1042,11 @@ class SumNumberInputNode extends InputNode {
} }
let item_nodes = []; let item_nodes = [];
let item_nodes_map = new Map();
let powder_nodes = []; let powder_nodes = [];
let edit_input_nodes = []; let edit_input_nodes = [];
let skp_inputs = []; let skp_inputs = [];
let equip_inputs = [];
let build_node; let build_node;
let stat_agg_node; let stat_agg_node;
let edit_agg_node; let edit_agg_node;
@ -939,25 +1055,53 @@ let atree_graph_creator;
function builder_graph_init() { function builder_graph_init() {
// Phase 1/3: Set up item input, propagate updates, etc. // Phase 1/3: Set up item input, propagate updates, etc.
// Level input node.
let level_input = new InputNode('level-input', document.getElementById('level-choice'));
// "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display.
build_node = new BuildAssembleNode();
for (const input of item_nodes) {
}
build_node.link_to(level_input);
let build_encode_node = new BuildEncodeNode();
build_encode_node.link_to(build_node, 'build');
// Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff). // Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff).
for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) { for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) {
let input_field = document.getElementById(eq+"-choice"); let input_field = document.getElementById(eq+"-choice");
let item_image = document.getElementById(eq+"-img"); let item_image = document.getElementById(eq+"-img");
let item_input = new ItemInputNode(eq+'-input', input_field, none_item); let item_input = new ItemInputNode(eq+'-input', input_field, none_item);
equip_inputs.push(item_input);
if (powder_inputs.includes(eq+'-powder')) { // TODO: fragile
const powder_name = eq+'-powder';
let powder_node = new PowderInputNode(powder_name, document.getElementById(powder_name))
.link_to(item_input, 'item');
powder_nodes.push(powder_node);
build_encode_node.link_to(powder_node, powder_name);
let item_powdering = new ItemPowderingNode(eq+'-powder-apply')
.link_to(powder_node, 'powdering').link_to(item_input, 'item');
item_input = item_powdering;
}
item_nodes.push(item_input); item_nodes.push(item_input);
item_nodes_map.set(eq, item_input);
new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input);
new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input); new ItemDisplayNode(eq+'-item-display', display_elem).link_to(item_input);
//new PrintNode(eq+'-debug').link_to(item_input); //new PrintNode(eq+'-debug').link_to(item_input);
//document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm'); //document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm');
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]])) {
let input_field = document.getElementById(eq+"-choice"); let input_field = document.getElementById(eq+"-choice");
let item_image = document.getElementById(eq+"-img"); let item_image = document.getElementById(eq+"-img");
let item_input = new ItemInputNode(eq+'-input', input_field, none_item); let item_input = new ItemInputNode(eq+'-input', input_field, none_item);
equip_inputs.push(item_input);
item_nodes.push(item_input); item_nodes.push(item_input);
new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input); new ItemInputDisplayNode(eq+'-input-display', eq, item_image).link_to(item_input);
build_node.link_to(item_input, eq);
} }
// weapon image changer node. // weapon image changer node.
@ -965,38 +1109,12 @@ function builder_graph_init() {
let weapon_dps = document.getElementById("weapon-dps"); let weapon_dps = document.getElementById("weapon-dps");
new WeaponInputDisplayNode('weapon-type', weapon_image, weapon_dps).link_to(item_nodes[8]); new WeaponInputDisplayNode('weapon-type', weapon_image, weapon_dps).link_to(item_nodes[8]);
// Level input node.
let level_input = new InputNode('level-input', document.getElementById('level-choice'));
// linking to atree verification // linking to atree verification
atree_validate.link_to(level_input, 'level'); atree_validate.link_to(level_input, 'level');
// "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display.
build_node = new BuildAssembleNode();
for (const input of item_nodes) {
build_node.link_to(input);
}
build_node.link_to(level_input);
let build_encode_node = new BuildEncodeNode();
build_encode_node.link_to(build_node, 'build');
let url_update_node = new URLUpdateNode(); let url_update_node = new URLUpdateNode();
url_update_node.link_to(build_encode_node, 'build-str'); url_update_node.link_to(build_encode_node, 'build-str');
for (const input of powder_inputs) {
let powder_node = new PowderInputNode(input, document.getElementById(input));
powder_nodes.push(powder_node);
build_encode_node.link_to(powder_node, input);
}
item_nodes[0].link_to(powder_nodes[0], 'powdering');
item_nodes[1].link_to(powder_nodes[1], 'powdering');
item_nodes[2].link_to(powder_nodes[2], 'powdering');
item_nodes[3].link_to(powder_nodes[3], 'powdering');
item_nodes[8].link_to(powder_nodes[4], 'powdering');
// Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage // Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
// Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap)
@ -1030,20 +1148,21 @@ function builder_graph_init() {
// Phase 3/3: Set up atree stuff. // Phase 3/3: Set up atree stuff.
let class_node = new PlayerClassNode('builder-class').link_to(build_node); let class_node = new PlayerClassNode('builder-class').link_to(build_node);
// These two are defined in `atree.js` // These two are defined in `builder/atree.js`
atree_node.link_to(class_node, 'player-class'); atree_node.link_to(class_node, 'player-class');
atree_merge.link_to(class_node, 'player-class'); atree_merge.link_to(class_node, 'player-class');
pre_scale_agg_node.link_to(atree_stats, 'atree-raw-stats'); pre_scale_agg_node.link_to(atree_raw_stats, 'atree-raw-stats');
atree_scaling.link_to(pre_scale_agg_node, 'scale-stats'); radiance_node.link_to(pre_scale_agg_node, 'stats');
stat_agg_node.link_to(pre_scale_agg_node, 'pre-scaling'); atree_scaling.link_to(radiance_node, 'scale-stats');
stat_agg_node.link_to(atree_scaling, 'atree-scaling'); stat_agg_node.link_to(radiance_node, 'pre-scaling');
stat_agg_node.link_to(atree_scaling_stats, 'atree-scaling');
build_encode_node.link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state'); build_encode_node.link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Trigger the update cascade for build! // Trigger the update cascade for build!
// --------------------------------------------------------------- // ---------------------------------------------------------------
for (const input_node of item_nodes.concat(powder_nodes)) { for (const input_node of equip_inputs) {
input_node.update(); input_node.update();
} }
armor_powder_node.update(); armor_powder_node.update();
@ -1056,11 +1175,15 @@ function builder_graph_init() {
if (atree_data !== null && atree_node.value !== null) { // janky check if atree is valid if (atree_data !== null && atree_node.value !== null) { // janky check if atree is valid
const atree_state = atree_state_node.value; const atree_state = atree_state_node.value;
if (atree_data.length > 0) { if (atree_data.length > 0) {
const active_nodes = decode_atree(atree_node.value, atree_data); try {
for (const node of active_nodes) { const active_nodes = decode_atree(atree_node.value, atree_data);
atree_set_state(atree_state.get(node.ability.id), true); for (const node of active_nodes) {
atree_set_state(atree_state.get(node.ability.id), true);
}
atree_state_node.mark_dirty().update();
} catch (e) {
console.log("Failed to decode atree. This can happen when updating versions. Give up!")
} }
atree_state_node.mark_dirty().update();
} }
} }

View file

@ -1,6 +1,6 @@
let all_nodes = new Set(); let all_nodes = new Set();
let node_debug_stack = []; let node_debug_stack = [];
let COMPUTE_GRAPH_DEBUG = false; let COMPUTE_GRAPH_DEBUG = true;
class ComputeNode { class ComputeNode {
/** /**
* Make a generic compute node. * Make a generic compute node.
@ -15,7 +15,7 @@ class ComputeNode {
this.value = null; this.value = null;
this.name = name; this.name = name;
this.update_task = null; this.update_task = null;
this.fail_cb = false; // Set to true to force updates even if parent failed. this.fail_cb = false; // Set to true to force updates even if parent failed
this.dirty = 2; // 3 states: this.dirty = 2; // 3 states:
// 2: dirty // 2: dirty
// 1: possibly dirty // 1: possibly dirty
@ -39,6 +39,13 @@ class ComputeNode {
if (this.dirty == 2) { if (this.dirty == 2) {
let calc_inputs = new Map(); let calc_inputs = new Map();
for (const input of this.inputs) { for (const input of this.inputs) {
if (input.dirty) {
if (COMPUTE_GRAPH_DEBUG) {
console.log(node_debug_stack);
console.log(this);
}
throw "Invalid compute graph state!";
}
calc_inputs.set(this.input_translation.get(input.name), input.value); calc_inputs.set(this.input_translation.get(input.name), input.value);
} }
this.value = this.compute_func(calc_inputs); this.value = this.compute_func(calc_inputs);
@ -213,7 +220,7 @@ class PrintNode extends ComputeNode {
* *
* Signature: InputNode() => str * Signature: InputNode() => str
*/ */
class InputNode extends ComputeNode { class InputNode extends ValueCheckComputeNode {
constructor(name, input_field) { constructor(name, input_field) {
super(name); super(name);
this.input_field = input_field; this.input_field = input_field;

View file

@ -397,4 +397,8 @@ class Craft{
statMap.set("crafted", true); statMap.set("crafted", true);
this.statMap = statMap; this.statMap = statMap;
} }
copy() {
return new Craft(this.recipe, this.mat_tiers, this.ingreds, this.atkSpd, this.hash.slice(3));
}
} }

View file

@ -31,42 +31,31 @@ let player_craft;
function init_crafter() { function init_crafter() {
//no ing
console.log("all ingredients");
console.log(ingMap);
console.log("all recipes");
console.log(recipeMap);
/*console.log(ingList);
console.log(recipeList);
console.log(ingIDMap);
console.log(recipeIDMap);*/
try { try {
document.getElementById("recipe-choice").addEventListener("change", (event) => { document.getElementById("recipe-choice").addEventListener("input", (event) => {
updateMaterials(); updateMaterials();
updateCraftedImage(); updateCraftedImage();
calculateCraftSchedule(); calculateCraftSchedule();
}); });
document.getElementById("recipe-choice").addEventListener("oninput", (event) => { document.getElementById("recipe-choice").addEventListener("input", (event) => {
updateCraftedImage(); updateCraftedImage();
}); });
document.getElementById("level-choice").addEventListener("change", (event) => { document.getElementById("level-choice").addEventListener("input", (event) => {
updateMaterials(); updateMaterials();
calculateCraftSchedule(); calculateCraftSchedule();
}); });
for (let i = 1; i < 4; ++i) { for (let i = 1; i < 4; ++i) {
document.getElementById("mat-1-"+i).setAttribute("onclick", document.getElementById("mat-1-"+i).getAttribute("onclick") + "; calculateCraftSchedule();"); document.getElementById("mat-1-"+i).addEventListener("click", (e) => calculateCraftSchedule());
document.getElementById("mat-2-"+i).setAttribute("onclick", document.getElementById("mat-2-"+i).getAttribute("onclick") + "; calculateCraftSchedule();"); document.getElementById("mat-2-"+i).addEventListener("click", (e) => calculateCraftSchedule());
} }
for (let i = 1; i < 7; ++i) { for (let i = 1; i < 7; ++i) {
document.getElementById("ing-choice-" + i ).setAttribute("oninput", "calculateCraftSchedule();"); document.getElementById("ing-choice-" + i ).addEventListener("input", (e) => calculateCraftSchedule());
} }
for (const str of ["slow", "normal", "fast"]) { for (const str of ["slow", "normal", "fast"]) {
document.getElementById(str + "-atk-button").setAttribute("onclick", document.getElementById(str + "-atk-button").getAttribute("onclick") + "; calculateCraftSchedule();"); document.getElementById(str + "-atk-button").addEventListener("click", (e) => calculateCraftSchedule());
} }
populateFields(); populateFields();
decodeCraft(ing_url_tag); decodeCraft(ing_url_tag);
} catch (error) { } catch (error) {
@ -259,12 +248,10 @@ function populateFields() {
ing_list.appendChild(el); ing_list.appendChild(el);
} }
} }
} }
/* /*
Copies the CR Hash (CR-blahblahblah) * Copies the CR Hash (CR-blahblahblah)
*/ */
function copyRecipeHash() { function copyRecipeHash() {
if (player_craft) { if (player_craft) {
copyTextToClipboard("CR-"+location.hash.slice(1)); copyTextToClipboard("CR-"+location.hash.slice(1));
@ -345,9 +332,16 @@ function toggleMaterial(buttonId) {
function updateCraftedImage() { function updateCraftedImage() {
let input = document.getElementById("recipe-choice"); let input = document.getElementById("recipe-choice");
if (all_types.includes(input.value)) { if (all_types.includes(input.value)) {
document.getElementById("recipe-img").src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + input.value.toLowerCase() + ".png"; let img = document.getElementById("recipe-img");
if (["potion", "scroll", "food"].includes(input.value.toLowerCase())) {
img.style.backgroundImage = "url('../media/items/common.png')";
img.style.backgroundSize = "500% 100%";
} else {
img.style.backgroundImage = "url('../media/items/" + (newIcons ? "new.png')" : "old.png')");
img.style.backgroundSize = "1200% 100%";
}
img.style.backgroundPosition = itemBGPositions[input.value.toLowerCase()]
} }
} }
/* Reset all fields /* Reset all fields

View file

@ -188,7 +188,7 @@ function getCustomFromHash(hash) {
/** An object representing a Custom Item. Mostly for vanity purposes. /** An object representing a Custom Item. Mostly for vanity purposes.
* @dep Requires the use of nonRolledIDs and rolledIDs from display_constants.js. * @dep Requires the use of nonRolledIDs and rolledIDs from display_constants.js.
* @dep Requires the use of attackSpeeds from build.js. * @dep Requires the use of attackSpeeds from `builder/build.js`.
*/ */
class Custom { class Custom {
/** /**
@ -336,4 +336,7 @@ class Custom {
this.statMap.set("restrict", "Custom Item") this.statMap.set("restrict", "Custom Item")
} }
copy() {
return new Custom(new Map(this.statMap));
}
} }

View file

@ -12,7 +12,6 @@ let roll_range_ids = ["neg_roll_range-choice-min","neg_roll_range-choice-max","p
function init_customizer() { function init_customizer() {
try { try {
populateFields(); populateFields();
decodeCustom(custom_url_tag); decodeCustom(custom_url_tag);
@ -181,7 +180,11 @@ function calculateCustom() {
player_custom_item.setHash(custom_str); player_custom_item.setHash(custom_str);
displaysq2ExpandedItem(player_custom_item.statMap, "custom-stats"); if (player_custom_item.statMap.get('category') == 'weapon') {
// wonkiness needed to set the right fields... this is jank
apply_weapon_powders(player_custom_item.statMap);
}
displayExpandedItem(player_custom_item.statMap, "custom-stats");
}catch (error) { }catch (error) {
//The error elements no longer exist in the page. Add them back if needed. //The error elements no longer exist in the page. Add them back if needed.
@ -266,7 +269,7 @@ function decodeCustom(custom_url_tag) {
val = tiers[Base64.toInt(tag.charAt(2))]; val = tiers[Base64.toInt(tag.charAt(2))];
len = -1; len = -1;
} else if (id === "type") { } else if (id === "type") {
val = types[Base64.toInt(tag.charAt(2))]; val = item_types[Base64.toInt(tag.charAt(2))];
len = -1; len = -1;
} else if (id === "atkSpd") { } else if (id === "atkSpd") {
val = attackSpeeds[Base64.toInt(tag.charAt(2))]; val = attackSpeeds[Base64.toInt(tag.charAt(2))];
@ -313,7 +316,7 @@ function populateFields() {
tier_list.appendChild(el); tier_list.appendChild(el);
} }
let type_list = document.getElementById("type-list"); let type_list = document.getElementById("type-list");
for (const type of types) { for (const type of item_types) {
let el = document.createElement("option"); let el = document.createElement("option");
el.value = type; el.value = type;
type_list.appendChild(el); type_list.appendChild(el);
@ -602,10 +605,6 @@ function resetBaseValues() {
} }
} }
function _init_customizer() {
load_ing_init(init_customizer);
}
/** Saves the current user's item as a JSON file. /** Saves the current user's item as a JSON file.
* Starts a JSON download. * Starts a JSON download.
*/ */
@ -715,4 +714,8 @@ function full_range_to_base(min, max) {
load_init(_init_customizer); (async function() {
let load_promises = [ load_init() ];
await Promise.all(load_promises);
init_customizer();
})();

View file

@ -1,4 +1,6 @@
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.00], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]); /**
* File implementing core damage calculation logic.
*/
function get_base_dps(item) { function get_base_dps(item) {
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))]; const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];
@ -122,15 +124,21 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
// These do not count raw damage. I think. Easy enough to change // These do not count raw damage. I think. Easy enough to change
let total_min = 0; let total_min = 0;
let total_max = 0; let total_max = 0;
let save_prop = [];
for (let i in damage_elements) { for (let i in damage_elements) {
save_prop.push(damages[i].slice());
total_min += damages[i][0];
total_max += damages[i][1];
let damage_specific = damage_elements[i] + specific_boost_str + 'Pct'; let damage_specific = damage_elements[i] + specific_boost_str + 'Pct';
let damageBoost = 1 + skill_boost[i] + static_boost let damageBoost = 1 + skill_boost[i] + static_boost
+ ((stats.get(damage_specific) + stats.get(damage_elements[i]+'DamPct')) /100); + ((stats.get(damage_specific) + stats.get(damage_elements[i]+'DamPct')) /100);
if (i > 0) {
damageBoost += stats.get('r'+specific_boost_str+'Pct') / 100;
}
damages[i][0] *= Math.max(damageBoost, 0); damages[i][0] *= Math.max(damageBoost, 0);
damages[i][1] *= Math.max(damageBoost, 0); damages[i][1] *= Math.max(damageBoost, 0);
// Collect total damage post %boost // Collect total damage post %boost
total_min += damages[i][0];
total_max += damages[i][1];
} }
let total_elem_min = total_min - damages[0][0]; let total_elem_min = total_min - damages[0][0];
@ -140,6 +148,7 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
let prop_raw = stats.get(specific_boost_str.toLowerCase()+'Raw') + stats.get('damRaw'); let prop_raw = stats.get(specific_boost_str.toLowerCase()+'Raw') + stats.get('damRaw');
let rainbow_raw = stats.get('r'+specific_boost_str+'Raw') + stats.get('rDamRaw'); let rainbow_raw = stats.get('r'+specific_boost_str+'Raw') + stats.get('rDamRaw');
for (let i in damages) { for (let i in damages) {
let save_obj = save_prop[i];
let damages_obj = damages[i]; let damages_obj = damages[i];
let damage_prefix = damage_elements[i] + specific_boost_str; let damage_prefix = damage_elements[i] + specific_boost_str;
// Normie raw // Normie raw
@ -153,22 +162,22 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
if (total_max > 0) { // TODO: what about total negative all raw? if (total_max > 0) { // TODO: what about total negative all raw?
// TODO: compute actual chance of 0 damage. For now we just copy max ratio // TODO: compute actual chance of 0 damage. For now we just copy max ratio
if (total_min === 0) { if (total_min === 0) {
min_boost += (damages_obj[1] / total_max) * prop_raw; min_boost += (save_obj[1] / total_max) * prop_raw;
} }
else { else {
min_boost += (damages_obj[0] / total_min) * prop_raw; min_boost += (save_obj[0] / total_min) * prop_raw;
} }
max_boost += (damages_obj[1] / total_max) * prop_raw; max_boost += (save_obj[1] / total_max) * prop_raw;
} }
if (i != 0 && total_elem_max > 0) { // rainraw TODO above if (i != 0 && total_elem_max > 0) { // rainraw TODO above
// TODO: compute actual chance of 0 damage. For now we just copy max ratio // TODO: compute actual chance of 0 damage. For now we just copy max ratio
if (total_elem_min === 0) { if (total_elem_min === 0) {
min_boost += (damages_obj[1] / total_elem_max) * rainbow_raw; min_boost += (save_obj[1] / total_elem_max) * rainbow_raw;
} }
else { else {
min_boost += (damages_obj[0] / total_elem_min) * rainbow_raw; min_boost += (save_obj[0] / total_elem_min) * rainbow_raw;
} }
max_boost += (damages_obj[1] / total_elem_max) * rainbow_raw; max_boost += (save_obj[1] / total_elem_max) * rainbow_raw;
} }
damages_obj[0] += min_boost * total_convert; damages_obj[0] += min_boost * total_convert;
damages_obj[1] += max_boost * total_convert; damages_obj[1] += max_boost * total_convert;
@ -193,7 +202,7 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
damage_mult *= (1 + v/100); damage_mult *= (1 + v/100);
} }
const crit_mult = stats.get("critDamPct")/100; const crit_mult = 1+(stats.get("critDamPct")/100);
for (const damage of damages) { for (const damage of damages) {
const res = [ const res = [
@ -243,17 +252,21 @@ spell_damage: {
name: str != "total" Name of the part. name: str != "total" Name of the part.
type: "damage" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields type: "damage" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
multipliers: array[num, 6] floating point spellmults (though supposedly wynn only supports integer mults) multipliers: array[num, 6] floating point spellmults (though supposedly wynn only supports integer mults)
display: bool To show part or not (for some spells there are too many intermediate calc parts). Default: True
} }
spell_heal: { spell_heal: {
name: str != "total" Name of the part. name: str != "total" Name of the part.
type: "heal" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields type: "heal" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
power: num floating point healing power (1 is 100% of max hp). power: num floating point healing power (1 is 100% of max hp).
display: bool To show part or not (for some spells there are too many intermediate calc parts). Default: True
} }
spell_total: { spell_total: {
name: str != "total" Name of the part. name: str != "total" Name of the part.
type: "total" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields type: "total" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
hits: Map[str, num] Keys are other part names, numbers are the multipliers. Undefined behavior if subparts hits: Map[str, Union[str, num]] Keys are other part names, numbers are the multipliers. Undefined behavior if subparts
are not the same type of spell. Can only pull from spells defined before it. are not the same type of spell. Can only pull from spells defined before it.
Alternatively, a property reference of the format <ability_id>.propname
display: bool To show part or not (for some spells there are too many intermediate calc parts). Default: True
} }

View file

@ -1,3 +1,12 @@
/**
* File containing generic display code, ex. for displaying items and spell damage.
* TODO: split this file into separate parts for each "component".
*/
const itemBGPositions = {"bow": "0 0", "spear": "9.090909090909088% 0", "wand": "18.181818181818183% 0", "dagger": "27.27272727272727% 0", "relik": "36.36363636363637% 0",
"helmet": "45.45454545454546% 0", "chestplate": "54.54545454545454% 0", "leggings": "63.63636363636363% 0", "boots": "72.72727272727272% 0",
"ring": "81.81818181818181% 0", "bracelet": "90.90909090909092% 0", "necklace": "100% 0",
"potion": "25% 0", "scroll": "50% 0", "food": "75% 0"};
function apply_elemental_format(p_elem, id, suffix) { function apply_elemental_format(p_elem, id, suffix) {
suffix = (typeof suffix !== 'undefined') ? suffix : ""; suffix = (typeof suffix !== 'undefined') ? suffix : "";
@ -63,7 +72,8 @@ function displayBuildStats(parent_id,build,command_group,stats){
let active_elem; let active_elem;
let elemental_format = false; let elemental_format = false;
//TODO this is put here for readability, consolidate with definition in build.js //TODO this is put here for readability, consolidate with definition in `builder/build.js`
// TODO amend: uuhhhhh these two constants have diverged too far...
let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef"]; let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef"];
for (const command of display_commands) { for (const command of display_commands) {
@ -100,24 +110,8 @@ function displayBuildStats(parent_id,build,command_group,stats){
if (reversedIDs.includes(id)) { if (reversedIDs.includes(id)) {
style === "positive" ? style = "negative" : style = "positive"; style === "positive" ? style = "negative" : style = "positive";
} }
if (id === "poison" && id_val > 0) {
id_val = Math.ceil(id_val*stats.get("poisonPct")/100);
}
displayFixedID(parent_div, id, id_val, elemental_format, style); displayFixedID(parent_div, id, id_val, elemental_format, style);
if (id === "poison" && id_val > 0) { if (id === "ls" && id_val != 0) {
let row = make_elem('div', ['row']);
let value_elem = make_elem('div', ['col', 'text-end']);
let prefix_elem = make_elem('b', [], {textContent: "\u279C With Strength: "});
let number_elem = make_elem('b', [style], {
textContent: (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id]
});
value_elem.append(prefix_elem);
value_elem.append(number_elem);
row.appendChild(value_elem);
parent_div.appendChild(row);
}
else if (id === "ls" && id_val != 0) {
let row = make_elem('div', ['row']); let row = make_elem('div', ['row']);
let value_elem = make_elem('div', ['col', 'text-end']); let value_elem = make_elem('div', ['col', 'text-end']);
@ -259,7 +253,7 @@ function displayExpandedItem(item, parent_id){
if (item.get("custom")) { if (item.get("custom")) {
item_link = "../custom/#" + item.get("hash"); item_link = "../custom/#" + item.get("hash");
} else if (item.get("crafted")) { } else if (item.get("crafted")) {
a_item_link = "../crafter/#" + item.get("hash"); item_link = "../crafter/#" + item.get("hash");
} else { } else {
item_link = "../item/#" + item.get("displayName"); item_link = "../item/#" + item.get("displayName");
} }
@ -286,11 +280,18 @@ function displayExpandedItem(item, parent_id){
parent_div.appendChild(nolink_row); parent_div.appendChild(nolink_row);
if (item.has("type")) { if (item.has("type")) {
let img = make_elem("img", [], { let img = make_elem("div", [], {
src: "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png",
alt: item.get("type"), alt: item.get("type"),
style: " z=index: 1; position: relative;" style: "z-index: 1; position: relative; image-rendering: pixelated; width: 50%; height: 50%; background-position: " + itemBGPositions[item.get("type")] + ";"
}); });
if (["potion", "scroll", "food"].includes(item.get("type"))) {
img.style.backgroundImage = "url('../media/items/common.png')";
img.style.backgroundSize = "500% 100%";
} else {
img.style.backgroundImage = "url('../media/items/" + (newIcons ? "new.png')" : "old.png')");
img.style.backgroundSize = "1200% 100%";
}
let container = make_elem("div"); let container = make_elem("div");
let bckgrd = make_elem("div", ["col", "px-0", "d-flex", "align-items-center", "justify-content-center", 'scaled-bckgrd'], { // , "no-collapse" let bckgrd = make_elem("div", ["col", "px-0", "d-flex", "align-items-center", "justify-content-center", 'scaled-bckgrd'], { // , "no-collapse"
@ -377,46 +378,46 @@ function displayExpandedItem(item, parent_id){
} }
} }
//Show powder specials ;-; //Show powder specials ;-;
let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots"];//, "ring", "bracelet", "necklace"]; let powder_specials_check = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots"];
if(nonConsumables.includes(item.get("type"))) { if(powder_specials_check.includes(item.get("type"))) {
let powder_special = document.createElement("div"); let powder_special = make_elem("div", ['col']);
powder_special.classList.add("col");
let powders = item.get("powders"); let powders = item.get("powders");
let element = ""; let element;
let power = 0; let power_index;
for (let i = 0; i < powders.length; i++) { for (let i = 0; i < powders.length; i++) {
let firstPowderType = skp_elements[Math.floor(powders[i]/6)]; const firstPowderType = skp_elements[Math.floor(powders[i]/6)];
if (element !== "") break; const powder1_power = powders[i] % 6;
else if (powders[i]%6 > 2) { //t4+ if (powder1_power > 2) { //t4+
for (let j = i+1; j < powders.length; j++) { for (let j = i+1; j < powders.length; j++) {
let currentPowderType = skp_elements[Math.floor(powders[j]/6)] const currentPowderType = skp_elements[Math.floor(powders[j]/6)]
if (powders[j] % 6 > 2 && firstPowderType === currentPowderType) { const powder2_power = powders[j] % 6;
if (powder2_power > 2 && firstPowderType === currentPowderType) {
element = currentPowderType; element = currentPowderType;
power = Math.round(((powders[i] % 6 + powders[j] % 6 + 2) / 2 - 4) * 2); power_index = powder1_power + powder2_power - 6;
break; break;
} }
} }
} }
if (element) { break; } // terminate early if already found.
} }
if (element !== "") {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]" if (element) {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]"
let powderSpecial = powderSpecialStats[ skp_elements.indexOf(element)]; const powderSpecial = powderSpecialStats[skp_elements.indexOf(element)];
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); const specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("span"); const specialTitle = make_elem("span", [damageClasses[skp_elements.indexOf(element) + 1]]);
let specialEffects = document.createElement("span"); const specialEffects = document.createElement("span");
addClasses(specialTitle, [damageClasses[skp_elements.indexOf(element) + 1]]);
let effects; let effects;
if (item.get("category") === "weapon") {//weapon if (item.get("category") === "weapon") {//weapon
effects = powderSpecial["weaponSpecialEffects"]; effects = powderSpecial["weaponSpecialEffects"];
specialTitle.textContent = powderSpecial["weaponSpecialName"]; specialTitle.textContent = powderSpecial["weaponSpecialName"];
}else if (item.get("category") === "armor") {//armor } else if (item.get("category") === "armor") {//armor
effects = powderSpecial["armorSpecialEffects"]; effects = powderSpecial["armorSpecialEffects"];
specialTitle.textContent += powderSpecial["armorSpecialName"] + ": "; specialTitle.textContent += powderSpecial["armorSpecialName"] + ": ";
} }
for (const [key,value] of effects.entries()) { for (const [key,value] of effects.entries()) {
if (key !== "Description") { if (key !== "Description") {
let effect = document.createElement("p"); let effect = make_elem("p", ["m-0"], {
effect.classList.add("m-0"); textContent: key + ": " + value[power_index] + specialSuffixes.get(key)
effect.textContent = key + ": " + value[power] + specialSuffixes.get(key); });
if(key === "Damage"){ if(key === "Damage"){
effect.textContent += elementIcons[skp_elements.indexOf(element)]; effect.textContent += elementIcons[skp_elements.indexOf(element)];
} }
@ -424,20 +425,19 @@ function displayExpandedItem(item, parent_id){
effect.textContent += " / Mana Used"; effect.textContent += " / Mana Used";
} }
specialEffects.appendChild(effect); specialEffects.appendChild(effect);
}else{ } else {
specialTitle.textContent += "[ " + effects.get("Description") + " ]"; specialTitle.textContent += "[ " + effects.get("Description") + " ]";
} }
} }
powder_special.appendChild(specialTitle); powder_special.append(specialTitle, specialEffects);
powder_special.appendChild(specialEffects);
parent_div.appendChild(powder_special); parent_div.appendChild(powder_special);
} }
} }
let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots", "ring", "bracelet", "necklace"];
if(item.get("tier") && item.get("tier") === "Crafted") { if(item.get("tier") && item.get("tier") === "Crafted") {
let dura_elem = document.createElement("div"); let dura_elem = make_elem("div", ["col"]);
dura_elem.classList.add("col"); let dura;
let dura = [];
let suffix = ""; let suffix = "";
if(nonConsumables.includes(item.get("type"))) { if(nonConsumables.includes(item.get("type"))) {
dura = item.get("durability"); dura = item.get("durability");
@ -446,9 +446,9 @@ function displayExpandedItem(item, parent_id){
dura = item.get("duration"); dura = item.get("duration");
dura_elem.textContent = "Duration: " dura_elem.textContent = "Duration: "
suffix = " sec." suffix = " sec."
let charges = document.createElement("b"); parent_div.appendChild(make_elem('b', [], {
charges.textContent = "Charges: " + item.get("charges"); textContent: "Charges: " + item.get("charges")
parent_div.appendChild(charges); }));
} }
if (typeof(dura) === "string") { if (typeof(dura) === "string") {
@ -461,9 +461,7 @@ function displayExpandedItem(item, parent_id){
} }
//Show item tier //Show item tier
if (item.get("tier") && item.get("tier") !== " ") { if (item.get("tier") && item.get("tier") !== " ") {
let item_desc_elem = document.createElement("div"); let item_desc_elem = make_elem("div", ["col", item.get("tier")]);
item_desc_elem.classList.add("col");
item_desc_elem.classList.add(item.get("tier"));
if (tome_types.includes(item.get("type"))) { if (tome_types.includes(item.get("type"))) {
tome_type_map = new Map([["weaponTome", "Weapon Tome"],["armorTome", "Armor Tome"],["guildTome", "Guild Tome"]]); 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")); item_desc_elem.textContent = item.get("tier")+" "+tome_type_map.get(item.get("type"));
@ -475,20 +473,19 @@ function displayExpandedItem(item, parent_id){
//Show item hash if applicable //Show item hash if applicable
if (item.get("crafted") || item.get("custom")) { if (item.get("crafted") || item.get("custom")) {
let item_desc_elem = document.createElement("p"); parent_div.append(make_elem('p', ['itemp'], {
item_desc_elem.classList.add('itemp'); style: {
item_desc_elem.style.maxWidth = "100%"; maxWidth: '100%',
item_desc_elem.style.wordWrap = "break-word"; wordWrap: 'break-word',
item_desc_elem.style.wordBreak = "break-word"; wordBreak: 'break-word'
item_desc_elem.textContent = item.get("hash"); },
parent_div.append(item_desc_elem); textContent: item.get('hash')
}));
} }
if (item.get("category") === "weapon") { if (item.get("category") === "weapon") {
let total_damages = item.get("basedps"); let total_damages = item.get("basedps");
let base_dps_elem = document.createElement("p"); let base_dps_elem = make_elem("p", ["left", "itemp"]);
base_dps_elem.classList.add("left");
base_dps_elem.classList.add("itemp");
if (item.get("tier") === "Crafted") { if (item.get("tier") === "Crafted") {
let base_dps_min = total_damages[0]; let base_dps_min = total_damages[0];
let base_dps_max = total_damages[1]; let base_dps_max = total_damages[1];
@ -498,8 +495,7 @@ function displayExpandedItem(item, parent_id){
else { else {
base_dps_elem.textContent = "Base DPS: "+(total_damages); base_dps_elem.textContent = "Base DPS: "+(total_damages);
} }
parent_div.appendChild(document.createElement("p")); parent_div.append(make_elem("p"), base_dps_elem);
parent_div.appendChild(base_dps_elem);
} }
} }
@ -987,9 +983,9 @@ function displayFixedID(active, id, value, elemental_format, style) {
} }
} }
function displayPoisonDamage(overallparent_elem, build) { function displayPoisonDamage(overallparent_elem, statMap) {
overallparent_elem.textContent = ""; overallparent_elem.textContent = "";
if (build.statMap.get('poison') <= 0) { if (statMap.get('poison') <= 0) {
overallparent_elem.style = "display: none"; overallparent_elem.style = "display: none";
return; return;
} }
@ -1003,7 +999,8 @@ function displayPoisonDamage(overallparent_elem, build) {
title_elemavg.append(make_elem('span', [], { textContent: "Poison Stats" })); title_elemavg.append(make_elem('span', [], { textContent: "Poison Stats" }));
spell_summary.append(title_elemavg); spell_summary.append(title_elemavg);
let poison_tick = Math.ceil(build.statMap.get("poison") * (1+skillPointsToPercentage(build.total_skillpoints[0])) * (build.statMap.get("poisonPct"))/100 /3); let poison_tick = Math.floor(statMap.get("poison")/3);
//let poison_tick = Math.ceil(statMap.get("poison") * (1+skillPointsToPercentage(statMap.get('str'))) * (statMap.get("poisonPct"))/100 /3);
let overallpoisonDamage = make_elem("p"); let overallpoisonDamage = make_elem("p");
overallpoisonDamage.append( overallpoisonDamage.append(
@ -1257,8 +1254,8 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
//iterate through the special and display its effects. //iterate through the special and display its effects.
let powder_special = make_elem("p", ["pt-3"]); let powder_special = make_elem("p", ["pt-3"]);
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("p"); let specialTitle = make_elem("p");
let specialEffects = document.createElement("p"); let specialEffects = make_elem("p");
specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]); specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]);
let effects = special[0]["weaponSpecialEffects"]; let effects = special[0]["weaponSpecialEffects"];
let power = special[1]; let power = special[1];
@ -1382,7 +1379,7 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
} }
function getSpellCost(stats, spell) { function getSpellCost(stats, spell) {
return Math.max(1, getBaseSpellCost(stats, spell)); return Math.max(1, getBaseSpellCost(stats, spell) * (1 + stats.get('spPct'+spell.base_spell+'Final')/100));
} }
function getBaseSpellCost(stats, spell) { function getBaseSpellCost(stats, spell) {
@ -1445,6 +1442,7 @@ function displaySpellDamage(parent_elem, _overallparent_elem, stats, spell, spel
for (let i = 0; i < spell_results.length; ++i) { for (let i = 0; i < spell_results.length; ++i) {
const spell_info = spell_results[i]; const spell_info = spell_results[i];
if (!spell_info.display) { continue; }
let part_div = make_elem("p", ["pt-3"]); let part_div = make_elem("p", ["pt-3"]);
parent_elem.append(part_div); parent_elem.append(part_div);
@ -1499,9 +1497,7 @@ function displaySpellDamage(parent_elem, _overallparent_elem, stats, spell, spel
_damage_display("Crit Average: ", critAverage, spell_info.crit_min, spell_info.crit_max); _damage_display("Crit Average: ", critAverage, spell_info.crit_min, spell_info.crit_max);
} else if (spell_info.type === "heal") { } else if (spell_info.type === "heal") {
let heal_amount = spell_info.heal_amount; let heal_amount = spell_info.heal_amount;
let healLabel = document.createElement("p"); let healLabel = make_elem("p", ["Set"], {textContent: heal_amount.toFixed(2)});
healLabel.textContent = heal_amount;
// healLabel.classList.add("damagep");
part_div.append(healLabel); part_div.append(healLabel);
if (spell_info.name === spell.display) { if (spell_info.name === spell.display) {
add_summary(spell_info.name+ ": ", heal_amount, "Set"); add_summary(spell_info.name+ ": ", heal_amount, "Set");
@ -1513,417 +1509,6 @@ function displaySpellDamage(parent_elem, _overallparent_elem, stats, spell, spel
_overallparent_elem.append(overallparent_elem); _overallparent_elem.append(overallparent_elem);
} }
/** 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);
}
function addClickableArrow(elem, target) { function addClickableArrow(elem, target) {
//up and down arrow - done ugly //up and down arrow - done ugly
let arrow = make_elem("img", [], { id: "arrow_" + elem.id, src: "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png" }); let arrow = make_elem("img", [], { id: "arrow_" + elem.id, src: "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png" });

View file

@ -40,9 +40,37 @@ let idPrefixes = {"displayName": "",
"hprRaw":"Health Regen Raw: ", "hprRaw":"Health Regen Raw: ",
"hprPct":"Health Regen %: ", "hprPct":"Health Regen %: ",
"sdRaw":"Raw Spell Damage: ", "sdRaw":"Raw Spell Damage: ",
"rSdRaw":"Elem. Spell Damage Raw: ",
"nSdRaw":"Neut. Spell Damage Raw: ",
"eSdRaw":"Raw Earth Spell Damage: ",
"tSdRaw":"Raw Thunder Spell Damage: ",
"wSdRaw":"Raw Water Spell Damage: ",
"fSdRaw":"Raw Fire Spell Damage: ",
"aSdRaw":"Raw Air Spell Damage: ",
"sdPct":"Spell Damage %: ", "sdPct":"Spell Damage %: ",
"rSdPct":"Elem. Spell Damage %: ",
"nSdPct":"Neut. Spell Damage %: ",
"eSdPct":"% Earth Spell Damage: ",
"tSdPct":"% Thunder Spell Damage: ",
"wSdPct":"% Water Spell Damage: ",
"fSdPct":"% Fire Spell Damage: ",
"aSdPct":"% Air Spell Damage: ",
"mdRaw":"Raw Melee Damage: ", "mdRaw":"Raw Melee Damage: ",
"rMdRaw":"Elem. Melee Damage Raw: ",
"nMdRaw":"Neut. Melee Damage Raw: ",
"eMdRaw":"Raw Earth Melee Damage: ",
"tMdRaw":"Raw Thunder Melee Damage: ",
"wMdRaw":"Raw Water Melee Damage: ",
"fMdRaw":"Raw Fire Melee Damage: ",
"aMdRaw":"Raw Air Melee Damage: ",
"mdPct":"Melee Damage %: ", "mdPct":"Melee Damage %: ",
"rMdPct":"Elem. Melee Damage %: ",
"nMdPct":"Neut. Melee Damage %: ",
"eMdPct":"% Earth Melee Damage: ",
"tMdPct":"% Thunder Melee Damage: ",
"wMdPct":"% Water Melee Damage: ",
"fMdPct":"% Fire Melee Damage: ",
"aMdPct":"% Air Melee Damage: ",
"mr":"Mana Regen: ", "mr":"Mana Regen: ",
"ms":"Mana Steal: ", "ms":"Mana Steal: ",
"ref":"Reflection: ", "ref":"Reflection: ",
@ -52,7 +80,7 @@ let idPrefixes = {"displayName": "",
"expd":"Exploding: ", "expd":"Exploding: ",
"spd":"Walk Speed Bonus: ", "spd":"Walk Speed Bonus: ",
"atkTier":"Attack Speed Bonus: ", "atkTier":"Attack Speed Bonus: ",
"eDamPct":"Earth Damage %: ", "eDamPct":"Earth Damage %: ",
"tDamPct":"Thunder Damage %: ", "tDamPct":"Thunder Damage %: ",
"wDamPct":"Water Damage %: ", "wDamPct":"Water Damage %: ",
"fDamPct":"Fire Damage %: ", "fDamPct":"Fire Damage %: ",
@ -70,7 +98,6 @@ let idPrefixes = {"displayName": "",
"spRaw3":"3rd Spell Cost Raw: ", "spRaw3":"3rd Spell Cost Raw: ",
"spPct4":"4th Spell Cost %: ", "spPct4":"4th Spell Cost %: ",
"spRaw4":"4th Spell Cost Raw: ", "spRaw4":"4th Spell Cost Raw: ",
"rainbowRaw":"Rainbow Spell Damage Raw: ",
"sprint":"Sprint Bonus: ", "sprint":"Sprint Bonus: ",
"sprintReg":"Sprint Regen Bonus: ", "sprintReg":"Sprint Regen Bonus: ",
"jh":"Jump Height: ", "jh":"Jump Height: ",
@ -117,9 +144,37 @@ let idSuffixes = {"displayName": "",
"hprRaw":"", "hprRaw":"",
"hprPct":"%", "hprPct":"%",
"sdRaw":"", "sdRaw":"",
"rSdRaw":"",
"nSdRaw":"",
"eSdRaw":"",
"tSdRaw":"",
"wSdRaw":"",
"fSdRaw":"",
"aSdRaw":"",
"sdPct":"%", "sdPct":"%",
"rSdPct":"%",
"nSdPct":"%",
"eSdPct":"%",
"tSdPct":"%",
"wSdPct":"%",
"fSdPct":"%",
"aSdPct":"%",
"mdRaw":"", "mdRaw":"",
"rMdRaw":"",
"nMdRaw":"",
"eMdRaw":"",
"tMdRaw":"",
"wMdRaw":"",
"fMdRaw":"",
"aMdRaw":"",
"mdPct":"%", "mdPct":"%",
"rMdPct":"%",
"nMdPct":"%",
"eMdPct":"%",
"tMdPct":"%",
"wMdPct":"%",
"fMdPct":"%",
"aMdPct":"%",
"mr":"/5s", "mr":"/5s",
"ms":"/3s", "ms":"/3s",
"ref":"%", "ref":"%",
@ -129,7 +184,7 @@ let idSuffixes = {"displayName": "",
"expd":"%", "expd":"%",
"spd":"%", "spd":"%",
"atkTier":" tier", "atkTier":" tier",
"eDamPct":"%", "eDamPct":"%",
"tDamPct":"%", "tDamPct":"%",
"wDamPct":"%", "wDamPct":"%",
"fDamPct":"%", "fDamPct":"%",
@ -147,7 +202,6 @@ let idSuffixes = {"displayName": "",
"spRaw3":"", "spRaw3":"",
"spPct4":"%", "spPct4":"%",
"spRaw4":"", "spRaw4":"",
"rainbowRaw":"",
"sprint":"%", "sprint":"%",
"sprintReg":"%", "sprintReg":"%",
"jh":"", "jh":"",
@ -209,25 +263,16 @@ let posModSuffixes = {
/* /*
* Display commands * Display commands
*/ */
let build_all_display_commands = [ let build_overall_display_commands = [
"#defense-stats", "#defense-stats",
"str", "dex", "int", "def", "agi", "str", "dex", "int", "def", "agi",
"!spacer", "!spacer",
"mr", "ms", "mr", "ms",
"hprRaw", "hprPct",
"ls", "ls",
"sdRaw", "sdPct",
"mdRaw", "mdPct",
"!elemental",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"!elemental",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"atkTier",
"poison", "poison",
"ref", "thorns", "ref", "thorns",
"expd", "expd",
"spd", "spd",
"rainbowRaw",
"sprint", "sprintReg", "sprint", "sprintReg",
"jh", "jh",
"xpb", "lb", "lq", "xpb", "lb", "lq",
@ -236,24 +281,65 @@ let build_all_display_commands = [
"gXp", "gSpd", "gXp", "gSpd",
]; ];
let build_offensive_display_commands = [ let build_detailed_display_commands = [
"#defense-stats",
"str", "dex", "int", "def", "agi", "str", "dex", "int", "def", "agi",
"!spacer",
"mr", "ms", "mr", "ms",
"sdRaw", "sdPct", "hprRaw", "hprPct",
"mdRaw", "mdPct",
"ref", "thorns",
"ls", "ls",
"poison", "sdRaw", "nSdRaw", "rSdRaw",
"expd", "sdPct", "nSdPct", "rSdPct",
"spd", "mdRaw", "nMdRaw", "rMdRaw",
"atkTier", "mdPct", "nMdPct", "rMdPct",
"rainbowRaw",
"!elemental", "!elemental",
"fSdRaw", "wSdRaw", "aSdRaw", "tSdRaw", "eSdRaw",
"fSdPct", "wSdPct", "aSdPct", "tSdPct", "eSdPct",
"fMdRaw", "wMdRaw", "aMdRaw", "tMdRaw", "eMdRaw",
"fMdPct", "wMdPct", "aMdPct", "tMdPct", "eMdPct",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"!elemental", "!elemental",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"atkTier",
"poison",
"ref", "thorns",
"expd",
"spd",
"rSdRaw",
"sprint", "sprintReg",
"jh",
"xpb", "lb", "lq",
"spRegen",
"eSteal",
"gXp", "gSpd",
]; ];
// full
//"#defense-stats",
//"str", "dex", "int", "def", "agi",
//"!spacer",
//"mr", "ms",
//"hprRaw", "hprPct",
//"ls",
//"sdRaw", "sdPct",
//"mdRaw", "mdPct",
//"!elemental",
//"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
//"!elemental",
//"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
//"atkTier",
//"poison",
//"ref", "thorns",
//"expd",
//"spd",
//"rainbowRaw",
//"sprint", "sprintReg",
//"jh",
//"xpb", "lb", "lq",
//"spRegen",
//"eSteal",
//"gXp", "gSpd",
let build_basic_display_commands = [ let build_basic_display_commands = [
'#defense-stats', '#defense-stats',
// defense stats [hp, ehp, hpr, ] // defense stats [hp, ehp, hpr, ]
@ -283,8 +369,10 @@ let sq2_item_display_commands = [
"str", "dex", "int", "def", "agi", "str", "dex", "int", "def", "agi",
"hpBonus", "hpBonus",
"hprRaw", "hprPct", "hprRaw", "hprPct",
"sdRaw", "sdPct", "sdRaw", "nSdRaw", "eSdRaw", "tSdRaw", "wSdRaw", "fSdRaw", "aSdRaw", "rSdRaw",
"mdRaw", "mdPct", "sdPct", "nSdPct", "eSdPct", "tSdPct", "wSdPct", "fSdPct", "aSdPct", "rSdPct",
"mdRaw", "nMdRaw", "eMdRaw", "tMdRaw", "wMdRaw", "fMdRaw", "aMdRaw", "rMdRaw",
"mdPct", "nMdPct", "eMdPct", "tMdPct", "wMdPct", "fMdPct", "aMdPct", "rMdPct",
"mr", "ms", "mr", "ms",
"ref", "thorns", "ref", "thorns",
"ls", "ls",
@ -297,7 +385,6 @@ let sq2_item_display_commands = [
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
"!elemental", "!elemental",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"rainbowRaw",
"sprint", "sprintReg", "sprint", "sprintReg",
"jh", "jh",
"xpb", "lb", "lq", "xpb", "lb", "lq",

8
js/drag_drop_touch.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,33 +1,26 @@
//which icons to use
let window_storage = window.localStorage; let window_storage = window.localStorage;
icon_state_stored = window_storage.getItem("newicons");
newIcons = true; newIcons = true;
if (icon_state_stored === "false") {toggleIcons()} if (window_storage.getItem("newicons") === "false") {
toggleIcons();
}
/** Toggle icons on the ENTIRE page. /** Toggle icons on the ENTIRE page.
* *
*/ */
function toggleIcons() { function toggleIcons() {
newIcons = !newIcons; newIcons = !newIcons;
let imgs = document.getElementsByTagName("IMG"); window_storage.setItem("newicons", newIcons.toString());
let favicon = document.querySelector("link[rel~='icon']"); let newOrOld = (newIcons ? "new" : "old");
let toggleiconbutton = document.getElementById("toggle-icon-button");
if (newIcons) { //switch to new let imgs = document.getElementsByTagName("img");
favicon.href = favicon.href.replace("media/icons/old","media/icons/new"); let divs = document.getElementsByClassName("item-display-new-toggleable");
for (const img of imgs) { let favicon = document.querySelector("link[rel~='icon']");
if (img.src.includes("media/icons/old")) {img.src = img.src.replace("media/icons/old","media/icons/new");} favicon.href = favicon.href.replace("media/icons/" + (newIcons ? "old" : "new"), "media/icons/" + newOrOld);
if (img.src.includes("media/items/old")) {img.src = img.src.replace("media/items/old","media/items/new");} for (const img of imgs) {
} // if doesn't contain, replace() does nothing
toggleiconbutton.textContent = "Use Old Icons"; img.src = img.src.replace("media/icons/" + (newIcons ? "old" : "new"), "media/icons/" + newOrOld);
window_storage.setItem("newicons","true"); }
} else { //switch to old for (let i = 0; i < divs.length; i++) {
favicon.href = favicon.href.replace("media/icons/new","media/icons/old"); divs.item(i).style.backgroundImage = "url('../media/items/" + (newIcons ? "new" : "old") + ".png')";
for (const img of imgs) { }
if (img.src.includes("media/icons/new")) {img.src = img.src.replace("media/icons/new","media/icons/old");}
if (img.src.includes("media/items/new")) {img.src = img.src.replace("media/items/new","media/items/old");}
}
toggleiconbutton.textContent = "Use New Icons";
window_storage.setItem("newicons","false");
}
} }

View file

@ -1,17 +1,12 @@
const translate_mappings = { // commented out filters
//"Name": "name", //"Name": "name",
//"Display Name": "displayName", //"Display Name": "displayName",
//"tier"Tier": ", //"Tier": "tier",
//"Set": "set", //"Set": "set",
"Powder Slots": "slots",
//"Type": "type", //"Type": "type",
//"armorType", (deleted) //"Drop type": "drop", BROKEN
//"color", (deleted) //"Quest requirement": "quest", BROKEN
//"lore", (deleted) //"Restriction": "restrict", BROKEN
//"material", (deleted)
"Drop type": "drop",
"Quest requirement": "quest",
"Restriction": "restrict",
//"Base Neutral Damage": "nDam", //"Base Neutral Damage": "nDam",
//"Base Fire Damage": "fDam", //"Base Fire Damage": "fDam",
//"Base Water Damage": "wDam", //"Base Water Damage": "wDam",
@ -19,6 +14,13 @@ const translate_mappings = {
//"Base Thunder Damage": "tDam", //"Base Thunder Damage": "tDam",
//"Base Earth Damage": "eDam", //"Base Earth Damage": "eDam",
//"Base Attack Speed": "atkSpd", //"Base Attack Speed": "atkSpd",
//"Class Requirement": "classReq",
// "Fixed IDs": "fixID", BROKEN
// "Custom Skin": "skin", BROKEN
//"Item Category": "category",
const translate_mappings = {
"Powder Slots": "slots",
"Health": "hp", "Health": "hp",
"Raw Fire Defense": "fDef", "Raw Fire Defense": "fDef",
"Raw Water Defense": "wDef", "Raw Water Defense": "wDef",
@ -26,7 +28,6 @@ const translate_mappings = {
"Raw Thunder Defense": "tDef", "Raw Thunder Defense": "tDef",
"Raw Earth Defense": "eDef", "Raw Earth Defense": "eDef",
"Combat Level": "lvl", "Combat Level": "lvl",
//"Class Requirement": "classReq",
"Req Strength": "strReq", "Req Strength": "strReq",
"Req Dexterity": "dexReq", "Req Dexterity": "dexReq",
"Req Intelligence": "intReq", "Req Intelligence": "intReq",
@ -67,10 +68,6 @@ const translate_mappings = {
"% Air Defense": "aDefPct", "% Air Defense": "aDefPct",
"% Thunder Defense": "tDefPct", "% Thunder Defense": "tDefPct",
"% Earth Defense": "eDefPct", "% Earth Defense": "eDefPct",
"Fixed IDs": "fixID",
"Custom Skin": "skin",
//"Item Category": "category",
"1st Spell Cost %": "-spPct1", "1st Spell Cost %": "-spPct1",
"1st Spell Cost Raw": "-spRaw1", "1st Spell Cost Raw": "-spRaw1",
"2nd Spell Cost %": "-spPct2", "2nd Spell Cost %": "-spPct2",
@ -79,129 +76,180 @@ const translate_mappings = {
"3rd Spell Cost Raw": "-spRaw3", "3rd Spell Cost Raw": "-spRaw3",
"4th Spell Cost %": "-spPct4", "4th Spell Cost %": "-spPct4",
"4th Spell Cost Raw": "-spRaw4", "4th Spell Cost Raw": "-spRaw4",
"Rainbow Spell Damage Raw": "rainbowRaw",
"Rainbow Spell Damage": "rainbowRaw",
"Sprint": "sprint", "Sprint": "sprint",
"Sprint Regen": "sprintReg", "Sprint Regen": "sprintReg",
"Jump Height": "jh", "Jump Height": "jh",
"Loot Quality": "lq", "Loot Quality": "lq",
"Gather XP Bonus": "gXp", "Gather XP Bonus": "gXp",
"Gather Speed Bonus": "gSpd", "Gather Speed Bonus": "gSpd"
}; };
const special_mappings = { const special_mappings = {
"Sum (skill points)": "s:str+dex+int+def+agi", "Sum (skill points)": "str+dex+int+def+agi",
"Sum (Mana Sustain)": "s:mr+ms", "Sum (Mana Sustain)": "mr+ms",
"Sum (Life Sustain)": "s:hpr+ls", "Sum (Life Sustain)": "hpr+ls",
"Sum (Health + Health Bonus)": "s:hp+hpBonus", "Sum (Health + Health Bonus)": "hp+hpBonus",
"No Strength Req": "f:strReq=0", "Base DPS": "(nDam+fDam+wDam+aDam+tDam+eDam) * atkspdmod(atkspd)"
"No Dexterity Req": "f:dexReq=0",
"No Intelligence Req": "f:intReq=0",
"No Agility Req": "f:agiReq=0",
"No Defense Req": "f:defReq=0",
}; };
let itemFilters = document.getElementById("filter-items"); let item_filters = [];
if (itemFilters) { for (let x in translate_mappings) {
for (let x in translate_mappings) { item_filters.push(x);
let el = document.createElement("option"); }
el.value = x; for (let x in special_mappings) {
itemFilters.appendChild(el); item_filters.push(x);
}
for (let x in special_mappings) {
let el = document.createElement("option");
el.value = x;
itemFilters.appendChild(el);
}
} }
let itemCategories = [ "armor", "accessory", "weapon" ]; let item_categories = ["armor", "accessory", "weapon"];
function applyQuery(items, query) { const types = {bow: false, spear: false, wand: false, dagger: false, relik: false, helmet: false, chestplate: false, leggings: false, boots: false, ring: false, bracelet: false, necklace: false};
return items.filter(query.filter, query).sort(query.compare); const rarities = {normal: true, unique: true, set: true, rare: true, legendary: true, fabled: true, mythic: true};
} const filters = [], excludes = [];
let filter_id_counter = 0;
function displayItems(results) { function displayItems(items_copy) {
let items_parent = document.getElementById("main"); let items_parent = document.getElementById("search-results");
for (let i in results) { for (let i in items_copy) {
let item = results[i].itemExp; if (i > 200) {break;}
let box = document.createElement("div"); let item = items_copy[i].itemExp;
box.classList.add("box"); let box = make_elem('div', ['col-lg-3', 'col-sm-6', 'p-2'], {id: 'item'+i});
box.id = "item"+i;
let bckgrdbox = make_elem("div", ["dark-7", "rounded", "px-2", "col-auto"], {id: 'item'+i+'b'});
box.append(bckgrdbox);
items_parent.appendChild(box); items_parent.appendChild(box);
displayExpandedItem(item, box.id); item.set("powders", []);
if (item.get("category") == "weapon") {
apply_weapon_powders(item);
}
displayExpandedItem(item, bckgrdbox.id, true);
} }
} }
let searchDb; let search_db;
let expr_parser;
function doItemSearch() { function do_item_search() {
document.getElementById("summary").style.color = "red"; // to display errors, changed to white if search successful
window.scrollTo(0, 0); window.scrollTo(0, 0);
let queries = []; let queries = [];
queries.push('f:name?="'+document.getElementById("item-name-choice").value.trim()+'"');
let categoryOrType = document.getElementById("item-category-choice").value; // name
if (itemTypes.includes(categoryOrType)) { if (document.getElementById("item-name-choice").value != "") {
queries.push('f:type="'+categoryOrType+'"'); queries.push("f:name?=\"" + document.getElementById("item-name-choice").value.trim() + "\"");
}
else if (itemCategories.includes(categoryOrType)) {
queries.push('f:cat="'+categoryOrType+'"');
} }
let rarity = document.getElementById("item-rarity-choice").value; // types
if (rarity) { let allTypes = true, noTypes = true;
if (rarity === "ANY") { let typeQuery = "f:("
for (const type of Object.keys(types)) {
} if (types[type]) {
else if (rarity === "Sane") { typeQuery += "type=\"" + type + "\"|";
queries.push('f:tiername!="mythic"'); noTypes = false;
} } else {
else { allTypes = false;
queries.push('f:tiername="'+rarity+'"');
} }
} }
if (noTypes) {
document.getElementById("summary").innerHTML = "Error: Cannot search without at least 1 type selected";
return;
} else if (!allTypes) {
queries.push(typeQuery.substring(0, typeQuery.length - 1) + ")");
}
let level_dat = document.getElementById("item-level-choice").value.split("-"); // rarities
queries.push('f:(lvl>='+parseInt(level_dat[0])+'&lvl<='+parseInt(level_dat[1])+')'); let allRarities = true, noRarities = true;
let rarityQuery = "f:("
for (const rarity of Object.keys(rarities)) {
if (rarities[rarity]) {
rarityQuery += "tiername=\"" + rarity + "\"|";
noRarities = false;
} else {
allRarities = false;
}
}
if (noRarities) {
document.getElementById("summary").innerHTML = "Error: Cannot search without at least 1 rarity selected";
return;
} else if (!allRarities) {
queries.push(rarityQuery.substring(0, rarityQuery.length - 1) + ")");
}
for (let i = 1; i <= 4; ++i) { // filters
let raw_dat = document.getElementById("filter"+i+"-choice").value; for (const filter of filters) {
let filter_dat = translate_mappings[raw_dat]; let min = parseInt(filter.min_elem.value);
if (filter_dat !== undefined) { let max = parseInt(filter.max_elem.value);
queries.push("s:"+filter_dat); if (min > max) {
queries.push("f:"+filter_dat+"!=0"); document.getElementById("summary").innerHTML = "Error: The minimum of filter " + filter.input_elem.value + " (" + min + ") is greater than its maximum (" + max + ")";
continue; return;
} }
filter_dat = special_mappings[raw_dat]; let zero_in_min_max = (isNaN(min) || min < 0) && (isNaN(max) || max > 0);
if (filter_dat !== undefined) {
queries.push(filter_dat); let raw_name = filter.input_elem.value;
continue; if (raw_name == "") {
continue; // empty
} }
let filter_name = translate_mappings[raw_name];
if (filter_name === undefined) {
filter_name = special_mappings[raw_name];
if (filter_name === undefined) {
document.getElementById("summary").innerHTML = "Error: The filter \"" + filter.input_elem.value + "\" is not recognized";
return;
}
filter_name = "(" + filter_name + ")";
}
if (!isNaN(min)) {
queries.push("f:" + filter_name + ">=" + min);
}
if (!isNaN(max)) {
queries.push("f:" + filter_name + "<=" + max);
}
if (zero_in_min_max) {
queries.push("f:" + filter_name + "!=0");
}
queries.push("s:" + (filter.ascending ? "0-" : "") + filter_name);
} }
let filterQuery = "true"; // excludes
let sortQueries = []; for (const exclude of excludes) {
let raw_name = exclude.input_elem.value;
if (raw_name == "") {
continue; // empty
}
let filter_name = translate_mappings[raw_name];
if (filter_name === undefined) {
filter_name = special_mappings[raw_name];
if (filter_name === undefined) {
document.getElementById("summary").innerHTML = "Error: The excluded filter \"" + exclude.input_elem.value + "\" is not recognized";
return;
}
filter_name = "(" + filter_name + ")";
}
queries.push("f:" + filter_name + "=0");
}
let filter_query = "true";
let sort_queries = [];
console.log(queries); console.log(queries);
for (const query of queries) { for (const query of queries) {
if (query.startsWith("s:")) { if (query.startsWith("s:")) {
sortQueries.push(query.slice(2)); sort_queries.push(query.slice(2));
} }
else if (query.startsWith("f:")) { else if (query.startsWith("f:")) {
filterQuery = filterQuery + "&" + query.slice(2); filter_query = filter_query + "&" + query.slice(2);
} }
} }
console.log(filterQuery); document.getElementById("search-results").textContent = "";
console.log(sortQueries);
let results = []; let results = [];
try { try {
const filterExpr = exprParser.parse(filterQuery); const filter_expr = expr_parser.parse(filter_query);
const sortExprs = sortQueries.map(q => exprParser.parse(q)); const sort_exprs = sort_queries.map(q => expr_parser.parse(q));
for (let i = 0; i < searchDb.length; ++i) { for (let i = 0; i < search_db.length; ++i) {
const item = searchDb[i][0]; const item = search_db[i][0];
const itemExp = searchDb[i][1]; const itemExp = search_db[i][1];
if (checkBool(filterExpr.resolve(item, itemExp))) { if (checkBool(filter_expr.resolve(item, itemExp))) {
results.push({ item, itemExp, sortKeys: sortExprs.map(e => e.resolve(item, itemExp)) }); results.push({ item, itemExp, sortKeys: sort_exprs.map(e => e.resolve(item, itemExp)) });
} }
} }
results.sort((a, b) => { results.sort((a, b) => {
@ -211,13 +259,264 @@ function doItemSearch() {
document.getElementById("summary").textContent = e.message; document.getElementById("summary").textContent = e.message;
return; return;
} }
document.getElementById("summary").textContent = results.length + " results." document.getElementById("summary").textContent = results.length + " results:";
document.getElementById("summary").style.color = "white";
displayItems(results); displayItems(results);
} }
function init_items() { function init_items() {
searchDb = items.filter( i => ! i.remapID ).map( i => [i, expandItem(i, [])] ); search_db = items.filter( i => ! i.remapID ).map( i => [i, expandItem(i, [])] );
exprParser = new ExprParser(itemQueryProps, itemQueryFuncs); expr_parser = new ExprParser(itemQueryProps, itemQueryFuncs);
// init type buttons
for (const type of Object.keys(types)) {
document.getElementById("type-" + type).addEventListener("click", function() {
types[type] = !types[type];
this.classList.toggle("type-selected");
});
}
document.getElementById("all-types").addEventListener("click", function() {
for (const type of Object.keys(types)) {
types[type] = true;
document.getElementById("type-" + type).classList.add("type-selected");
}
});
document.getElementById("none-types").addEventListener("click", function() {
for (const type of Object.keys(types)) {
types[type] = false;
document.getElementById("type-" + type).classList.remove("type-selected");
}
});
// init rarity buttons
for (const rarity of Object.keys(rarities)) {
document.getElementById("rarity-" + rarity).addEventListener("click", function() {
rarities[rarity] = !rarities[rarity];
this.classList.toggle("rarity-selected");
});
}
document.getElementById("all-rarities").addEventListener("click", function() {
for (const rarity of Object.keys(rarities)) {
rarities[rarity] = true;
document.getElementById("rarity-" + rarity).classList.add("rarity-selected");
}
});
document.getElementById("none-rarities").addEventListener("click", function() {
for (const rarity of Object.keys(rarities)) {
rarities[rarity] = false;
document.getElementById("rarity-" + rarity).classList.remove("rarity-selected");
}
});
// filters
document.getElementById("add-filter").addEventListener("click", create_filter);
document.getElementById("add-exclude").addEventListener("click", create_exclude);
create_filter();
filters[0].input_elem.value = "Combat Level";
init_filter_drag();
} }
load_init(init_items); function reset_item_search() {
document.getElementById("item-name-choice").value = "";
document.getElementById("all-types").click();
document.getElementById("all-rarities").click();
}
function create_filter() {
let data = {ascending: false};
let row = make_elem("div", ["row", "filter-row"], {});
let col = make_elem("div", ["col"], {});
row.appendChild(col);
data.div = row;
let reorder_img = make_elem("img", ["reorder-filter"], {src: "../media/icons/3-lines.svg", draggable: "true"});
col.appendChild(reorder_img);
let filter_input = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "filter-input"],
{id: "filter-input-" + filter_id_counter, type: "text", placeholder: "Filter"}
);
filter_id_counter++;
col.appendChild(filter_input);
data.input_elem = filter_input;
let asc_desc = make_elem("div", [], {style: "cursor: pointer; display: inline-block;"});
asc_desc.appendChild(make_elem("img", ["desc-icon", "asc-sel"], {src: "../media/icons/triangle.svg"}));
asc_desc.appendChild(make_elem("img", ["asc-icon"], {src: "../media/icons/triangle.svg"}));
asc_desc.addEventListener("click", function() {
data.ascending = !data.ascending;
asc_desc.children[0].classList.toggle("asc-sel");
asc_desc.children[1].classList.toggle("asc-sel");
});
col.appendChild(asc_desc);
let min = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "min-max-input"],
{type: "number", placeholder: "-\u221E"}
);
col.appendChild(min);
data.min_elem = min;
let to = make_elem("span", [], {innerHTML: "&nbsp;to&nbsp;"});
col.appendChild(to);
let max = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "min-max-input"],
{type: "number", placeholder: "\u221E"}
);
col.appendChild(max);
data.max_elem = max;
let trash = make_elem("img", ["delete-filter"], {src: "../media/icons/trash.svg"});
trash.addEventListener("click", function() {
filters.splice(Array.from(row.parentElement.children).indexOf(row) - 1, 1);
row.remove();
});
col.appendChild(trash);
document.getElementById("filter-container").insertBefore(row, document.getElementById("add-filter").parentElement);
filters.push(data);
init_filter_dropdown(data);
}
let currently_dragging = null;
function init_filter_drag() {
let container = document.getElementById("filter-container");
container.addEventListener("dragstart", function(e) {
if (e.path[0].classList.contains("reorder-filter")) {
currently_dragging = filters[Array.from(e.path[3].children).indexOf(e.path[2]) - 1];
} else {
e.preventDefault();
}
});
container.addEventListener("dragenter", function(e) {
e.preventDefault();
});
container.addEventListener("dragleave", function(e) {
e.preventDefault();
});
container.addEventListener("dragend", function(e) {
e.preventDefault();
for (const el of document.getElementsByClassName("filter-dragged-over")) {
el.classList.remove("filter-dragged-over");
}
currently_dragging = null;
});
container.addEventListener("dragover", function(e) {
e.preventDefault();
for (const el of document.getElementsByClassName("filter-dragged-over")) {
el.classList.remove("filter-dragged-over");
}
if (!e.path.includes(currently_dragging.div)) {
for (let i = 0; i < e.path.length; i++) {
if (e.path[i].classList.contains("filter-row")) {
e.path[i].classList.add("filter-dragged-over");
break;
}
}
}
});
container.addEventListener("drop", function(e) {
e.preventDefault();
for (const el of document.getElementsByClassName("filter-dragged-over")) {
el.classList.remove("filter-dragged-over");
}
if (!e.path.includes(currently_dragging.div)) {
for (let i = 0; i < e.path.length; i++) {
if (e.path[i].classList.contains("filter-row")) {
let old_index = filters.indexOf(currently_dragging);
let new_index = Array.from(e.path[i + 1].children).indexOf(e.path[i]) - 1;
filters.splice(old_index, 1);
filters.splice(new_index, 0, currently_dragging);
currently_dragging.div.remove();
container.insertBefore(currently_dragging.div, container.children[new_index + 1]);
break;
}
}
}
currently_dragging = null;
});
}
function create_exclude() {
let data = {};
let row = make_elem("div", ["row", "filter-row"], {});
let col = make_elem("div", ["col"], {});
row.appendChild(col);
data.div = row;
let filter_input = make_elem("input",
["col", "border-dark", "text-light", "dark-5", "rounded", "scaled-font", "form-control", "form-control-sm", "filter-input"],
{id: "filter-input-" + filter_id_counter, type: "text", placeholder: "Excluded Filter"}
);
filter_id_counter++;
col.appendChild(filter_input);
data.input_elem = filter_input;
let trash = make_elem("img", ["delete-filter"], {src: "../media/icons/trash.svg"});
trash.addEventListener("click", function() {
excludes.splice(Array.from(row.parentElement.children).indexOf(row) - 1, 1);
row.remove();
});
col.appendChild(trash);
document.getElementById("exclude-container").insertBefore(row, document.getElementById("add-exclude").parentElement);
excludes.push(data);
init_filter_dropdown(data);
}
function init_filter_dropdown(filter) {
let field_choice = filter.input_elem;
field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));};
filter.autoComplete = new autoComplete({
data: {
src: item_filters,
},
threshold: 0,
selector: "#" + field_choice.id,
wrapper: false,
resultsList: {
maxResults: 100,
tabSelect: true,
noResults: true,
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
element: (list, data) => {
let position = field_choice.getBoundingClientRect();
list.style.top = position.bottom + window.scrollY +"px";
list.style.left = position.x+"px";
list.style.width = position.width+"px";
list.style.maxHeight = position.height * 4 +"px";
if (!data.results.length) {
const message = make_elem('li', ['scaled-font'], {textContent: "No results found!"});
list.prepend(message);
};
},
},
resultItem: {
class: "scaled-font search-item",
selected: "dark-5",
},
events: {
input: {
selection: (event) => {
if (event.detail.selection.value) {
event.target.value = event.detail.selection.value;
};
},
},
}
});
}
(async function() {
await Promise.resolve(load_init());
init_items();
})();

View file

@ -234,7 +234,7 @@ function stringify(v) {
return typeof v === 'number' ? (Math.round(v * 100) / 100).toString() : v; return typeof v === 'number' ? (Math.round(v * 100) / 100).toString() : v;
} }
function init_items2() { function init_items_adv() {
const itemList = document.getElementById('item-list'); const itemList = document.getElementById('item-list');
const itemListFooter = document.getElementById('item-list-footer'); const itemListFooter = document.getElementById('item-list-footer');
@ -328,7 +328,11 @@ function init_items2() {
for (let i = 0; i < searchMax; i++) { for (let i = 0; i < searchMax; i++) {
const result = searchResults[i]; const result = searchResults[i];
itemEntries[i].classList.add('visible'); itemEntries[i].classList.add('visible');
displaysq2ExpandedItem(result.itemExp, `item-entry-${i}`); result.itemExp.set("powders", []);
if (result.itemExp.get("category") == "weapon") {
apply_weapon_powders(result.itemExp);
}
displayExpandedItem(result.itemExp, `item-entry-${i}`);
if (result.sortKeys.length > 0) { if (result.sortKeys.length > 0) {
const sortKeyListContainer = document.createElement('div'); const sortKeyListContainer = document.createElement('div');
sortKeyListContainer.classList.add('row'); sortKeyListContainer.classList.add('row');
@ -391,4 +395,7 @@ function init_items2() {
.addEventListener('mousedown', e => scrollTo({ top: 0, behavior: 'smooth' })); .addEventListener('mousedown', e => scrollTo({ top: 0, behavior: 'smooth' }));
} }
load_init(init_items2); (async function() {
await Promise.resolve(load_init());
init_items_adv();
})();

View file

@ -1,4 +1,4 @@
const DB_VERSION = 101; const DB_VERSION = 120;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
let db; let db;
@ -92,13 +92,32 @@ function clean_item(item) {
} }
} }
async function load_old_version(version_str) {
load_in_progress = true;
let getUrl = window.location;
let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// No random string -- we want to use caching
let url = `${baseUrl}/data/${version_str}/items.json`;
let result = await (await fetch(url)).json();
items = result.items;
for (const item of items) {
clean_item(item);
}
let sets_ = result.sets;
sets = new Map();
for (const set in sets_) {
sets.set(set, sets_[set]);
}
init_maps();
load_complete = true;
}
/* /*
* Load item set from remote DB (aka a big json file). Calls init() on success. * Load item set from remote DB (aka a big json file). Calls init() on success.
*/ */
async function load() { async function load() {
let getUrl = window.location; let getUrl = window.location;
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1]; let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// "Random" string to prevent caching! // "Random" string to prevent caching!
let url = baseUrl + "/compress.json?"+new Date(); let url = baseUrl + "/compress.json?"+new Date();
let result = await (await fetch(url)).json(); let result = await (await fetch(url)).json();
@ -150,7 +169,7 @@ async function load_init() {
console.log("Skipping load...") console.log("Skipping load...")
} }
else { else {
load_in_progress = true load_in_progress = true;
if (reload) { if (reload) {
console.log("Using new data...") console.log("Using new data...")
await load(); await load();
@ -190,7 +209,7 @@ async function load_init() {
} }
// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon. // List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon.
for (const it of itemTypes) { for (const it of item_types) {
itemLists.set(it, []); itemLists.set(it, []);
} }
@ -254,5 +273,9 @@ function init_maps() {
redirectMap.set(item.id, item.remapID); redirectMap.set(item.id, item.remapID);
} }
} }
console.log(itemMap); for (const [set_name, set_data] of sets) {
for (const item_name of set_data.items) {
itemMap.get(item_name).set = set_name;
}
}
} }

View file

@ -1,4 +1,4 @@
const ING_DB_VERSION = 14; const ING_DB_VERSION = 19;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
@ -59,22 +59,41 @@ function clean_ing(ing) {
} }
} }
async function load_ings_old_version(version_str) {
iload_in_progress = true;
let getUrl = window.location;
let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// No random string -- we want to use caching
let url = `${baseUrl}/data/${version_str}/ingreds.json`;
let result = await (await fetch(url)).json();
ings = result;
for (const id in ings) {
clean_ing(ings[id]);
}
url = baseUrl + "/recipes_compress.json";
result = await (await fetch(url)).json();
recipes = result.recipes;
init_maps();
iload_complete = true;
}
/* /*
* Load item set from remote DB (aka a big json file). Calls init() on success. * Load item set from remote DB (aka a big json file). Calls init() on success.
*/ */
async function load_ings() { async function load_ings() {
let getUrl = window.location; let getUrl = window.location;
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1]; let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// "Random" string to prevent caching! // "Random" string to prevent caching!
let url = baseUrl + "/ingreds_compress.json?"+new Date(); let url = baseUrl + "/ingreds_compress.json?"+new Date();
url = url.replace(/\w+.html/, "") ;
let result = await (await fetch(url)).json(); let result = await (await fetch(url)).json();
result = await (await fetch(url)).json(); result = await (await fetch(url)).json();
ings = result; ings = result;
url = url.replace("/ingreds_compress.json", "/recipes_compress.json"); url = baseUrl + "/recipes_compress.json?"+new Date();
result = await (await fetch(url)).json(); result = await (await fetch(url)).json();
recipes = result.recipes; recipes = result.recipes;
@ -197,7 +216,7 @@ function init_ing_maps() {
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0} posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}
}; };
ing.id = 4001 + ing.pid; ing.id = 4001 + ing.pid;
ing.diplayName = ing.name; ing.displayName = ing.name;
switch(i) { switch(i) {
case 0: case 0:
ing.itemIDs["strReq"] = powderIng["skpReq"]; ing.itemIDs["strReq"] = powderIng["skpReq"];

View file

@ -1,4 +1,4 @@
const TOME_DB_VERSION = 3; const TOME_DB_VERSION = 6;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
let tdb; let tdb;
@ -34,13 +34,27 @@ async function load_tome_local() {
}); });
} }
async function load_tome_old_version(version_str) {
tload_in_progress = true;
let getUrl = window.location;
let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// No random string -- we want to use caching
let url = `${baseUrl}/data/${version_str}/tomes.json`;
let result = await (await fetch(url)).json();
tomes = result.tomes;
for (const tome of tomes) {
//dependency on clean_item in load.js
clean_item(tome);
}
init_tome_maps();
tload_complete = true;
}
/* /*
* Load tome set from remote DB (json). Calls init() on success. * Load tome set from remote DB (json). Calls init() on success.
*/ */
async function load_tome() { async function load_tome() {
let getUrl = window.location; let getUrl = window.location;
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1]; let baseUrl = `${getUrl.protocol}//${getUrl.host}/`;
// "Random" string to prevent caching! // "Random" string to prevent caching!
let url = baseUrl + "/tomes.json?"+new Date(); let url = baseUrl + "/tomes.json?"+new Date();
let result = await (await fetch(url)).json(); let result = await (await fetch(url)).json();
@ -119,11 +133,13 @@ async function load_tome_init() {
}); });
} }
let none_tomes = [ const none_tomes_info = [
["tome", "weaponTome", "No Weapon Tome"], ["tome", "weaponTome", "No Weapon Tome"],
["tome", "armorTome", "No Armor Tome"], ["tome", "armorTome", "No Armor Tome"],
["tome", "guildTome", "No Guild Tome"] ["tome", "guildTome", "No Guild Tome"]
]; ];
let none_tomes;
function init_tome_maps() { function init_tome_maps() {
//warp //warp
tomeMap = new Map(); tomeMap = new Map();
@ -135,12 +151,13 @@ function init_tome_maps() {
tomeLists.set(it, []); tomeLists.set(it, []);
} }
none_tomes = [];
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
let tome = Object(); let tome = Object();
tome.slots = 0; tome.slots = 0;
tome.category = none_tomes[i][0]; tome.category = none_tomes_info[i][0];
tome.type = none_tomes[i][1]; tome.type = none_tomes_info[i][1];
tome.name = none_tomes[i][2]; tome.name = none_tomes_info[i][2];
tome.displayName = tome.name; tome.displayName = tome.name;
tome.set = null; tome.set = null;
tome.quest = null; tome.quest = null;
@ -159,7 +176,7 @@ function init_tome_maps() {
//dependency - load.js //dependency - load.js
clean_item(tome); clean_item(tome);
none_tomes[i] = tome; none_tomes.push(tome);
} }
tomes = tomes.concat(none_tomes); tomes = tomes.concat(none_tomes);
for (const tome of tomes) { for (const tome of tomes) {

View file

@ -55,11 +55,11 @@ class PowderSpecial{
function _ps(a,b,c,d,e) { return new PowderSpecial(a,b,c,d,e); } //bruh moment function _ps(a,b,c,d,e) { return new PowderSpecial(a,b,c,d,e); } //bruh moment
let powderSpecialStats = [ let powderSpecialStats = [
_ps("Quake",new Map([["Radius",[4.4,4.9,5.4,5.9,6.4]], ["Damage",[155,220,285,350,415]] ]),"Rage",new Map([ ["Damage", [0.3,0.4,0.5,0.7,1.0]],["Description", "% " + "\u2764" + " Missing"] ]), 396), //e _ps("Quake",new Map([["Radius",[4.5,5,5.5,6,6.5]], ["Damage",[240,280,320,360,400]] ]),"Rage",new Map([ ["Damage", [0.2,0.4,0.6,0.8,1.0]],["Description", "% " + "\u2764" + " Missing below 75%"] ]),300), //e
_ps("Chain Lightning",new Map([ ["Chains", [5,6,7,8,9]], ["Damage", [200,225,250,275,300]] ]),"Kill Streak",new Map([ ["Damage", [3,4.5,6,7.5,9]],["Duration", [5,5,5,5,5]],["Description", "Mob Killed"] ]),200), //t _ps("Chain Lightning",new Map([ ["Chains", [5,6,7,8,9]], ["Damage", [200,225,250,275,300]] ]),"Kill Streak",new Map([ ["Damage", [3,4.5,6,7.5,9]],["Duration", [5,5,5,5,5]],["Description", "Mob Killed"] ]),150), //t
_ps("Curse",new Map([ ["Duration", [7,7.5,8,8.5,9]],["Damage Boost", [90,120,150,180,210]] ]),"Concentration",new Map([ ["Damage", [1,2,3,4,5]],["Duration",[1,1,1,1,1]],["Description", "Mana Used"] ]),150), //w _ps("Curse",new Map([ ["Duration", [4,4,4,4,4]],["Damage Boost", [10,15,20,25,30]] ]),"Concentration",new Map([ ["Damage", [0.05,0.1,0.15,0.2,0.25]],["Duration",[1,1,1,1,1]],["Description", "Mana Used"] ]),100), //w
_ps("Courage",new Map([ ["Duration", [6,6.5,7,7.5,8]],["Damage", [75,87.5,100,112.5,125]],["Damage Boost", [70,90,110,130,150]] ]),"Endurance",new Map([ ["Damage", [2,3,4,5,6]],["Duration", [8,8,8,8,8]],["Description", "Hit Taken"] ]),200), //f _ps("Courage",new Map([ ["Duration", [4,4,4,4,4]],["Damage", [60, 70, 80, 90, 100]],["Damage Boost", [10,12.5,15,17.5,20]] ]),"Endurance",new Map([ ["Damage", [2,3,4,5,6]],["Duration", [8,8,8,8,8]],["Description", "Hit Taken"] ]),100), //f
_ps("Wind Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [400,450,500,550,600]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a _ps("Wind Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [100,125,150,175,200]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a
]; ];
/** /**
@ -158,7 +158,46 @@ function calc_weapon_powder(weapon, damageBases) {
neutralRemainingRaw = damages[0].slice(); neutralRemainingRaw = damages[0].slice();
neutralBase = damages[0].slice(); neutralBase = damages[0].slice();
} }
//apply powders to weapon (1.21 fked implementation)
let powder_apply_order = [];
let powder_apply_map = new Map();
for (const powderID of powders) {
const powder = powderStats[powderID];
// Bitwise to force conversion to integer (integer division).
const element = (powderID/6) | 0;
const conversion_ratio = powder.convert/100;
if (powder_apply_map.has(element)) {
let apply_info = powder_apply_map.get(element);
apply_info.conv += conversion_ratio;
apply_info.min += powder.min;
apply_info.max += powder.max;
}
else {
let apply_info = {
conv: conversion_ratio,
min: powder.min,
max: powder.max
};
powder_apply_order.push(element);
powder_apply_map.set(element, apply_info);
}
}
for (const element of powder_apply_order) {
const apply_info = powder_apply_map.get(element);
const conversion_ratio = apply_info.conv;
const min_diff = Math.min(neutralRemainingRaw[0], conversion_ratio * neutralRemainingRaw[0]);
const max_diff = Math.min(neutralRemainingRaw[1], conversion_ratio * neutralRemainingRaw[1]);
neutralRemainingRaw[0] -= min_diff;
neutralRemainingRaw[1] -= max_diff;
damages[element+1][0] += min_diff;
damages[element+1][1] += max_diff;
damages[element+1][0] += apply_info.min;
damages[element+1][1] += apply_info.max;
}
/*
//apply powders to weapon //apply powders to weapon
for (const powderID of powders) { for (const powderID of powders) {
const powder = powderStats[powderID]; const powder = powderStats[powderID];
@ -181,14 +220,15 @@ function calc_weapon_powder(weapon, damageBases) {
damages[element+1][0] += powder.min; damages[element+1][0] += powder.min;
damages[element+1][1] += powder.max; damages[element+1][1] += powder.max;
} }
*/
// The ordering of these two blocks decides whether neutral is present when converted away or not.
damages[0] = neutralRemainingRaw;
// The ordering of these two blocks decides whether neutral is present when converted away or not. // The ordering of these two blocks decides whether neutral is present when converted away or not.
let present_elements = [] let present_elements = []
for (const damage of damages) { for (const damage of damages) {
present_elements.push(damage[1] > 0); present_elements.push(damage[1] > 0);
} }
// The ordering of these two blocks decides whether neutral is present when converted away or not.
damages[0] = neutralRemainingRaw;
return [damages, present_elements]; return [damages, present_elements];
} }

View file

@ -1,115 +1,550 @@
let queryTypeMap = new Map(); // dynamic type casts
function checkBool(v) {
class NameQuery { if (typeof v !== 'boolean') throw new Error(`Expected boolean, but got ${typeof v}`);
constructor(string) { this.queryString = string.toLowerCase(); } return v;
filter(item) {
if (item.get("restrict") && item.get("restrict") === "DEPRECATED") {
return false;
}
return (item.get("displayName").toLowerCase().includes(this.queryString));
}
compare(a, b) { return a < b; }
}
queryTypeMap.set("name", function(s) { return new NameQuery(s); } );
class LevelRangeQuery {
constructor(min, max) { this.min = min; this.max = max; }
filter(item) {
if (item.get("remapID") === undefined) {
return (item.get("lvl") <= this.max && item.get("lvl") >= this.min);
}
return false;
}
compare(a, b) { return a > b; }
} }
class NegateQuery { function checkNum(v) {
constructor(id) { if (typeof v === 'boolean') {
this.id = id; if (v) return 1;
this.compare = function(a, b) { return 0; }; return 0;
} }
if (typeof v !== 'number') throw new Error(`Expected number, but got ${typeof v}`);
filter(item) { return v;
return (!item.get(this.id)) || (item.get(this.id) == 0);
}
}
queryTypeMap.set("null", function(s) { return new IdQuery(s); } );
class IdQuery {
constructor(id) {
this.id = id;
if (nonRolledIDs.includes(id)) {
this.compare = function(a, b) {
return b.get(id) - a.get(id);
};
this.filter = function(a) {
return a.get(this.id);
}
console.log("QUERY: ID, NONROLL");
}
else if (reversedIDs.includes(id)) {
this.compare = function(a, b) {
return a.get("maxRolls").get(id) - b.get("maxRolls").get(id);
};
this.filter = function(a) {
return a.get("maxRolls").get(this.id);
}
console.log("QUERY: ID, REVERSE");
}
else {
this.compare = function(a, b) {
return b.get("maxRolls").get(id) - a.get("maxRolls").get(id);
};
this.filter = function(a) {
return a.get("maxRolls").get(this.id);
}
console.log("QUERY: ID, ,,,");
}
}
}
queryTypeMap.set("stat", function(s) { return new IdQuery(s); } );
class IdMatchQuery {
constructor(id, value) {
this.id = id;
this.value = value;
this.compare = function(a, b) {
return 0;
};
}
filter(item) {
return item.get(this.id) && (item.get(this.id) == this.value);
}
} }
class SumQuery { function checkStr(v) {
constructor(ids) { if (typeof v !== 'string') throw new Error(`Expected string, but got ${typeof v}`);
let getters = []; return v;
for (const id of ids) { }
if (nonRolledIDs.includes(id)) {
getters.push(a => a.get(id)); function checkComparable(v) {
} if (typeof v === 'boolean') throw new Error('Boolean is not comparable');
else { return v;
getters.push(a => a.get("maxRolls").get(id)); }
}
// properties of items that can be looked up
} // each entry is a function `(item, extended item) -> value`
this.compare = function(a, b) { const itemQueryProps = (function() {
let balance = 0; const props = {};
for (const getter of getters) {
if (getter(a)) { balance -= getter(a); } function prop(names, type, resolve) {
if (getter(b)) { balance += getter(b); } if (Array.isArray(names)) {
} for (name of names) {
return balance; props[name] = { type, resolve };
}; }
} } else {
props[names] = { type, resolve };
filter(item) { }
return true; }
}
function maxId(names, idKey) {
prop(names, 'number', (i, ie) => ie.get('maxRolls').get(idKey) || 0);
}
function minId(names, idKey) {
prop(names, 'number', (i, ie) => ie.get('minRolls').get(idKey) || 0);
}
function rangeAvg(names, getProp) {
prop(names, 'number', (i, ie) => {
const range = getProp(i, ie);
if (!range) return 0;
const ndx = range.indexOf('-');
return (parseInt(range.substring(0, ndx), 10) + parseInt(range.substring(ndx + 1), 10)) / 2;
});
}
function map(names, comps, outType, f) {
return prop(names, outType, (i, ie) => {
const args = [];
for (let k = 0; k < comps.length; k++) args.push(comps[k].resolve(i, ie));
return f.apply(null, args);
});
}
function sum(names, ...comps) {
return map(names, comps, 'number', (...summands) => {
let total = 0;
for (let i = 0; i < summands.length; i++) total += summands[i];
return total;
});
}
prop('name', 'string', (i, ie) => i.displayName || i.name);
prop('type', 'string', (i, ie) => i.type);
prop(['cat', 'category'], 'string', (i, ie) => i.category);
const tierIndices = { Normal: 0, Unique: 1, Set: 2, Rare: 3, Legendary: 4, Fabled: 5, Mythic: 6 };
prop(['rarityname', 'raritystr', 'tiername', 'tierstr'], 'string', (i, ie) => i.tier);
prop(['rarity', 'tier'], 'number', (i, ie) => tierIndices[i.tier]);
prop(['level', 'lvl', 'combatlevel', 'combatlvl'], 'number', (i, ie) => i.lvl);
prop(['strmin', 'strreq'], 'number', (i, ie) => i.strReq);
prop(['dexmin', 'dexreq'], 'number', (i, ie) => i.dexReq);
prop(['intmin', 'intreq'], 'number', (i, ie) => i.intReq);
prop(['defmin', 'defreq'], 'number', (i, ie) => i.defReq);
prop(['agimin', 'agireq'], 'number', (i, ie) => i.agiReq);
sum(['summin', 'sumreq', 'totalmin', 'totalreq'], props.strmin, props.dexmin, props.intmin, props.defmin, props.agimin);
prop('str', 'number', (i, ie) => i.str);
prop('dex', 'number', (i, ie) => i.dex);
prop('int', 'number', (i, ie) => i.int);
prop('def', 'number', (i, ie) => i.def);
prop('agi', 'number', (i, ie) => i.agi);
sum(['skillpoints', 'skillpts', 'attributes', 'attrs'], props.str, props.dex, props.int, props.def, props.agi);
rangeAvg(['neutraldmg', 'neutraldam', 'ndmg', 'ndam'], (i, ie) => i.nDam);
rangeAvg(['earthdmg', 'earthdam', 'edmg', 'edam'], (i, ie) => i.eDam);
rangeAvg(['thunderdmg', 'thunderdam', 'tdmg', 'tdam'], (i, ie) => i.tDam);
rangeAvg(['waterdmg', 'waterdam', 'wdmg', 'wdam'], (i, ie) => i.wDam);
rangeAvg(['firedmg', 'firedam', 'fdmg', 'fdam'], (i, ie) => i.fDam);
rangeAvg(['airdmg', 'airdam', 'admg', 'adam'], (i, ie) => i.aDam);
sum(['sumdmg', 'sumdam', 'totaldmg', 'totaldam'], props.ndam, props.edam, props.tdam, props.wdam, props.fdam, props.adam);
maxId(['earthdmg%', 'earthdam%', 'edmg%', 'edam%', 'edampct'], 'eDamPct');
maxId(['thunderdmg%', 'thunderdam%', 'tdmg%', 'tdam%', 'tdampct'], 'tDamPct');
maxId(['waterdmg%', 'waterdam%', 'wdmg%', 'wdam%', 'wdampct'], 'wDamPct');
maxId(['firedmg%', 'firedam%', 'fdmg%', 'fdam%', 'fdampct'], 'fDamPct');
maxId(['airdmg%', 'airdam%', 'admg%', 'adam%', 'adampct'], 'aDamPct');
sum(['sumdmg%', 'sumdam%', 'totaldmg%', 'totaldam%', 'sumdampct', 'totaldampct'], props.edampct, props.tdampct, props.wdampct, props.fdampct, props.adampct);
maxId(['mainatkdmg', 'mainatkdam', 'mainatkdmg%', 'mainatkdam%', 'meleedmg', 'meleedam', 'meleedmg%', 'meleedam%', 'mdpct'], 'mdPct');
maxId(['mainatkrawdmg', 'mainatkrawdam', 'mainatkneutraldmg', 'mainatkneutraldam', 'meleerawdmg', 'meleerawdam', 'meleeneutraldmg', 'meleeneutraldam', 'mdraw'], 'mdRaw');
maxId(['spelldmg', 'spelldam', 'spelldmg%', 'spelldam%', 'sdpct'], 'sdPct');
maxId(['spellrawdmg', 'spellrawdam', 'spellneutraldmg', 'spellneutraldam', 'sdraw'], 'sdRaw');
maxId(['rainbowraw'], '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);
maxId(['bonusattackspeed', 'bonusatkspd', 'attackspeedid', 'atkspdid', 'atktier'], 'atkTier');
sum(['sumattackspeed', 'totalattackspeed', 'sumatkspd', 'totalatkspd', 'sumatktier', 'totalatktier'], props.atkspd, props.atktier);
prop(['earthdef', 'edef'], 'number', (i, ie) => i.eDef || 0);
prop(['thunderdef', 'tdef'], 'number', (i, ie) => i.tDef || 0);
prop(['waterdef', 'wdef'], 'number', (i, ie) => i.wDef || 0);
prop(['firedef', 'fdef'], 'number', (i, ie) => i.fDef || 0);
prop(['airdef', 'adef'], 'number', (i, ie) => i.aDef || 0);
sum(['sumdef', 'totaldef'], props.edef, props.tdef, props.wdef, props.fdef, props.adef);
maxId(['earthdef%', 'edef%', 'edefpct'], 'eDefPct');
maxId(['thunderdef%', 'tdef%', 'tdefpct'], 'tDefPct');
maxId(['waterdef%', 'wdef%', 'wdefpct'], 'wDefPct');
maxId(['firedef%', 'fdef%', 'fdefpct'], 'fDefPct');
maxId(['airdef%', 'adef%', 'adefpct'], 'aDefPct');
sum(['sumdef%', 'totaldef%', 'sumdefpct', 'totaldefpct'], props.edefpct, props.tdefpct, props.wdefpct, props.fdefpct, props.adefpct);
prop(['health', 'hp'], 'number', (i, ie) => i.hp || 0);
maxId(['bonushealth', 'healthid', 'bonushp', 'hpid', 'hpbonus'], 'hpBonus');
sum(['sumhealth', 'sumhp', 'totalhealth', 'totalhp'], props.hp, props.hpid);
maxId(['hpregen', 'hpr', 'hr', 'hprraw'], 'hprRaw');
maxId(['hpregen%', 'hpr%', 'hr%', 'hprpct'], 'hprPct');
maxId(['lifesteal', 'ls'], 'ls');
maxId(['manaregen', 'mr'], 'mr');
maxId(['manasteal', 'ms'], 'ms');
maxId(['walkspeed', 'movespeed', 'ws', 'spd'], 'spd');
maxId('sprint', 'sprint');
maxId(['sprintregen', 'sprintreg'], 'sprintReg');
maxId(['jumpheight', 'jh'], 'jh');
maxId(['spellcost1', 'rawspellcost1', 'spcost1', 'spraw1'], 'spRaw1');
maxId(['spellcost1%', 'spcost1%', 'sppct1'], 'spPct1');
maxId(['spellcost2', 'rawspellcost2', 'spcost2', 'spraw2'], 'spRaw2');
maxId(['spellcost2%', 'spcost2%', 'sppct2'], 'spPct2');
maxId(['spellcost3', 'rawspellcost3', 'spcost3', 'spraw3'], 'spRaw3');
maxId(['spellcost3%', 'spcost3%', 'sppct3'], 'spPct3');
maxId(['spellcost4', 'rawspellcost4', 'spcost4', 'spraw4'], 'spRaw4');
maxId(['spellcost4%', 'spcost4%', 'sppct4'], 'spPct4');
sum(['sumspellcost', 'totalspellcost', 'sumrawspellcost', 'totalrawspellcost', 'sumspcost', 'totalspcost', 'sumspraw', 'totalspraw'], props.spraw1, props.spraw2, props.spraw3, props.spraw4);
sum(['sumspellcost%', 'totalspellcost%', 'sumspcost%', 'totalspcost%', 'sumsppct', 'totalsppct'], props.sppct1, props.sppct2, props.sppct3, props.sppct4);
maxId(['exploding', 'expl', 'expd'], 'expd');
maxId('poison', 'poison');
maxId('thorns', 'thorns');
maxId(['reflection', 'refl', 'ref'], 'ref');
maxId(['soulpointregen', 'spr', 'spregen'], 'spRegen');
maxId(['lootbonus', 'lb'], 'lb');
maxId(['xpbonus', 'xpb', 'xb'], 'xpb');
maxId(['stealing', 'esteal'], 'eSteal');
prop(['powderslots', 'powders', 'slots', 'sockets'], 'number', (i, ie) => i.slots || 0);
return props;
})();
// functions that can be called in query expressions
const itemQueryFuncs = {
max: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to max()');
let runningMax = -Infinity;
for (let i = 0; i < args.length; i++) {
if (checkNum(args[i]) > runningMax) runningMax = args[i];
}
return runningMax;
}
},
min: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to min()');
let runningMin = Infinity;
for (let i = 0; i < args.length; i++) {
if (checkNum(args[i]) < runningMin) runningMin = args[i];
}
return runningMin;
}
},
floor: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to floor()');
return Math.floor(checkNum(args[0]));
}
},
ceil: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to ceil()');
return Math.ceil(checkNum(args[0]));
}
},
round: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to round()');
return Math.round(checkNum(args[0]));
}
},
sqrt: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to sqrt()');
return Math.sqrt(checkNum(args[0]));
}
},
abs: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to abs()');
return Math.abs(checkNum(args[0]));
}
},
contains: {
type: 'boolean',
fn: function(item, itemExp, args) {
if (args.length < 2) throw new Error('Not enough args to contains()');
return checkStr(args[0]).toLowerCase().includes(checkStr(args[1]).toLowerCase());
}
},
atkspdmod: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to atkSpdMod()');
switch (checkNum(args[0])) {
case 2:
return 3.1;
case 1:
return 2.5;
case 0:
return 2.05;
case -1:
return 1.5;
case -2:
return 0.83;
}
if (args[0] <= -3) return 0.51;
if (args[0] >= 3) return 4.3;
throw new Error('Invalid argument to atkSpdMod()');
}
}
};
// static type check
function staticCheck(expType, term) {
if (expType === 'any' || expType === term.type) {
return true;
}
if (expType === 'number' && term.type === 'boolean') {
return true;
}
throw new Error(`Expected ${expType}, but got ${term.type}`);
}
// expression terms
class Term {
constructor(type) {
this.type = type;
}
resolve(item, itemExt) {
throw new Error('Abstract method!');
}
}
class LiteralTerm extends Term {
constructor(type, value) {
super(type);
this.value = value;
}
resolve(item, itemExt) {
return this.value;
}
}
class BoolLitTerm extends LiteralTerm {
constructor(value) {
super('boolean', value);
}
}
class NumLitTerm extends LiteralTerm {
constructor(value) {
super('number', value);
}
}
class StrLitTerm extends LiteralTerm {
constructor(value) {
super('string', value);
}
}
class BinaryOpTerm extends Term {
constructor(type, leftType, left, rightType, right) {
super(type);
staticCheck(leftType, left);
staticCheck(rightType, right);
this.left = left;
this.right = right;
}
resolve(item, itemExt) {
return this.apply(this.left.resolve(item, itemExt), this.right.resolve(item, itemExt));
}
apply(a, b) {
throw new Error('Abstract method!');
}
}
class LogicalTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'boolean', left, 'boolean', right);
}
}
class ConjTerm extends LogicalTerm {
apply(a, b) {
return a && b;
}
}
class DisjTerm extends LogicalTerm {
apply(a, b) {
return a || b;
}
}
class EqualityTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'any', left, 'any', right);
}
apply(a, b) {
return (typeof a === 'string' && typeof b === 'string')
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
}
compare(a, b) {
throw new Error('Abstract method!');
}
}
class EqTerm extends EqualityTerm {
compare(a, b) {
return a === b;
}
}
class NeqTerm extends EqualityTerm {
compare(a, b) {
return a !== b;
}
}
class ContainsTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'string', left, 'string', right);
}
apply(a, b) {
return a.toLowerCase().includes(b.toLowerCase());
}
}
class InequalityTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'any', left, 'any', right);
}
apply(a, b) {
checkComparable(a);
checkComparable(b);
return (typeof a === 'string' && typeof b === 'string')
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
}
compare(a, b) {
throw new Error('Abstract method!');
}
}
class LeqTerm extends InequalityTerm {
compare(a, b) {
return a <= b;
}
}
class LtTerm extends InequalityTerm {
compare(a, b) {
return a < b;
}
}
class GtTerm extends InequalityTerm {
compare(a, b) {
return a > b;
}
}
class GeqTerm extends InequalityTerm {
compare(a, b) {
return a >= b;
}
}
class ArithmeticTerm extends BinaryOpTerm {
constructor(left, right) {
super('number', 'number', left, 'number', right);
}
}
class AddTerm extends ArithmeticTerm {
apply(a, b) {
return a + b;
}
}
class SubTerm extends ArithmeticTerm {
apply(a, b) {
return a - b;
}
}
class MulTerm extends ArithmeticTerm {
apply(a, b) {
return a * b;
}
}
class DivTerm extends ArithmeticTerm {
apply(a, b) {
return a / b;
}
}
class ExpTerm extends ArithmeticTerm {
apply(a, b) {
return a ** b;
}
}
class UnaryOpTerm extends Term {
constructor(type, inType, inVal) {
super(type);
staticCheck(inType, inVal);
this.inVal = inVal;
}
resolve(item, itemExt) {
return this.apply(this.inVal.resolve(item, itemExt));
}
apply(x) {
throw new Error('Abstract method!');
}
}
class NegTerm extends UnaryOpTerm {
constructor(inVal) {
super('number', 'number', inVal);
}
apply(x) {
return -x;
}
}
class InvTerm extends UnaryOpTerm {
constructor(inVal) {
super('boolean', 'boolean', inVal);
}
apply(x) {
return !x;
}
}
class FnCallTerm extends Term {
constructor(fn, argExprs) {
super(fn.type);
this.fn = fn;
this.argExprs = argExprs;
}
resolve(item, itemExt) {
const argVals = [];
for (const argExpr of this.argExprs) {
argVals.push(argExpr.resolve(item, itemExt));
}
return this.fn.fn(item, itemExt, argVals);
}
}
class PropTerm extends Term {
constructor(prop) {
super(prop.type);
this.prop = prop;
}
resolve(item, itemExt) {
return this.prop.resolve(item, itemExt);
}
}
function compareLexico(ia, keysA, ib, keysB) {
for (let i = 0; i < keysA.length; i++) { // assuming keysA and keysB are the same length
let aKey = keysA[i], bKey = keysB[i];
if (typeof aKey !== typeof bKey) throw new Error(`Incomparable types ${typeof aKey} and ${typeof bKey}`); // can this even happen?
switch (typeof aKey) {
case 'string':
aKey = aKey.toLowerCase();
bKey = bKey.toLowerCase();
if (aKey < bKey) return -1;
if (aKey > bKey) return 1;
break;
case 'number': // sort numeric stuff in reverse order
aKey = isNaN(aKey) ? 0 : aKey;
bKey = isNaN(bKey) ? 0 : bKey;
if (aKey < bKey) return 1;
if (aKey > bKey) return -1;
break;
default:
throw new Error(`Incomparable type ${typeof aKey}`);
}
}
return ib.lvl - ia.lvl;
} }

View file

@ -1,549 +0,0 @@
// dynamic type casts
function checkBool(v) {
if (typeof v !== 'boolean') throw new Error(`Expected boolean, but got ${typeof v}`);
return v;
}
function checkNum(v) {
if (typeof v === 'boolean') {
if (v) return 1;
return 0;
}
if (typeof v !== 'number') throw new Error(`Expected number, but got ${typeof v}`);
return v;
}
function checkStr(v) {
if (typeof v !== 'string') throw new Error(`Expected string, but got ${typeof v}`);
return v;
}
function checkComparable(v) {
if (typeof v === 'boolean') throw new Error('Boolean is not comparable');
return v;
}
// properties of items that can be looked up
// each entry is a function `(item, extended item) -> value`
const itemQueryProps = (function() {
const props = {};
function prop(names, type, resolve) {
if (Array.isArray(names)) {
for (name of names) {
props[name] = { type, resolve };
}
} else {
props[names] = { type, resolve };
}
}
function maxId(names, idKey) {
prop(names, 'number', (i, ie) => ie.get('maxRolls').get(idKey) || 0);
}
function minId(names, idKey) {
prop(names, 'number', (i, ie) => ie.get('minRolls').get(idKey) || 0);
}
function rangeAvg(names, getProp) {
prop(names, 'number', (i, ie) => {
const range = getProp(i, ie);
if (!range) return 0;
const ndx = range.indexOf('-');
return (parseInt(range.substring(0, ndx), 10) + parseInt(range.substring(ndx + 1), 10)) / 2;
});
}
function map(names, comps, outType, f) {
return prop(names, outType, (i, ie) => {
const args = [];
for (let k = 0; k < comps.length; k++) args.push(comps[k].resolve(i, ie));
return f.apply(null, args);
});
}
function sum(names, ...comps) {
return map(names, comps, 'number', (...summands) => {
let total = 0;
for (let i = 0; i < summands.length; i++) total += summands[i];
return total;
});
}
prop('name', 'string', (i, ie) => i.displayName || i.name);
prop('type', 'string', (i, ie) => i.type);
prop(['cat', 'category'], 'string', (i, ie) => i.category);
const tierIndices = { Normal: 0, Unique: 1, Set: 2, Rare: 3, Legendary: 4, Fabled: 5, Mythic: 6 };
prop(['rarityname', 'raritystr', 'tiername', 'tierstr'], 'string', (i, ie) => i.tier);
prop(['rarity', 'tier'], 'number', (i, ie) => tierIndices[i.tier]);
prop(['level', 'lvl', 'combatlevel', 'combatlvl'], 'number', (i, ie) => i.lvl);
prop(['strmin', 'strreq'], 'number', (i, ie) => i.strReq);
prop(['dexmin', 'dexreq'], 'number', (i, ie) => i.dexReq);
prop(['intmin', 'intreq'], 'number', (i, ie) => i.intReq);
prop(['defmin', 'defreq'], 'number', (i, ie) => i.defReq);
prop(['agimin', 'agireq'], 'number', (i, ie) => i.agiReq);
sum(['summin', 'sumreq', 'totalmin', 'totalreq'], props.strmin, props.dexmin, props.intmin, props.defmin, props.agimin);
prop('str', 'number', (i, ie) => i.str);
prop('dex', 'number', (i, ie) => i.dex);
prop('int', 'number', (i, ie) => i.int);
prop('def', 'number', (i, ie) => i.def);
prop('agi', 'number', (i, ie) => i.agi);
sum(['skillpoints', 'skillpts', 'attributes', 'attrs'], props.str, props.dex, props.int, props.def, props.agi);
rangeAvg(['neutraldmg', 'neutraldam', 'ndmg', 'ndam'], (i, ie) => i.nDam);
rangeAvg(['earthdmg', 'earthdam', 'edmg', 'edam'], (i, ie) => i.eDam);
rangeAvg(['thunderdmg', 'thunderdam', 'tdmg', 'tdam'], (i, ie) => i.tDam);
rangeAvg(['waterdmg', 'waterdam', 'wdmg', 'wdam'], (i, ie) => i.wDam);
rangeAvg(['firedmg', 'firedam', 'fdmg', 'fdam'], (i, ie) => i.fDam);
rangeAvg(['airdmg', 'airdam', 'admg', 'adam'], (i, ie) => i.aDam);
sum(['sumdmg', 'sumdam', 'totaldmg', 'totaldam'], props.ndam, props.edam, props.tdam, props.wdam, props.fdam, props.adam);
maxId(['earthdmg%', 'earthdam%', 'edmg%', 'edam%', 'edampct'], 'eDamPct');
maxId(['thunderdmg%', 'thunderdam%', 'tdmg%', 'tdam%', 'tdampct'], 'tDamPct');
maxId(['waterdmg%', 'waterdam%', 'wdmg%', 'wdam%', 'wdampct'], 'wDamPct');
maxId(['firedmg%', 'firedam%', 'fdmg%', 'fdam%', 'fdampct'], 'fDamPct');
maxId(['airdmg%', 'airdam%', 'admg%', 'adam%', 'adampct'], 'aDamPct');
sum(['sumdmg%', 'sumdam%', 'totaldmg%', 'totaldam%', 'sumdampct', 'totaldampct'], props.edampct, props.tdampct, props.wdampct, props.fdampct, props.adampct);
maxId(['mainatkdmg', 'mainatkdam', 'mainatkdmg%', 'mainatkdam%', 'meleedmg', 'meleedam', 'meleedmg%', 'meleedam%', 'mdpct'], 'mdPct');
maxId(['mainatkrawdmg', 'mainatkrawdam', 'mainatkneutraldmg', 'mainatkneutraldam', 'meleerawdmg', 'meleerawdam', 'meleeneutraldmg', 'meleeneutraldam', 'mdraw'], 'mdRaw');
maxId(['spelldmg', 'spelldam', 'spelldmg%', 'spelldam%', 'sdpct'], 'sdPct');
maxId(['spellrawdmg', 'spellrawdam', 'spellneutraldmg', 'spellneutraldam', 'sdraw'], 'sdRaw');
const atkSpdIndices = { SUPER_SLOW: -3, VERY_SLOW: -2, SLOW: -1, NORMAL: 0, FAST: 1, VERY_FAST: 2, SUPER_FAST: 3 };
prop(['attackspeed', 'atkspd'], 'string', (i, ie) => i.atkSpd ? atkSpdIndices[i.atkSpd] : 0);
maxId(['bonusattackspeed', 'bonusatkspd', 'attackspeedid', 'atkspdid', 'atktier'], 'atkTier');
sum(['sumattackspeed', 'totalattackspeed', 'sumatkspd', 'totalatkspd', 'sumatktier', 'totalatktier'], props.atkspd, props.atktier);
prop(['earthdef', 'edef'], 'number', (i, ie) => i.eDef || 0);
prop(['thunderdef', 'tdef'], 'number', (i, ie) => i.tDef || 0);
prop(['waterdef', 'wdef'], 'number', (i, ie) => i.wDef || 0);
prop(['firedef', 'fdef'], 'number', (i, ie) => i.fDef || 0);
prop(['airdef', 'adef'], 'number', (i, ie) => i.aDef || 0);
sum(['sumdef', 'totaldef'], props.edef, props.tdef, props.wdef, props.fdef, props.adef);
maxId(['earthdef%', 'edef%', 'edefpct'], 'eDefPct');
maxId(['thunderdef%', 'tdef%', 'tdefpct'], 'tDefPct');
maxId(['waterdef%', 'wdef%', 'wdefpct'], 'wDefPct');
maxId(['firedef%', 'fdef%', 'fdefpct'], 'fDefPct');
maxId(['airdef%', 'adef%', 'adefpct'], 'aDefPct');
sum(['sumdef%', 'totaldef%', 'sumdefpct', 'totaldefpct'], props.edefpct, props.tdefpct, props.wdefpct, props.fdefpct, props.adefpct);
prop(['health', 'hp'], 'number', (i, ie) => i.hp || 0);
maxId(['bonushealth', 'healthid', 'bonushp', 'hpid', 'hpbonus'], 'hpBonus');
sum(['sumhealth', 'sumhp', 'totalhealth', 'totalhp'], props.hp, props.hpid);
maxId(['hpregen', 'hpr', 'hr', 'hprraw'], 'hprRaw');
maxId(['hpregen%', 'hpr%', 'hr%', 'hprpct'], 'hprPct');
maxId(['lifesteal', 'ls'], 'ls');
maxId(['manaregen', 'mr'], 'mr');
maxId(['manasteal', 'ms'], 'ms');
maxId(['walkspeed', 'movespeed', 'ws', 'spd'], 'spd');
maxId('sprint', 'sprint');
maxId(['sprintregen', 'sprintreg'], 'sprintReg');
maxId(['jumpheight', 'jh'], 'jh');
maxId(['spellcost1', 'rawspellcost1', 'spcost1', 'spraw1'], 'spRaw1');
maxId(['spellcost1%', 'spcost1%', 'sppct1'], 'spPct1');
maxId(['spellcost2', 'rawspellcost2', 'spcost2', 'spraw2'], 'spRaw2');
maxId(['spellcost2%', 'spcost2%', 'sppct2'], 'spPct2');
maxId(['spellcost3', 'rawspellcost3', 'spcost3', 'spraw3'], 'spRaw3');
maxId(['spellcost3%', 'spcost3%', 'sppct3'], 'spPct3');
maxId(['spellcost4', 'rawspellcost4', 'spcost4', 'spraw4'], 'spRaw4');
maxId(['spellcost4%', 'spcost4%', 'sppct4'], 'spPct4');
sum(['sumspellcost', 'totalspellcost', 'sumrawspellcost', 'totalrawspellcost', 'sumspcost', 'totalspcost', 'sumspraw', 'totalspraw'], props.spraw1, props.spraw2, props.spraw3, props.spraw4);
sum(['sumspellcost%', 'totalspellcost%', 'sumspcost%', 'totalspcost%', 'sumsppct', 'totalsppct'], props.sppct1, props.sppct2, props.sppct3, props.sppct4);
maxId(['exploding', 'expl', 'expd'], 'expd');
maxId('poison', 'poison');
maxId('thorns', 'thorns');
maxId(['reflection', 'refl', 'ref'], 'ref');
maxId(['soulpointregen', 'spr', 'spregen'], 'spRegen');
maxId(['lootbonus', 'lb'], 'lb');
maxId(['xpbonus', 'xpb', 'xb'], 'xpb');
maxId(['stealing', 'esteal'], 'eSteal');
prop(['powderslots', 'powders', 'slots', 'sockets'], 'number', (i, ie) => i.slots || 0);
return props;
})();
// functions that can be called in query expressions
const itemQueryFuncs = {
max: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to max()');
let runningMax = -Infinity;
for (let i = 0; i < args.length; i++) {
if (checkNum(args[i]) > runningMax) runningMax = args[i];
}
return runningMax;
}
},
min: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to min()');
let runningMin = Infinity;
for (let i = 0; i < args.length; i++) {
if (checkNum(args[i]) < runningMin) runningMin = args[i];
}
return runningMin;
}
},
floor: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to floor()');
return Math.floor(checkNum(args[0]));
}
},
ceil: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to ceil()');
return Math.ceil(checkNum(args[0]));
}
},
round: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to round()');
return Math.round(checkNum(args[0]));
}
},
sqrt: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to sqrt()');
return Math.sqrt(checkNum(args[0]));
}
},
abs: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to abs()');
return Math.abs(checkNum(args[0]));
}
},
contains: {
type: 'boolean',
fn: function(item, itemExp, args) {
if (args.length < 2) throw new Error('Not enough args to contains()');
return checkStr(args[0]).toLowerCase().includes(checkStr(args[1]).toLowerCase());
}
},
atkspdmod: {
type: 'number',
fn: function(item, itemExp, args) {
if (args.length < 1) throw new Error('Not enough args to atkSpdMod()');
switch (checkNum(args[0])) {
case 2:
return 3.1;
case 1:
return 2.5;
case 0:
return 2.05;
case -1:
return 1.5;
case -2:
return 0.83;
}
if (args[0] <= -3) return 0.51;
if (args[0] >= 3) return 4.3;
throw new Error('Invalid argument to atkSpdMod()');
}
}
};
// static type check
function staticCheck(expType, term) {
if (expType === 'any' || expType === term.type) {
return true;
}
if (expType === 'number' && term.type === 'boolean') {
return true;
}
throw new Error(`Expected ${expType}, but got ${term.type}`);
}
// expression terms
class Term {
constructor(type) {
this.type = type;
}
resolve(item, itemExt) {
throw new Error('Abstract method!');
}
}
class LiteralTerm extends Term {
constructor(type, value) {
super(type);
this.value = value;
}
resolve(item, itemExt) {
return this.value;
}
}
class BoolLitTerm extends LiteralTerm {
constructor(value) {
super('boolean', value);
}
}
class NumLitTerm extends LiteralTerm {
constructor(value) {
super('number', value);
}
}
class StrLitTerm extends LiteralTerm {
constructor(value) {
super('string', value);
}
}
class BinaryOpTerm extends Term {
constructor(type, leftType, left, rightType, right) {
super(type);
staticCheck(leftType, left);
staticCheck(rightType, right);
this.left = left;
this.right = right;
}
resolve(item, itemExt) {
return this.apply(this.left.resolve(item, itemExt), this.right.resolve(item, itemExt));
}
apply(a, b) {
throw new Error('Abstract method!');
}
}
class LogicalTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'boolean', left, 'boolean', right);
}
}
class ConjTerm extends LogicalTerm {
apply(a, b) {
return a && b;
}
}
class DisjTerm extends LogicalTerm {
apply(a, b) {
return a || b;
}
}
class EqualityTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'any', left, 'any', right);
}
apply(a, b) {
return (typeof a === 'string' && typeof b === 'string')
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
}
compare(a, b) {
throw new Error('Abstract method!');
}
}
class EqTerm extends EqualityTerm {
compare(a, b) {
return a === b;
}
}
class NeqTerm extends EqualityTerm {
compare(a, b) {
return a !== b;
}
}
class ContainsTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'string', left, 'string', right);
}
apply(a, b) {
return a.toLowerCase().includes(b.toLowerCase());
}
}
class InequalityTerm extends BinaryOpTerm {
constructor(left, right) {
super('boolean', 'any', left, 'any', right);
}
apply(a, b) {
checkComparable(a);
checkComparable(b);
return (typeof a === 'string' && typeof b === 'string')
? this.compare(a.toLowerCase(), b.toLowerCase()) : this.compare(a, b);
}
compare(a, b) {
throw new Error('Abstract method!');
}
}
class LeqTerm extends InequalityTerm {
compare(a, b) {
return a <= b;
}
}
class LtTerm extends InequalityTerm {
compare(a, b) {
return a < b;
}
}
class GtTerm extends InequalityTerm {
compare(a, b) {
return a > b;
}
}
class GeqTerm extends InequalityTerm {
compare(a, b) {
return a >= b;
}
}
class ArithmeticTerm extends BinaryOpTerm {
constructor(left, right) {
super('number', 'number', left, 'number', right);
}
}
class AddTerm extends ArithmeticTerm {
apply(a, b) {
return a + b;
}
}
class SubTerm extends ArithmeticTerm {
apply(a, b) {
return a - b;
}
}
class MulTerm extends ArithmeticTerm {
apply(a, b) {
return a * b;
}
}
class DivTerm extends ArithmeticTerm {
apply(a, b) {
return a / b;
}
}
class ExpTerm extends ArithmeticTerm {
apply(a, b) {
return a ** b;
}
}
class UnaryOpTerm extends Term {
constructor(type, inType, inVal) {
super(type);
staticCheck(inType, inVal);
this.inVal = inVal;
}
resolve(item, itemExt) {
return this.apply(this.inVal.resolve(item, itemExt));
}
apply(x) {
throw new Error('Abstract method!');
}
}
class NegTerm extends UnaryOpTerm {
constructor(inVal) {
super('number', 'number', inVal);
}
apply(x) {
return -x;
}
}
class InvTerm extends UnaryOpTerm {
constructor(inVal) {
super('boolean', 'boolean', inVal);
}
apply(x) {
return !x;
}
}
class FnCallTerm extends Term {
constructor(fn, argExprs) {
super(fn.type);
this.fn = fn;
this.argExprs = argExprs;
}
resolve(item, itemExt) {
const argVals = [];
for (const argExpr of this.argExprs) {
argVals.push(argExpr.resolve(item, itemExt));
}
return this.fn.fn(item, itemExt, argVals);
}
}
class PropTerm extends Term {
constructor(prop) {
super(prop.type);
this.prop = prop;
}
resolve(item, itemExt) {
return this.prop.resolve(item, itemExt);
}
}
function compareLexico(ia, keysA, ib, keysB) {
for (let i = 0; i < keysA.length; i++) { // assuming keysA and keysB are the same length
let aKey = keysA[i], bKey = keysB[i];
if (typeof aKey !== typeof bKey) throw new Error(`Incomparable types ${typeof aKey} and ${typeof bKey}`); // can this even happen?
switch (typeof aKey) {
case 'string':
aKey = aKey.toLowerCase();
bKey = bKey.toLowerCase();
if (aKey < bKey) return -1;
if (aKey > bKey) return 1;
break;
case 'number': // sort numeric stuff in reverse order
aKey = isNaN(aKey) ? 0 : aKey;
bKey = isNaN(bKey) ? 0 : bKey;
if (aKey < bKey) return 1;
if (aKey > bKey) return -1;
break;
default:
throw new Error(`Incomparable type ${typeof aKey}`);
}
}
return ib.lvl - ia.lvl;
}

View file

@ -187,8 +187,17 @@ function calculate_skillpoints(equipment, weapon) {
permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item))); permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item)));
} }
} }
// skip root. if (sccs.length === 1) {
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []); // Only crafteds. Just do end check (check req first, then apply sp after)
const total = check_end(best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice());
final_skillpoints = best_skillpoints.slice();
best_total = total;
best_activeSetCounts = best_activeSetCounts;
best = [];
} else {
// skip root.
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []);
}
// add extra sp bonus // add extra sp bonus
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts); apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);

View file

@ -1,34 +0,0 @@
//which icons to use
let window_storage = window.localStorage;
console.log(window_storage);
icon_state_stored = window_storage.getItem("newicons");
newIcons = true;
if (icon_state_stored === "false") {toggleIcons()}
/** Toggle icons on the ENTIRE page.
*
*/
function toggleIcons() {
newIcons = !newIcons;
let imgs = document.getElementsByTagName("IMG");
let favicon = document.querySelector("link[rel~='icon']");
if (newIcons) { //switch to new
favicon.href = favicon.href.replace("media/icons/old","media/icons/new");
for (const img of imgs) {
if (img.src.includes("media/icons/old")) {img.src = img.src.replace("media/icons/old","media/icons/new");}
if (img.src.includes("media/items/old")) {img.src = img.src.replace("media/items/old","media/items/new");}
}
//toggleiconbutton.textContent = "Use Old Icons";
window_storage.setItem("newicons","true");
} else { //switch to old
favicon.href = favicon.href.replace("media/icons/new","media/icons/old");
for (const img of imgs) {
if (img.src.includes("media/icons/new")) {img.src = img.src.replace("media/icons/new","media/icons/old");}
if (img.src.includes("media/items/new")) {img.src = img.src.replace("media/items/new","media/items/old");}
}
//toggleiconbutton.textContent = "Use New Icons";
window_storage.setItem("newicons","false");
}
}

View file

@ -1,264 +0,0 @@
let itemCategories = [ "armor", "accessory", "weapon" ];
const sq2_translate_mappings = {
//"Name": "name",
//"Display Name": "displayName",
//"tier"Tier": ",
//"Set": "set",
"Powder Slots": "slots",
//"Type": "type",
//"armorType", (deleted)
//"color", (deleted)
//"lore", (deleted)
//"material", (deleted)
"Drop type": "drop",
"Quest requirement": "quest",
"Restriction": "restrict",
//"Base Neutral Damage": "nDam",
//"Base Fire Damage": "fDam",
//"Base Water Damage": "wDam",
//"Base Air Damage": "aDam",
//"Base Thunder Damage": "tDam",
//"Base Earth Damage": "eDam",
//"Base Attack Speed": "atkSpd",
"Health": "hp",
"Raw Fire Defense": "fDef",
"Raw Water Defense": "wDef",
"Raw Air Defense": "aDef",
"Raw Thunder Defense": "tDef",
"Raw Earth Defense": "eDef",
"Combat Level": "lvl",
//"Class Requirement": "classReq",
"Req Strength": "strReq",
"Req Dexterity": "dexReq",
"Req Intelligence": "intReq",
"Req Agility": "agiReq",
"Req Defense": "defReq",
"% Health Regen": "hprPct",
"Mana Regen": "mr",
"% Spell Damage": "sdPct",
"% Melee Damage": "mdPct",
"Life Steal": "ls",
"Mana Steal": "ms",
"XP Bonus": "xpb",
"Loot Bonus": "lb",
"Reflection": "ref",
"Strength": "str",
"Dexterity": "dex",
"Intelligence": "int",
"Agility": "agi",
"Defense": "def",
"Thorns": "thorns",
"Exploding": "expd",
"Walk Speed": "spd",
"Attack Speed Bonus": "atkTier",
"Poison": "poison",
"Health Bonus": "hpBonus",
"Soul Point Regen": "spRegen",
"Stealing": "eSteal",
"Raw Health Regen": "hprRaw",
"Raw Spell": "sdRaw",
"Raw Melee": "mdRaw",
"% Fire Damage": "fDamPct",
"% Water Damage": "wDamPct",
"% Air Damage": "aDamPct",
"% Thunder Damage": "tDamPct",
"% Earth Damage": "eDamPct",
"% Fire Defense": "fDefPct",
"% Water Defense": "wDefPct",
"% Air Defense": "aDefPct",
"% Thunder Defense": "tDefPct",
"% Earth Defense": "eDefPct",
"Fixed IDs": "fixID",
"Custom Skin": "skin",
//"Item Category": "category",
"1st Spell Cost %": "spPct1",
"1st Spell Cost Raw": "spRaw1",
"2nd Spell Cost %": "spPct2",
"2nd Spell Cost Raw": "spRaw2",
"3rd Spell Cost %": "spPct3",
"3rd Spell Cost Raw": "spRaw3",
"4th Spell Cost %": "spPct4",
"4th Spell Cost Raw": "spRaw4",
"Rainbow Spell Damage": "rainbowRaw",
"Sprint": "sprint",
"Sprint Regen": "sprintReg",
"Jump Height": "jh",
"Loot Quality": "lq",
"Gather XP Bonus": "gXp",
"Gather Speed Bonus": "gSpd",
};
const sq2_special_mappings = {
"Sum (skill points)": new SumQuery(["str", "dex", "int", "def", "agi"]),
"Sum (Mana Sustain)": new SumQuery(["mr", "ms"]),
"Sum (Life Sustain)": new SumQuery(["hpr", "ls"]),
"Sum (Health + Health Bonus)": new SumQuery(["hp", "hpBonus"]),
"No Strength Req": new NegateQuery("strReq"),
"No Dexterity Req": new NegateQuery("dexReq"),
"No Intelligence Req": new NegateQuery("intReq"),
"No Agility Req": new NegateQuery("agiReq"),
"No Defense Req": new NegateQuery("defReq"),
};
let sq2ItemFilters = []
for (let x in sq2_translate_mappings) {
sq2ItemFilters.push(x);
}
for (let x in sq2_special_mappings) {
sq2ItemFilters.push(x);
}
function applyQuery(items, query) {
return items.filter(query.filter, query).sort(query.compare);
}
function displayItems(items_copy) {
let items_parent = document.getElementById("search-results");
for (let i in items_copy) {
if (i > 200) {break;}
let item = items_copy[i];
let box = document.createElement("div");
box.classList.add("col-lg-3", "col-sm-6", "p-2");
box.id = "item"+i;
box.addEventListener("dblclick", function() {set_item(item);});
let bckgrdbox = document.createElement("div");
bckgrdbox.classList.add("dark-7", "rounded", "px-2", "col-auto");
box.appendChild(bckgrdbox);
bckgrdbox.id = "item"+i+"b";
items_parent.appendChild(box);
item.set("powders", []);
if (item.get("category") == "weapon") {
apply_weapon_powders(item);
}
displayExpandedItem(item, bckgrdbox.id, true);
}
}
let items_expanded;
function doItemSearch() {
// window.scrollTo(0, 0);
let queries = [];
queries.push(new NameQuery(document.getElementById("item-name-choice").value.trim()));
let categoryOrType = document.getElementById("item-category-choice").value;
if (itemTypes.includes(categoryOrType)) {
queries.push(new IdMatchQuery("type", categoryOrType));
}
else if (itemCategories.includes(categoryOrType)) {
queries.push(new IdMatchQuery("category", categoryOrType));
}
let rarity = document.getElementById("item-rarity-choice").value;
if (rarity) {
if (rarity === "ANY") {
}
else {
queries.push(new IdMatchQuery("tier", rarity));
}
}
let level_dat = document.getElementById("item-level-choice").value ? document.getElementById("item-level-choice").value.split("-") : [1, 106];
queries.push(new LevelRangeQuery(parseInt(level_dat[0]), parseInt(level_dat[1])));
for (let i = 1; i <= 4; ++i) {
let raw_dat = document.getElementById("filter"+i+"-choice").value;
let filter_dat = sq2_translate_mappings[raw_dat];
if (filter_dat !== undefined) {
queries.push(new IdQuery(filter_dat));
continue;
}
filter_dat = sq2_special_mappings[raw_dat];
if (filter_dat !== undefined) {
queries.push(filter_dat);
continue;
}
}
let items_copy = items_expanded.slice();
document.getElementById("search-results").textContent = "";
for (const query of queries) {
console.log(items_copy.length);
console.log(query, query.filter);
items_copy = applyQuery(items_copy, query);
console.log(items_copy.length);
}
document.getElementById("summary").textContent = items_copy.length + " results:"
displayItems(items_copy);
}
function resetItemSearch() {
resetFields = ["item-name-choice", "item-category-choice", "item-rarity-choice", "item-level-choice", "filter1-choice", "filter2-choice", "filter3-choice", "filter4-choice"]
for (const field of resetFields) {
document.getElementById(field).value = "";
}
}
function init_items() {
items_expanded = items.filter( (i) => !("remapID" in i) ).map( (i) => expandItem(i) );
//init dropdowns
let filterInputs = new Map([["item-category", ["ALL", "armor", "helmet", "chestplate", "leggings", "boots", "accessory", "ring", "bracelet", "necklace", "weapon", "wand", "spear", "bow", "dagger", "relik"]],
["item-rarity", ["ANY", "Normal", "Unique", "Set", "Rare", "Legendary", "Fabled", "Mythic", "Sane"]],
["filter1", sq2ItemFilters],
["filter2", sq2ItemFilters],
["filter3", sq2ItemFilters],
["filter4", sq2ItemFilters]]);
for (const [field, data] of filterInputs) {
let field_choice = document.getElementById(field+"-choice");
// show dropdown on click
field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));};
filterInputs.set(field, new autoComplete({
data: {
src: data,
},
threshold: 0,
selector: "#"+ field +"-choice",
wrapper: false,
resultsList: {
maxResults: 100,
tabSelect: true,
noResults: true,
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
element: (list, data) => {
let position = document.getElementById(field+'-choice').getBoundingClientRect();
list.style.top = position.bottom + window.scrollY +"px";
list.style.left = position.x+"px";
list.style.width = position.width+"px";
list.style.maxHeight = position.height * 4 +"px";
if (!data.results.length) {
message = document.createElement('li');
message.classList.add('scaled-font');
message.textContent = "No results found!";
list.prepend(message);
};
},
},
resultItem: {
class: "scaled-font search-item",
selected: "dark-5",
},
events: {
input: {
selection: (event) => {
if (event.detail.selection.value) {
event.target.value = event.detail.selection.value;
};
},
},
}
}));
};
}
(async function() {
await Promise.resolve(load_init());
init_items();
})();

View file

@ -1,6 +1,14 @@
let getUrl = window.location; let getUrl = window.location;
const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1]; const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
// huge regex :doom:
// replace with navigator.userAgentData.mobile once it has wider support
const isMobile = function() {
let check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
return check;
}(); // runs immediately, so mobileCheck is a boolean not a function
const zip2 = (a, b) => a.map((k, i) => [k, b[i]]); const zip2 = (a, b) => a.map((k, i) => [k, b[i]]);
const zip3 = (a, b, c) => a.map((k, i) => [k, b[i], c[i]]); const zip3 = (a, b, c) => a.map((k, i) => [k, b[i], c[i]]);
@ -632,16 +640,6 @@ function matchType(object, target) {
return object; return object;
} }
/**
* Add multiple classes to a html element
*/
function addClasses(elem, classes) {
for (let _class of classes) {
elem.classList.add(_class);
}
return elem;
}
/** A utility function that reloads the page forcefully. /** A utility function that reloads the page forcefully.
* *
*/ */
@ -926,6 +924,17 @@ function make_elem(type, classlist = [], args = {}) {
const ret_elem = document.createElement(type); const ret_elem = document.createElement(type);
ret_elem.classList.add(...classlist); ret_elem.classList.add(...classlist);
for (const i in args) { for (const i in args) {
if (i === 'style') {
const style_obj = args[i];
if (typeof style_obj === 'string' || style_obj instanceof String) {
ret_elem.style = style_obj;
continue;
}
for (const k in style_obj) {
ret_elem.style[k] = style_obj[k];
}
continue;
}
ret_elem[i] = args[i]; ret_elem[i] = args[i];
} }
return ret_elem; return ret_elem;
@ -994,3 +1003,40 @@ function make_SCC_graph(root_node, nodes) {
} }
return sccs; return sccs;
} }
// Toggles display of a certain element, given the ID.
function toggle_tab(tab) {
let elem = document.getElementById(tab);
if (elem.style.display == "none") {
elem.style.display = "";
} else {
elem.style.display = "none";
}
}
// Toggle display of a certain tab, in a group of tabs, given the target tab ID, and a list of associated tabs.
// Also sets visual display of an element with ID of target + "-btn" to selected.
function show_tab(target, tabs) {
//hide all tabs, then show the tab of the div clicked and highlight the correct button
for (const i in tabs) {
document.getElementById(tabs[i]).style.display = "none";
document.getElementById(tabs[i] + "-btn").classList.remove("selected-btn");
}
document.getElementById(target).style.display = "";
document.getElementById(target + "-btn").classList.add("selected-btn");
}
// mobile navbar appearance control
let scrollPos = 0
if (screen.width < 992) {
document.addEventListener('scroll', (e) => {
if (document.documentElement.scrollTop - scrollPos > 20) {
document.getElementById("mobile-navbar").style.display = "none";
document.getElementById("mobile-navbar-dropdown").style.display = "none";
} else if (document.documentElement.scrollTop - scrollPos < -50 || scrollPos < 70) {
document.getElementById("mobile-navbar").style.display = "";
}
scrollPos = document.documentElement.scrollTop;
});
}

View file

@ -45,8 +45,51 @@
<hr/> <hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a> <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>
<div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
</div>
<div class = "container row py-5 vh-100 mx-0 mx-lg-auto scaled-font"> <div class = "container row py-5 vh-100 mx-0 mx-lg-auto scaled-font mt-lg-2" style="margin-top: 6vh;">
<div id = "mapdiv" class = "col-lg-8 col-sm-12 rounded border border-light border-3"> <div id = "mapdiv" class = "col-lg-8 col-sm-12 rounded border border-light border-3">
</div> </div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

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