Shaman atree + schema augmentation (#196)

* Beginnings of shaman atree

* Fix aura parents

* Day's work (ft. frozenearth and endistic)

* More incremental work -- finish another two pages i think, or one

* One more page to go on shaman atree

* Fix missing archetype on overseer and fix regeneration to be not 100% heal per tick

* Just missing 4 abils at the bottom hopefully

* Fix typos in display

* Fix archetype req for chant of the fanatic

* Shaman atree complete

except fluid healing doesn't work yet

* Add comments, fix some misc. formatting related stuffs

* Misc. patch

Fix rebound interaction
Fix translations for newly added target parts
Fix tree misc. connection/archetype missing bugs

Co-authored-by: hppeng <hppeng>
This commit is contained in:
hppeng-wynn 2022-08-11 03:13:53 -07:00 committed by GitHub
parent 755def77f3
commit fb1077de7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 2671 additions and 533 deletions

View file

@ -36,20 +36,71 @@
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
<div class="container-fluid me-4" style="max-width: 95%; display: none">
<div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0" style="display: none">
<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. -->
<div class = "row scaled-font mx-auto" id = "discord-banner-dev">
<div class = "col text-center item-title">Join the <a class = "link" href = "https://discord.gg/CGavnAnerv" target = "_blank">discord</a> today to suggest new features, submit bug reports, and hangout/talk to devs!</div>
</div>
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3">
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3 gx-0">
<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="row row-cols-1 row-cols-xl-2 rounded gy-4 gx-5 justify-content-center">
<div class="col-auto rounded order-xl-0 order-0">
<div class="row h-100 dark-shadow dark-6 rounded" id="helmet-dropdown">
<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>
<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-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -73,10 +124,10 @@
</div>
</div>
</div>
<div class="col-auto order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded" id="ring1-dropdown">
<div class="col-auto order-xl-0 order-1 my-0">
<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">
<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 class="col-3">
<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"/>
</div>
<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 class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -128,9 +178,9 @@
</div>
</div>
<div class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -154,9 +204,9 @@
</div>
</div>
<div class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -181,9 +231,9 @@
</div>
</div>
<div class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -208,9 +258,9 @@
</div>
</div>
<div class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -235,9 +285,9 @@
</div>
</div>
<div class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -261,9 +311,9 @@
</div>
</div>
<div class="col-auto 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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -287,29 +337,25 @@
</div>
</div>
</div>
<div class="col-auto order-xl-0 order-1">
<div class="row h-100 dark-shadow dark-6 rounded">
<div class="col-auto order-xl-0 order-1 level-input">
<div class="row h-100 px-1">
<div class="col">
<div class="row align-items-center">
<div class="col-3 text-nowrap fw-bold scaled-font">
<div class="row align-items-center justify-content-left">
<div class="col-auto text-nowrap fw-bold scaled-font">
Level:
</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"/>
</div>
<div class="col-auto px-1 text-nowrap scaled-font">
<button class="button fw-bold text-light dark-5 scaled-font rounded" id="reset-button" onclick="resetFields()">Reset</button>
</div>
</div>
<div class="row align-items-center justify-content-center my-1">
<div class="row align-items-center">
<div class="col-auto px-1 text-nowrap scaled-font">
<button class="border-dark text-light dark-5 scaled-font rounded" id=copy-button onclick="copyBuild()">Copy short</button>
</div>
<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 class="col-auto px-1 scaled-font">
<button class="button py-0 fw-bold text-light dark-5 scaled-font rounded" id="reset-button" onclick="resetFields()">Reset</button>
</div>
</div>
<div class="row align-items-left justify-content-left my-1">
<button class="col-auto mx-1 px-1 py-0 border-info text-light dark-5 scaled-font rounded fake-button"
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"
id=share-button onclick="shareBuild(player_build)">Copy for sharing</button>
</div>
</div>
</div>
@ -390,12 +436,9 @@
</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 class="col text-center">
<div class="col text-center py-1">
<div id="summary-box"></div>
<div id="err-box"></div>
<div id="stack-box"></div>
@ -407,8 +450,6 @@
</div>
</div>
</div>
<div class="col">
<div class="row row-cols-1 gy-4">
<div class="col mb-1 text-center scaled-font dark-5 rounded dark-shadow">
<div class="row row-cols-1 justify-content-center">
@ -416,9 +457,6 @@
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>
@ -446,19 +484,19 @@
<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')">
<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-tab" class="col tDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('dex')">
<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-tab" class="col wDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('int')">
<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-tab" class="col fDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('def')">
<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-tab" class="col aDam dark-4u fake-button elem-boost" onclick="toggle_boost_tab('agi')">
<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>
@ -589,6 +627,10 @@
</div>
</div>
</div>
</div>
<div class="row row-cols-1 d-lg-flex" id="adjust-id" style="display: none;">
<div class="col">
<div class="row row-cols-1 gy-3">
<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="col fw-bold dark-4 rounded-top">
@ -620,7 +662,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -644,7 +686,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -668,7 +710,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -692,7 +734,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -716,7 +758,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -740,7 +782,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -764,7 +806,7 @@
<div class="col-auto rounded">
<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">
<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 class="col-3">
<div class="row row-cols-1 h-100 align-items-center">
@ -789,7 +831,7 @@
</div>
<div class = "col dark-6 rounded-bottom my-3 my-xl-1" id = "atree-dropdown" style = "display:none;">
<div class="row row-cols-1 row-cols-xl-2">
<div class="col border border-semi-light rounded dark-9 hide-scroll" id="atree-ui" style="height: 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 class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs">
@ -1131,24 +1173,20 @@
</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="col rounded-top">
<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')">
Offense
<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'])">
Detailed
</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')">
Defense
</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 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'])">
Summary
</div>
</div>
</div>
<div style="display: none;" id="offensive-stats" class="col text-nowrap"></div>
<div style="display: none;" id="defensive-stats" class="col text-nowrap"></div>
<div id="overall-stats" class="col text-nowrap"></div>
<div style="display: none;" id="detailed-stats" class="col text-nowrap"></div>
<div id="summary-stats" class="col text-nowrap"></div>
</div>
</div>
<div class="col-xl-3 mb-3 px-0">
@ -1263,8 +1301,7 @@
<script type="text/javascript" src="../js/build_utils.js"></script>
<script type="text/javascript" src="../js/computation_graph.js"></script>
<script type="text/javascript">COMPUTE_GRAPH_DEBUG=true;</script>
<!-- <script type="text/javascript" src="../js/icons.js"></script> -->
<script type="text/javascript" src="../js/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/skillpoints.js"></script>
<script type="text/javascript" src="../js/damage_calc.js"></script>

File diff suppressed because one or more lines are too long

View file

@ -463,11 +463,8 @@
<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 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%)

View file

@ -50,7 +50,10 @@ add_spell_prop: {
cost: Optional[int] // change to spellcost. If the spell is not spell 1-4, this must be left empty.
multipliers: Optional[array[float, 6]] // Additive changes to spellmult (for damage spell)
power: Optional[float] // Additive change to healing power (for heal spell)
hits: Optional[Map[str, float]] // Additive changes to hits (for total entry)
hits: Optional[Map[str, Union[str, float]]] // Additive changes to hits (for total entry)
// Can either be a raw value number, or a reference
// of the format <ability_id>.propname
display: Optional[str] // Optional change to the displayed entry. Replaces old
}
@ -191,8 +194,8 @@ const atree_node = new (class extends ComputeNode {
atree_topo_sort.push(node);
}
}
console.log("Approximate topological order ability tree:");
console.log(atree_topo_sort);
//console.log("Approximate topological order ability tree:");
//console.log(atree_topo_sort);
return atree_topo_sort;
}
})();
@ -285,7 +288,10 @@ const atree_merge = new (class extends ComputeNode {
base_abil.subparts.push(abil.id);
base_abil.effects = base_abil.effects.concat(abil.effects);
for (let propname in abil.properties) {
base_abil[propname] = abil[propname];
if (propname in base_abil.properties) {
base_abil.properties[propname] += abil.properties[propname];
}
else { base_abil.properties[propname] = abil.properties[propname]; }
}
}
// do nothing otherwise.
@ -448,6 +454,207 @@ const atree_validate = new (class extends ComputeNode {
}
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
/**
* Make interactive elements (sliders, buttons)
*
* Signature: AbilityActiveUINode(atree-merged: MergedATree) => Map<str, slider_info>
*
* ElemState: {
* value: int // value for sliders; 0-1 for toggles
* }
*/
const atree_make_interactives = new (class extends ComputeNode {
constructor() { super('atree-make-interactives'); }
compute_func(input_map) {
const merged_abils = input_map.get('atree-merged');
const atree_order = input_map.get('atree-order');
const boost_slider_parent = document.getElementById("boost-sliders");
const boost_toggle_parent = document.getElementById("boost-toggles");
boost_slider_parent.innerHTML = "";
boost_toggle_parent.innerHTML = "";
/**
* slider_info
* label_name: str,
* max: int,
* step: int,
* id: str,
* abil: atree_node
* slider: html element
* }
*/
// Map<str, slider_info>
const slider_map = new Map();
const button_map = new Map();
// first, pull out all the sliders and toggles.
for (const [abil_id, ability] of merged_abils.entries()) {
for (const effect of ability.effects) {
if (effect['type'] === "stat_scaling" && effect['slider'] === true) {
const { slider_name, slider_behavior = 'merge', slider_max, slider_step } = effect;
if (slider_map.has(slider_name)) {
if (slider_max !== undefined) {
const slider_info = slider_map.get(slider_name);
slider_info.max += slider_max;
}
}
else if (slider_behavior === 'merge') {
slider_map.set(slider_name, {
label_name: slider_name+' ('+ability.display_name+')',
max: slider_max,
step: slider_step,
id: "ability-slider"+ability.id,
//color: effect['slider_color'] TODO: add colors to json
abil: ability
});
}
}
if (effect['type'] === "raw_stat" && effect['toggle']) {
const { toggle: toggle_name } = effect;
button_map.set(toggle_name, {
abil: ability
});
}
}
}
// next, render the sliders and toggles onto the abilities.
for (const [slider_name, slider_info] of slider_map.entries()) {
let slider_container = gen_slider_labeled(slider_info);
boost_slider_parent.appendChild(slider_container);
slider_info.slider = document.getElementById(slider_info.id);
slider_info.slider.addEventListener("change", (e) => atree_scaling.mark_dirty().update());
}
for (const [button_name, button_info] of button_map.entries()) {
let button = make_elem('button', ["button-boost", "border-0", "text-white", "dark-8u", "dark-shadow-sm", "m-1"], {
id: button_info.abil.id,
textContent: button_name
});
button.addEventListener("click", (e) => {
if (button.classList.contains("toggleOn")) {
button.classList.remove("toggleOn");
} else {
button.classList.add("toggleOn");
}
atree_scaling.mark_dirty().update()
});
button_info.button = button;
boost_toggle_parent.appendChild(button);
}
return [slider_map, button_map];
}
})().link_to(atree_node, 'atree-order').link_to(atree_merge, 'atree-merged');
/**
* Scaling stats from ability tree.
* Return StatMap of added stats,
*
* Signature: AbilityTreeScalingNode(atree-merged: MergedATree, scale-scats: StatMap,
* atree-interactive: [Map<str, slider_info>, Map<str, button_info>]) => (ATree, StatMap)
*/
const atree_scaling = new (class extends ComputeNode {
constructor() { super('atree-scaling-collector'); }
compute_func(input_map) {
const atree_merged = input_map.get('atree-merged');
const pre_scale_stats = input_map.get('scale-stats');
const [slider_map, button_map] = input_map.get('atree-interactive');
const atree_edit = new Map();
for (const [abil_id, abil] of atree_merged.entries()) {
atree_edit.set(abil_id, deepcopy(abil));
}
let ret_effects = new Map();
// Apply a stat bonus.
function apply_bonus(bonus_info, value) {
const { type, name, abil = null} = bonus_info;
if (type === 'stat') {
merge_stat(ret_effects, name, value);
} else if (type === 'prop') {
const merge_abil = atree_edit.get(abil);
merge_abil.properties[name] += value;
}
}
for (const [abil_id, abil] of atree_merged.entries()) {
if (abil.effects.length == 0) { continue; }
for (const effect of abil.effects) {
switch (effect.type) {
case 'raw_stat':
if (effect.toggle) {
const button = button_map.get(effect.toggle).button;
if (!button.classList.contains("toggleOn")) { continue; }
for (const bonus of effect.bonuses) {
apply_bonus(bonus, bonus.value);
}
} else {
for (const bonus of effect.bonuses) {
// Stat was applied earlier...
if (bonus.type === 'stat') { continue; }
apply_bonus(bonus, bonus.value);
}
}
continue;
case 'stat_scaling':
let total = 0;
const {slider = false, scaling = [0]} = effect;
let { positive = true, round = true } = effect;
if (slider) {
const slider_val = slider_map.get(effect.slider_name).slider.value;
total = parseInt(slider_val) * scaling[0];
round = false;
positive = false;
}
else {
// TODO: type: prop?
for (const [_scaling, input] of zip2(scaling, effect.inputs)) {
total += _scaling * pre_scale_stats.get(input.name);
}
}
if ('output' in effect) { // sometimes nodes will modify slider without having effect.
if (round) { total = Math.floor(round_near(total)); }
if (positive && total < 0) { total = 0; } // Normal stat scaling will not go negative.
if ('max' in effect && total > effect.max) { total = effect.max; }
if (Array.isArray(effect.output)) {
for (const output of effect.output) {
apply_bonus(output, total);
}
}
else {
apply_bonus(effect.output, total);
}
}
continue;
}
}
}
return [atree_edit, ret_effects];
}
})().link_to(atree_merge, 'atree-merged').link_to(atree_make_interactives, 'atree-interactive');
/**
* These following two nodes are just boilerplate that breaks down the scaling node.
*/
const atree_scaling_tree = new (class extends ComputeNode {
constructor() { super('atree-scaling-tree'); }
compute_func(input_map) {
const [[tree, stats]] = input_map.values();
return tree;
}
})().link_to(atree_scaling, 'atree-scaling');
const atree_scaling_stats = new (class extends ComputeNode {
constructor() { super('atree-scaling-stats'); }
compute_func(input_map) {
const [[tree, stats]] = input_map.values();
return stats;
}
})().link_to(atree_scaling, 'atree-scaling');
/**
* Render ability tree.
* Return map of id -> corresponding html element.
@ -506,7 +713,8 @@ const atree_render_active = new (class extends ComputeNode {
}
return ret_map;
}
})().link_to(atree_node, 'atree-order').link_to(atree_merge, 'atree-merged').link_to(atree_validate, 'atree-errors');
})().link_to(atree_node, 'atree-order').link_to(atree_scaling_tree, 'atree-merged').link_to(atree_validate, 'atree-errors');
/**
* Collect spells from abilities.
@ -521,13 +729,29 @@ const atree_collect_spells = new (class extends ComputeNode {
const [hard_error, errors] = input_map.get('atree-errors');
if (hard_error) { return []; }
/**
* Parse out "parametrized entries".
* Straight replace.
*
* Format: ability_id.propname
*/
function translate(v) {
if (typeof v === 'string') {
const [id_str, propname] = v.split('.');
const id = parseInt(id_str);
const ret = atree_merged.get(id).properties[propname];
return ret;
}
return v;
}
let ret_spells = new Map();
for (const [abil_id, abil] of atree_merged.entries()) {
// TODO: Possibly, make a better way for detecting "spell abilities"?
for (const effect of abil.effects) {
if (effect.type === 'replace_spell') {
// replace_spell just replaces all (defined) aspects.
const ret_spell = ret_spells.get(effect.base_spell);
let ret_spell = ret_spells.get(effect.base_spell);
if (ret_spell) {
// NOTE: do not mutate results of previous steps!
for (const key in effect) {
@ -535,7 +759,15 @@ const atree_collect_spells = new (class extends ComputeNode {
}
}
else {
ret_spells.set(effect.base_spell, deepcopy(effect));
ret_spell = deepcopy(effect);
ret_spells.set(effect.base_spell, ret_spell);
}
for (const part of ret_spell.parts) {
if ('hits' in part) {
for (const idx in part.hits) {
part.hits[idx] = translate(part.hits[idx]);
}
}
}
}
}
@ -572,7 +804,8 @@ const atree_collect_spells = new (class extends ComputeNode {
part.power += effect.power;
}
else if ('hits' in effect) {
for (const [idx, v] of Object.entries(effect.hits)) { // looks kinda similar to multipliers case... hmm... can we unify all of these three? (make healpower a list)
for (const [idx, _v] of Object.entries(effect.hits)) { // looks kinda similar to multipliers case... hmm... can we unify all of these three? (make healpower a list)
let v = translate(_v);
if (idx in part.hits) { part.hits[idx] += v; }
else { part.hits[idx] = v; }
}
@ -586,6 +819,11 @@ const atree_collect_spells = new (class extends ComputeNode {
if (!found_part && behavior === 'merge') { // add part. if behavior is merge
let spell_part = deepcopy(effect);
spell_part.name = target_part; // has some extra fields but whatever
if ('hits' in spell_part) {
for (const idx in spell_part.hits) {
spell_part.hits[idx] = translate(spell_part.hits[idx]);
}
}
ret_spell.parts.push(spell_part);
}
if ('display' in effect) {
@ -617,185 +855,16 @@ const atree_collect_spells = new (class extends ComputeNode {
}
return ret_spells;
}
})().link_to(atree_merge, 'atree-merged').link_to(atree_validate, 'atree-errors');
})().link_to(atree_scaling_tree, 'atree-merged').link_to(atree_validate, 'atree-errors');
/**
* Make interactive elements (sliders, buttons)
*
* Signature: AbilityActiveUINode(atree-merged: MergedATree) => Map<str, slider_info>
*
* ElemState: {
* value: int // value for sliders; 0-1 for toggles
* }
*/
const atree_make_interactives = new (class extends ComputeNode {
constructor() { super('atree-make-interactives'); }
compute_func(input_map) {
const merged_abils = input_map.get('atree-merged');
const atree_order = input_map.get('atree-order');
const atree_html = input_map.get('atree-elements');
document.getElementById("boost-sliders").innerHTML = "";
document.getElementById("boost-toggles").innerHTML = "";
/**
* slider_info
* label_name: str,
* max: int,
* step: int,
* id: str,
* abil: atree_node
* slider: html element
* }
*/
// Map<str, slider_info>
const slider_map = new Map();
const button_map = new Map();
// first, pull out all the sliders and toggles.
for (const [abil_id, ability] of merged_abils.entries()) {
for (const effect of ability.effects) {
if (effect['type'] === "stat_scaling" && effect['slider'] === true) {
const { slider_name, slider_behavior = 'merge', slider_max, slider_step } = effect;
if (slider_map.has(slider_name)) {
if (slider_max !== undefined) {
const slider_info = slider_map.get(slider_name);
slider_info.max += slider_max;
}
}
else if (slider_behavior === 'merge') {
slider_map.set(slider_name, {
label_name: slider_name+' ('+ability.display_name+')',
max: slider_max,
step: slider_step,
id: "ability-slider"+ability.id,
//color: effect['slider_color'] TODO: add colors to json
abil: ability
});
}
}
if (effect['type'] === "raw_stat" && effect['toggle']) {
const { toggle: toggle_name } = effect;
button_map.set(toggle_name, {
abil: ability
});
}
}
}
// next, render the sliders and toggles onto the abilities.
for (const [slider_name, slider_info] of slider_map.entries()) {
let slider_container = gen_slider_labeled(slider_info);
document.getElementById("boost-sliders").appendChild(slider_container);
slider_info.slider = document.getElementById(slider_info.id);
slider_info.slider.addEventListener("change", (e) => atree_scaling.mark_dirty().update());
}
for (const [button_name, button_info] of button_map.entries()) {
let button = make_elem('button', ["button-boost", "border-0", "text-white", "dark-8u", "dark-shadow-sm", "m-1"], {
id: button_info.abil.id,
textContent: button_name
});
button.addEventListener("click", (e) => {
if (button.classList.contains("toggleOn")) {
button.classList.remove("toggleOn");
} else {
button.classList.add("toggleOn");
}
atree_scaling.mark_dirty().update()
});
button_info.button = button;
document.getElementById("boost-toggles").appendChild(button);
}
return [slider_map, button_map];
}
})().link_to(atree_node, 'atree-order').link_to(atree_merge, 'atree-merged').link_to(atree_render_active, 'atree-elements');
/**
* Scaling stats from ability tree.
* Return StatMap of added stats,
*
* Signature: AbilityTreeScalingNode(atree-merged: MergedATree, scale-scats: StatMap,
* atree-interactive: [Map<str, slider_info>, Map<str, button_info>]) => StatMap
*/
const atree_scaling = new (class extends ComputeNode {
constructor() { super('atree-scaling-collector'); }
compute_func(input_map) {
const atree_merged = input_map.get('atree-merged');
const pre_scale_stats = input_map.get('scale-stats');
const [slider_map, button_map] = input_map.get('atree-interactive');
let ret_effects = new Map();
for (const [abil_id, abil] of atree_merged.entries()) {
if (abil.effects.length == 0) { continue; }
for (const effect of abil.effects) {
switch (effect.type) {
case 'raw_stat':
// TODO: toggles...
if (effect.toggle) {
const button = button_map.get(effect.toggle).button;
if (!button.classList.contains("toggleOn")) { continue; }
for (const bonus of effect.bonuses) {
const { type, name, abil = "", value } = bonus;
// TODO: prop
if (type === "stat") {
merge_stat(ret_effects, name, value);
}
}
}
continue;
case 'stat_scaling':
let total = 0;
const {slider = false, scaling = [0]} = effect;
let { positive = true, round = true } = effect;
if (slider) {
const slider_val = slider_map.get(effect.slider_name).slider.value;
total = parseInt(slider_val) * scaling[0];
round = false;
positive = false;
}
else {
// TODO: type: prop?
for (const [_scaling, input] of zip2(scaling, effect.inputs)) {
total += _scaling * pre_scale_stats.get(input.name);
}
}
if ('output' in effect) { // sometimes nodes will modify slider without having effect.
if (round) { total = Math.floor(round_near(total)); }
if (positive && total < 0) { total = 0; } // Normal stat scaling will not go negative.
if ('max' in effect && total > effect.max) { total = effect.max; }
if (Array.isArray(effect.output)) {
for (const output of effect.output) {
if (output.type === 'stat') { // TODO: prop
merge_stat(ret_effects, output.name, total);
}
}
}
else {
if (effect.output.type === 'stat') {
merge_stat(ret_effects, effect.output.name, total);
}
}
}
continue;
}
}
}
return ret_effects;
}
})().link_to(atree_merge, 'atree-merged').link_to(atree_make_interactives, 'atree-interactive');
/**
* Collect stats from ability tree.
* Collect raw stats from ability tree.
* Return StatMap of added stats.
*
* Signature: AbilityTreeStatsNode(atree-merged: MergedATree) => StatMap
*/
const atree_stats = new (class extends ComputeNode {
constructor() { super('atree-stats-collector'); }
const atree_raw_stats = new (class extends ComputeNode {
constructor() { super('atree-raw-stats-collector'); }
compute_func(input_map) {
const atree_merged = input_map.get('atree-merged');

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -17,6 +17,8 @@ let armor_powder_node = new (class extends ComputeNode {
}
})();
const damageMultipliers = new Map([ ["totem", .2], ["warscream", 0.0], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]);
let boosts_node = new (class extends ComputeNode {
constructor() { super('builder-boost-input'); }
@ -887,6 +889,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() {
this.mark_dirty();
this.update();
@ -1068,10 +1081,10 @@ function builder_graph_init() {
// These two are defined in `atree.js`
atree_node.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');
stat_agg_node.link_to(pre_scale_agg_node, 'pre-scaling');
stat_agg_node.link_to(atree_scaling, 'atree-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');

View file

@ -1,6 +1,6 @@
let all_nodes = new Set();
let node_debug_stack = [];
let COMPUTE_GRAPH_DEBUG = false;
let COMPUTE_GRAPH_DEBUG = true;
class ComputeNode {
/**
* Make a generic compute node.
@ -15,7 +15,7 @@ class ComputeNode {
this.value = null;
this.name = name;
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:
// 2: dirty
// 1: possibly dirty
@ -39,6 +39,13 @@ class ComputeNode {
if (this.dirty == 2) {
let calc_inputs = new Map();
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);
}
this.value = this.compute_func(calc_inputs);
@ -213,7 +220,7 @@ class PrintNode extends ComputeNode {
*
* Signature: InputNode() => str
*/
class InputNode extends ComputeNode {
class InputNode extends ValueCheckComputeNode {
constructor(name, input_field) {
super(name);
this.input_field = input_field;

View file

@ -2,8 +2,6 @@
* File implementing core damage calculation logic.
*/
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["warscream", 0.00], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]);
function get_base_dps(item) {
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];
//SUPER JANK @HPP PLS FIX
@ -260,8 +258,9 @@ spell_heal: {
spell_total: {
name: str != "total" Name of the part.
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.
Alternatively, a property reference of the format <ability_id>.propname
}

View file

@ -39,7 +39,25 @@ def translate_id(id_data, atree_data):
for bonus in effect["bonuses"]:
if "abil" in bonus and bonus["abil"] in id_data[_class]:
bonus["abil"] = id_data[_class][bonus["abil"]]
elif effect["type"] == "replace_spell":
for part in effect['parts']:
if 'hits' in part: # Translate parametrized hits...
hits_mapping = part['hits']
keys = list(hits_mapping.keys())
for k in keys:
v = hits_mapping[k]
if isinstance(v, str):
abil_id, propname = v.split('.')
hits_mapping[k] = str(id_data[_class][abil_id])+'.'+propname
elif effect["type"] == "add_spell_prop":
if 'hits' in effect: # Translate parametrized hits...
hits_mapping = effect['hits']
keys = list(hits_mapping.keys())
for k in keys:
v = hits_mapping[k]
if isinstance(v, str):
abil_id, propname = v.split('.')
hits_mapping[k] = str(id_data[_class][abil_id])+'.'+propname
elif effect["type"] == "stat_scaling":
if "inputs" in effect: # Might not exist for sliders
for _input in effect["inputs"]: