Merge remote-tracking branch 'origin/master' into atlas-revamp
This commit is contained in:
commit
787dea106c
33 changed files with 2995 additions and 2221 deletions
205
builder/doc.html
205
builder/doc.html
|
@ -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,12 +1301,10 @@
|
|||
<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>
|
||||
<script type="text/javascript" src="../js/atree_constants_min.js"></script>
|
||||
<script type="text/javascript" src="../js/display_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/display.js"></script>
|
||||
<script type="text/javascript" src="../js/load.js"></script>
|
||||
|
@ -1276,13 +1312,14 @@
|
|||
<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/craft.js"></script>
|
||||
<script type="text/javascript" src="../js/build.js"></script>
|
||||
<script type="text/javascript" src="../js/build_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/build_encode_decode.js"></script>
|
||||
<script type="text/javascript" src="../js/atree.js"></script>
|
||||
<script type="text/javascript" src="../js/builder.js"></script>
|
||||
<script type="text/javascript" src="../js/builder_graph.js"></script>
|
||||
<script type="text/javascript" src="../js/optimize.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/atree_constants_min.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/build.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/builder_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/build_encode_decode.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/atree.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/builder.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/builder_graph.js"></script>
|
||||
<!--script type="text/javascript" src="../js/builder/optimize.js"></script-->
|
||||
|
||||
<div id="graph_body" style="max-width: 100%; height: 100vh">
|
||||
<button id="saveButton">JANKY Export SVG</button>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -97,7 +97,7 @@
|
|||
<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="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 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>
|
||||
|
@ -124,7 +124,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto order-xl-0 order-1 my-0">
|
||||
<div class="col 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">
|
||||
<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>
|
||||
|
@ -150,7 +150,7 @@
|
|||
</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 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 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>
|
||||
|
@ -177,7 +177,7 @@
|
|||
</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 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 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>
|
||||
|
@ -203,7 +203,7 @@
|
|||
</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 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 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>
|
||||
|
@ -230,7 +230,7 @@
|
|||
</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 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 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>
|
||||
|
@ -257,7 +257,7 @@
|
|||
</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 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 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>
|
||||
|
@ -284,7 +284,7 @@
|
|||
</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 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 id="necklace-img" class="img-fluid rounded item-display-new-toggleable" style="background-image: url('../media/items/new.png'); background-position: 100% 0;"></div>
|
||||
|
@ -310,7 +310,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto order-xl-0 order-1">
|
||||
<div class="col order-xl-0 order-1">
|
||||
<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 id="weapon-img" class="img-fluid rounded item-display-new-toggleable" style = "background-image: url('../media/items/new.png');"></div>
|
||||
|
@ -337,7 +337,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto order-xl-0 order-1 level-input">
|
||||
<div class="col order-xl-0 order-1 level-input">
|
||||
<div class="row h-100 px-1">
|
||||
<div class="col">
|
||||
<div class="row align-items-center justify-content-left">
|
||||
|
@ -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%)
|
||||
|
@ -837,6 +834,8 @@
|
|||
<div class="col mx-auto" style="height: 90vh; overflow-y: auto;" id="atree-rhs">
|
||||
<div class="col mx-auto" style="height: 2em; overflow-y: auto;" id="atree-header">
|
||||
</div>
|
||||
<div class="col mx-auto" style="" id="atree-warning">
|
||||
</div>
|
||||
<div class="col mx-auto" style="overflow-y: auto;" id="atree-active">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1304,7 +1303,6 @@
|
|||
<script type="text/javascript" src="../js/powders.js"></script>
|
||||
<script type="text/javascript" src="../js/skillpoints.js"></script>
|
||||
<script type="text/javascript" src="../js/damage_calc.js"></script>
|
||||
<script type="text/javascript" src="../js/atree_constants_min.js"></script>
|
||||
<script type="text/javascript" src="../js/display_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/display.js"></script>
|
||||
<script type="text/javascript" src="../js/load.js"></script>
|
||||
|
@ -1312,13 +1310,13 @@
|
|||
<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/craft.js"></script>
|
||||
<script type="text/javascript" src="../js/build.js"></script>
|
||||
<script type="text/javascript" src="../js/build_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/build_encode_decode.js"></script>
|
||||
<script type="text/javascript" src="../js/atree.js"></script>
|
||||
<script type="text/javascript" src="../js/builder.js"></script>
|
||||
<script type="text/javascript" src="../js/builder_graph.js"></script>
|
||||
<script type="text/javascript" src="../js/optimize.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../js/builder/atree_constants_min.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/build.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/builder_constants.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/build_encode_decode.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/atree.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/builder.js"></script>
|
||||
<script type="text/javascript" src="../js/builder/builder_graph.js"></script>
|
||||
<!--script type="text/javascript" src="../js/builder/optimize.js"></script-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
1001
builder2.html
1001
builder2.html
File diff suppressed because it is too large
Load diff
14
clean.json
14
clean.json
|
@ -19,9 +19,9 @@
|
|||
"mr": 10,
|
||||
"int": 15,
|
||||
"def": -10,
|
||||
"wDamPct": 30,
|
||||
"aDamPct": 35,
|
||||
"tDamPct": -40,
|
||||
"wDefPct": 30,
|
||||
"spRaw4": -4,
|
||||
"id": 3579
|
||||
},
|
||||
|
@ -65,7 +65,7 @@
|
|||
"lvl": 104,
|
||||
"strReq": 60,
|
||||
"agiReq": 60,
|
||||
"mr": 8,
|
||||
"mr": 10,
|
||||
"ref": 48,
|
||||
"atkTier": -1,
|
||||
"aDamPct": 32,
|
||||
|
@ -973,7 +973,6 @@
|
|||
"ms": 30,
|
||||
"xpb": 30,
|
||||
"ref": 20,
|
||||
"spRaw3": 6,
|
||||
"id": 1711
|
||||
},
|
||||
{
|
||||
|
@ -2334,7 +2333,7 @@
|
|||
"aDamPct": 30,
|
||||
"aDefPct": 20,
|
||||
"fixID": true,
|
||||
"spRaw1": -5,
|
||||
"spRaw1": -3,
|
||||
"id": 2510
|
||||
},
|
||||
{
|
||||
|
@ -3187,10 +3186,10 @@
|
|||
"intReq": 23,
|
||||
"agiReq": 23,
|
||||
"defReq": 23,
|
||||
"mr": 5,
|
||||
"mr": 9,
|
||||
"sdPct": 15,
|
||||
"mdPct": 15,
|
||||
"ms": 5,
|
||||
"ms": 9,
|
||||
"str": 17,
|
||||
"dex": 17,
|
||||
"int": 17,
|
||||
|
@ -3211,6 +3210,7 @@
|
|||
"quest": "The Qira Hive",
|
||||
"category": "armor",
|
||||
"displayName": "Anima-Infused Cuirass",
|
||||
"set": "Master Hive",
|
||||
"slots": 2,
|
||||
"hp": 3800,
|
||||
"fDef": 200,
|
||||
|
@ -17497,7 +17497,7 @@
|
|||
"aDefPct": -25,
|
||||
"fixID": true,
|
||||
"spPct4": 28,
|
||||
"rainbowRaw": 1275,
|
||||
"rSdRaw": 1275,
|
||||
"id": 720
|
||||
},
|
||||
{
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -63,6 +63,7 @@
|
|||
<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.js"></script>
|
||||
<script type="text/javascript" src="/js/item_display.js"></script>
|
||||
<script type="text/javascript" src="/js/item.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
|
||||
<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/wynnstyles.css">
|
||||
</head>
|
||||
|
@ -39,7 +39,7 @@
|
|||
<a href="../credits.txt" class="link">Additional credits</a>
|
||||
</div>
|
||||
<div class = "col text-center" id = "help">
|
||||
<a href="items_2_help.html" class="link" target="_blank">Search Guide</a>
|
||||
<a href="items_adv_help.html" class="link" target="_blank">Search Guide</a>
|
||||
</div>
|
||||
<div class = "col text-end">
|
||||
<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/build_utils.js"></script>
|
||||
<script type="text/javascript" src="/js/icons.js"></script>
|
||||
<script type="text/javascript" src="/js/powders.js"></script>
|
||||
<script type="text/javascript" src="/js/damage_calc.js"></script>
|
||||
<script type="text/javascript" src="/js/display_constants.js"></script>
|
||||
<script type="text/javascript" src="/js/display.js"></script>
|
||||
<script type="text/javascript" src="/js/query.js"></script>
|
||||
<script type="text/javascript" src="/js/expr_parser.js"></script>
|
||||
<script type="text/javascript" src="/js/load.js"></script>
|
||||
<script type="text/javascript" src="/js/items_2.js"></script>
|
||||
<script type="text/javascript" src="/js/items_adv.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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
|
@ -12,7 +12,7 @@ function skillPointsToPercentage(skp){
|
|||
skp = 150;
|
||||
}
|
||||
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).toFixed(3);
|
||||
//return Math.min(Math.max(0.00,(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771)),.808);
|
||||
|
@ -86,7 +86,8 @@ let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slot
|
|||
"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"
|
||||
"critDamPct",
|
||||
"spPct1Final", "spPct2Final", "spPct3Final", "spPct4Final"
|
||||
];
|
||||
// Extra fake IDs (reserved for use in spell damage calculation) : damMult, defMult, poisonPct, activeMajorIDs
|
||||
let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ]
|
||||
|
@ -127,16 +128,7 @@ let nonRolledIDs = [
|
|||
"nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_",
|
||||
"majorIds",
|
||||
"damMobs",
|
||||
"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).
|
||||
"defMobs"
|
||||
];
|
||||
let rolledIDs = [
|
||||
"hprPct",
|
||||
|
@ -165,7 +157,7 @@ let rolledIDs = [
|
|||
"spPct2", "spRaw2",
|
||||
"spPct3", "spRaw3",
|
||||
"spPct4", "spRaw4",
|
||||
"pDamRaw",
|
||||
"rSdRaw",
|
||||
"sprint",
|
||||
"sprintReg",
|
||||
"jh",
|
||||
|
@ -180,7 +172,8 @@ let rolledIDs = [
|
|||
"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
|
||||
"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" ];
|
||||
|
||||
|
|
6
js/builder/README_atree_constants.md
Normal file
6
js/builder/README_atree_constants.md
Normal 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
|
|
@ -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;
|
||||
}
|
||||
})();
|
||||
|
@ -238,71 +241,6 @@ const atree_state_node = new (class extends ComputeNode {
|
|||
}
|
||||
})().link_to(atree_render, 'atree-render');
|
||||
|
||||
/**
|
||||
* Collect abilities and condense them into a list of "final abils".
|
||||
* This is just for rendering purposes, and for collecting things that modify spells into one chunk.
|
||||
* I stg if wynn makes abils that modify multiple spells
|
||||
* ... well we can extend this by making `base_abil` a list instead but annoy
|
||||
*
|
||||
* Signature: AbilityTreeMergeNode(player-class: WeaponType, atree: ATree, atree-state: RenderedATree) => Map[id, Ability]
|
||||
*/
|
||||
const atree_merge = new (class extends ComputeNode {
|
||||
constructor() { super('builder-atree-merge'); }
|
||||
|
||||
compute_func(input_map) {
|
||||
const player_class = input_map.get('player-class');
|
||||
const atree_state = input_map.get('atree-state');
|
||||
const atree_order = input_map.get('atree');
|
||||
|
||||
let abils_merged = new Map();
|
||||
for (const abil of default_abils[player_class]) {
|
||||
let tmp_abil = deepcopy(abil);
|
||||
if (!('desc' in tmp_abil)) {
|
||||
tmp_abil.desc = [];
|
||||
}
|
||||
else if (!Array.isArray(tmp_abil.desc)) {
|
||||
tmp_abil.desc = [tmp_abil.desc];
|
||||
}
|
||||
tmp_abil.subparts = [abil.id];
|
||||
abils_merged.set(abil.id, tmp_abil);
|
||||
}
|
||||
|
||||
for (const node of atree_order) {
|
||||
const abil_id = node.ability.id;
|
||||
if (!atree_state.get(abil_id).active) {
|
||||
continue;
|
||||
}
|
||||
const abil = node.ability;
|
||||
|
||||
if ('base_abil' in abil) {
|
||||
if (abils_merged.has(abil.base_abil)) {
|
||||
// Merge abilities.
|
||||
// TODO: What if there is more than one base abil?
|
||||
let base_abil = abils_merged.get(abil.base_abil);
|
||||
if (Array.isArray(abil.desc)) { base_abil.desc = base_abil.desc.concat(abil.desc); }
|
||||
else { base_abil.desc.push(abil.desc); }
|
||||
|
||||
base_abil.subparts.push(abil.id);
|
||||
base_abil.effects = base_abil.effects.concat(abil.effects);
|
||||
for (let propname in abil.properties) {
|
||||
base_abil[propname] = abil[propname];
|
||||
}
|
||||
}
|
||||
// do nothing otherwise.
|
||||
}
|
||||
else {
|
||||
let tmp_abil = deepcopy(abil);
|
||||
if (!Array.isArray(tmp_abil.desc)) {
|
||||
tmp_abil.desc = [tmp_abil.desc];
|
||||
}
|
||||
tmp_abil.subparts = [abil.id];
|
||||
abils_merged.set(abil_id, tmp_abil);
|
||||
}
|
||||
}
|
||||
return abils_merged;
|
||||
}
|
||||
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
|
||||
|
||||
/**
|
||||
* Check if an atree node can be activated.
|
||||
*
|
||||
|
@ -377,11 +315,11 @@ const atree_validate = new (class extends ComputeNode {
|
|||
const abil = node.ability;
|
||||
if (atree_state.get(abil.id).active) {
|
||||
atree_to_add.push([node, 'not reachable', false]);
|
||||
drawAtlasImage(atree_state.get(abil.id).img, atreeNodeAtlasImg, [atreeNodeAtlasPositions[abil.display.icon], 2], atreeNodeTileSize);
|
||||
draw_atlas_image(atree_state.get(abil.id).img, atree_node_atlas_img, [atree_node_atlas_positions[abil.display.icon], 2], atree_node_tile_size);
|
||||
}
|
||||
else {
|
||||
atree_not_present.push(abil.id);
|
||||
drawAtlasImage(atree_state.get(abil.id).img, atreeNodeAtlasImg, [atreeNodeAtlasPositions[abil.display.icon], 0], atreeNodeTileSize);
|
||||
draw_atlas_image(atree_state.get(abil.id).img, atree_node_atlas_img, [atree_node_atlas_positions[abil.display.icon], 0], atree_node_tile_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,7 +368,7 @@ const atree_validate = new (class extends ComputeNode {
|
|||
const node = atree_state.get(node_id);
|
||||
const [success, hard_error, reason] = abil_can_activate(node, atree_state, reachable, archetype_count, ap_left);
|
||||
if (success) {
|
||||
drawAtlasImage(node.img, atreeNodeAtlasImg, [atreeNodeAtlasPositions[node.ability.display.icon], 1], atreeNodeTileSize);
|
||||
draw_atlas_image(node.img, atree_node_atlas_img, [atree_node_atlas_positions[node.ability.display.icon], 1], atree_node_tile_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -449,176 +387,74 @@ const atree_validate = new (class extends ComputeNode {
|
|||
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
|
||||
|
||||
/**
|
||||
* Render ability tree.
|
||||
* Return map of id -> corresponding html element.
|
||||
* Collect abilities and condense them into a list of "final abils".
|
||||
* This is just for rendering purposes, and for collecting things that modify spells into one chunk.
|
||||
* I stg if wynn makes abils that modify multiple spells
|
||||
* ... well we can extend this by making `base_abil` a list instead but annoy
|
||||
*
|
||||
* Signature: AbilityTreeRenderActiveNode(atree-merged: MergedATree, atree-order: ATree, atree-errors: List[str]) => Map[int, ATreeNode]
|
||||
* Signature: AbilityTreeMergeNode(player-class: WeaponType, atree: ATree, atree-state: RenderedATree) => Map[id, Ability]
|
||||
*/
|
||||
const atree_render_active = new (class extends ComputeNode {
|
||||
constructor() {
|
||||
super('atree-render-active');
|
||||
this.list_elem = document.getElementById("atree-active");
|
||||
}
|
||||
const atree_merge = new (class extends ComputeNode {
|
||||
constructor() { super('builder-atree-merge'); }
|
||||
|
||||
compute_func(input_map) {
|
||||
const merged_abils = input_map.get('atree-merged');
|
||||
const atree_order = input_map.get('atree-order');
|
||||
const [hard_error, _errors] = input_map.get('atree-errors');
|
||||
const errors = deepcopy(_errors);
|
||||
|
||||
this.list_elem.innerHTML = ""; //reset all atree actives - should be done in a more general way later
|
||||
// TODO: move to display?
|
||||
if (errors.length > 0) {
|
||||
const errorbox = make_elem('div', ['rounded-bottom', 'dark-4', 'border', 'p-0', 'mx-2', 'my-4', 'dark-shadow']);
|
||||
this.list_elem.append(errorbox);
|
||||
|
||||
const error_title = make_elem('b', ['warning', 'scaled-font'], { innerHTML: "ATree Error!" });
|
||||
errorbox.append(error_title);
|
||||
|
||||
for (let i = 0; i < 5 && i < errors.length; ++i) {
|
||||
errorbox.append(make_elem("p", ["warning", "small-text"], {textContent: errors[i]}));
|
||||
}
|
||||
if (errors.length > 5) {
|
||||
const error = '... ' + (errors.length-5) + ' errors not shown';
|
||||
errorbox.append(make_elem("p", ["warning", "small-text"], {textContent: error}));
|
||||
}
|
||||
}
|
||||
const ret_map = new Map();
|
||||
const to_render_id = [999, 998];
|
||||
for (const node of atree_order) {
|
||||
if (!merged_abils.has(node.ability.id)) {
|
||||
continue;
|
||||
}
|
||||
to_render_id.push(node.ability.id);
|
||||
}
|
||||
for (const id of to_render_id) {
|
||||
const abil = merged_abils.get(id);
|
||||
|
||||
const active_tooltip = make_elem('div', ['rounded-bottom', 'dark-4', 'border', 'p-0', 'mx-2', 'my-4', 'dark-shadow']);
|
||||
active_tooltip.append(make_elem('b', ['scaled-font'], { innerHTML: abil.display_name }));
|
||||
|
||||
for (const desc of abil.desc) {
|
||||
active_tooltip.append(make_elem('p', ['scaled-font-sm', 'my-0', 'mx-1', 'text-wrap'], { textContent: desc }));
|
||||
}
|
||||
ret_map.set(abil.id, active_tooltip);
|
||||
|
||||
this.list_elem.append(active_tooltip);
|
||||
}
|
||||
return ret_map;
|
||||
}
|
||||
})().link_to(atree_node, 'atree-order').link_to(atree_merge, 'atree-merged').link_to(atree_validate, 'atree-errors');
|
||||
|
||||
/**
|
||||
* Collect spells from abilities.
|
||||
*
|
||||
* Signature: AbilityCollectSpellsNode(atree-merged: Map[id, Ability]) => List[Spell]
|
||||
*/
|
||||
const atree_collect_spells = new (class extends ComputeNode {
|
||||
constructor() { super('atree-spell-collector'); }
|
||||
|
||||
compute_func(input_map) {
|
||||
const atree_merged = input_map.get('atree-merged');
|
||||
const [hard_error, errors] = input_map.get('atree-errors');
|
||||
if (hard_error) { return []; }
|
||||
if (hard_error) { return null; }
|
||||
const player_class = input_map.get('player-class');
|
||||
const atree_state = input_map.get('atree-state');
|
||||
const atree_order = input_map.get('atree');
|
||||
|
||||
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);
|
||||
if (ret_spell) {
|
||||
// NOTE: do not mutate results of previous steps!
|
||||
for (const key in effect) {
|
||||
ret_spell[key] = deepcopy(effect[key]);
|
||||
let abils_merged = new Map();
|
||||
for (const abil of default_abils[player_class]) {
|
||||
let tmp_abil = deepcopy(abil);
|
||||
if (!('desc' in tmp_abil)) {
|
||||
tmp_abil.desc = [];
|
||||
}
|
||||
else if (!Array.isArray(tmp_abil.desc)) {
|
||||
tmp_abil.desc = [tmp_abil.desc];
|
||||
}
|
||||
tmp_abil.subparts = [abil.id];
|
||||
abils_merged.set(abil.id, tmp_abil);
|
||||
}
|
||||
|
||||
for (const node of atree_order) {
|
||||
const abil_id = node.ability.id;
|
||||
if (!atree_state.get(abil_id).active) {
|
||||
continue;
|
||||
}
|
||||
const abil = node.ability;
|
||||
|
||||
if ('base_abil' in abil) {
|
||||
if (abils_merged.has(abil.base_abil)) {
|
||||
// Merge abilities.
|
||||
// TODO: What if there is more than one base abil?
|
||||
let base_abil = abils_merged.get(abil.base_abil);
|
||||
if (Array.isArray(abil.desc)) { base_abil.desc = base_abil.desc.concat(abil.desc); }
|
||||
else { base_abil.desc.push(abil.desc); }
|
||||
|
||||
base_abil.subparts.push(abil.id);
|
||||
base_abil.effects = base_abil.effects.concat(abil.effects);
|
||||
for (let propname in abil.properties) {
|
||||
if (propname in base_abil.properties) {
|
||||
base_abil.properties[propname] += abil.properties[propname];
|
||||
}
|
||||
else { base_abil.properties[propname] = abil.properties[propname]; }
|
||||
}
|
||||
}
|
||||
// do nothing otherwise.
|
||||
}
|
||||
else {
|
||||
ret_spells.set(effect.base_spell, deepcopy(effect));
|
||||
let tmp_abil = deepcopy(abil);
|
||||
if (!Array.isArray(tmp_abil.desc)) {
|
||||
tmp_abil.desc = [tmp_abil.desc];
|
||||
}
|
||||
tmp_abil.subparts = [abil.id];
|
||||
abils_merged.set(abil_id, tmp_abil);
|
||||
}
|
||||
}
|
||||
return abils_merged;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [abil_id, abil] of atree_merged.entries()) {
|
||||
for (const effect of abil.effects) {
|
||||
switch (effect.type) {
|
||||
case 'replace_spell':
|
||||
// Already handled above.
|
||||
continue;
|
||||
case 'add_spell_prop': {
|
||||
const { base_spell, target_part = null, cost = 0, behavior = 'merge'} = effect;
|
||||
const ret_spell = ret_spells.get(base_spell);
|
||||
// TODO: unjankify this...
|
||||
if ('cost' in ret_spell) { ret_spell.cost += cost; }
|
||||
|
||||
if (target_part === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let found_part = false;
|
||||
for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
|
||||
if (part.name !== target_part) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('multipliers' in effect) {
|
||||
for (const [idx, v] of effect.multipliers.entries()) { // python: enumerate()
|
||||
part.multipliers[idx] += v;
|
||||
}
|
||||
}
|
||||
else if ('power' in effect) {
|
||||
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)
|
||||
if (idx in part.hits) { part.hits[idx] += v; }
|
||||
else { part.hits[idx] = v; }
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "uhh invalid spell add effect";
|
||||
}
|
||||
found_part = true;
|
||||
break;
|
||||
}
|
||||
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
|
||||
ret_spell.parts.push(spell_part);
|
||||
}
|
||||
if ('display' in effect) {
|
||||
ret_spell.display = effect.display;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 'convert_spell_conv':
|
||||
const { base_spell, target_part, conversion } = effect;
|
||||
const ret_spell = ret_spells.get(base_spell);
|
||||
const elem_idx = damageClasses.indexOf(conversion);
|
||||
let filter = target_part === 'all';
|
||||
for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
|
||||
if (filter || part.name === target_part) {
|
||||
if ('multipliers' in part) {
|
||||
let total_conv = 0;
|
||||
for (let i = 1; i < 6; ++i) { // skip neutral
|
||||
total_conv += part.multipliers[i];
|
||||
}
|
||||
let new_conv = [part.multipliers[0], 0, 0, 0, 0, 0];
|
||||
new_conv[elem_idx] = total_conv;
|
||||
part.multipliers = new_conv;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret_spells;
|
||||
}
|
||||
})().link_to(atree_merge, 'atree-merged').link_to(atree_validate, 'atree-errors');
|
||||
|
||||
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state').link_to(atree_validate, 'atree-errors');
|
||||
|
||||
/**
|
||||
* Make interactive elements (sliders, buttons)
|
||||
|
@ -635,10 +471,11 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
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 = "";
|
||||
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
|
||||
|
@ -687,7 +524,7 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
// 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);
|
||||
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());
|
||||
}
|
||||
|
@ -705,18 +542,18 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
atree_scaling.mark_dirty().update()
|
||||
});
|
||||
button_info.button = button;
|
||||
document.getElementById("boost-toggles").appendChild(button);
|
||||
boost_toggle_parent.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');
|
||||
})().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>]) => 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'); }
|
||||
|
@ -726,23 +563,41 @@ const atree_scaling = new (class extends ComputeNode {
|
|||
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);
|
||||
if (merge_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':
|
||||
// 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);
|
||||
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;
|
||||
|
@ -769,14 +624,242 @@ const atree_scaling = new (class extends ComputeNode {
|
|||
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);
|
||||
}
|
||||
apply_bonus(output, total);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (effect.output.type === 'stat') {
|
||||
merge_stat(ret_effects, effect.output.name, total);
|
||||
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');
|
||||
|
||||
const atree_render_errors = new (class extends ComputeNode {
|
||||
constructor() {
|
||||
super('atree-render-errors');
|
||||
this.list_elem = document.getElementById("atree-warning");
|
||||
}
|
||||
|
||||
compute_func(input_map) {
|
||||
const [hard_error, errors] = input_map.get('atree-errors');
|
||||
|
||||
this.list_elem.innerHTML = ""; //reset all atree actives - should be done in a more general way later
|
||||
// TODO: move to display?
|
||||
if (errors.length > 0) {
|
||||
const errorbox = make_elem('div', ['rounded-bottom', 'dark-4', 'border', 'p-0', 'mx-2', 'mb-0', 'mt-4', 'dark-shadow']);
|
||||
this.list_elem.append(errorbox);
|
||||
|
||||
const error_title = make_elem('b', ['warning', 'scaled-font'], { innerHTML: "ATree Error!" });
|
||||
errorbox.append(error_title);
|
||||
|
||||
for (let i = 0; i < 5 && i < errors.length; ++i) {
|
||||
errorbox.append(make_elem("p", ["warning", "small-text"], {textContent: errors[i]}));
|
||||
}
|
||||
if (errors.length > 5) {
|
||||
const error = '... ' + (errors.length-5) + ' errors not shown';
|
||||
errorbox.append(make_elem("p", ["warning", "small-text"], {textContent: error}));
|
||||
}
|
||||
}
|
||||
}
|
||||
})().link_to(atree_validate, 'atree-errors');
|
||||
|
||||
/**
|
||||
* Render ability tree.
|
||||
* Return map of id -> corresponding html element.
|
||||
*
|
||||
* Signature: AbilityTreeRenderActiveNode(atree-merged: MergedATree, atree-order: ATree, atree-errors: List[str]) => Map[int, ATreeNode]
|
||||
*/
|
||||
const atree_render_active = new (class extends ComputeNode {
|
||||
constructor() {
|
||||
super('atree-render-active');
|
||||
this.list_elem = document.getElementById("atree-active");
|
||||
}
|
||||
|
||||
compute_func(input_map) {
|
||||
console.log("boop");
|
||||
const merged_abils = input_map.get('atree-merged');
|
||||
const atree_order = input_map.get('atree-order');
|
||||
|
||||
this.list_elem.innerHTML = ""; //reset all atree actives - should be done in a more general way later
|
||||
const ret_map = new Map();
|
||||
const to_render_id = [999, 998];
|
||||
for (const node of atree_order) {
|
||||
if (!merged_abils.has(node.ability.id)) {
|
||||
continue;
|
||||
}
|
||||
to_render_id.push(node.ability.id);
|
||||
}
|
||||
for (const id of to_render_id) {
|
||||
const abil = merged_abils.get(id);
|
||||
|
||||
const active_tooltip = make_elem('div', ['rounded-bottom', 'dark-4', 'border', 'p-0', 'mx-2', 'my-4', 'dark-shadow']);
|
||||
active_tooltip.append(make_elem('b', ['scaled-font'], { innerHTML: abil.display_name }));
|
||||
|
||||
for (const desc of abil.desc) {
|
||||
active_tooltip.append(make_elem('p', ['scaled-font-sm', 'my-0', 'mx-1', 'text-wrap'], { textContent: desc }));
|
||||
}
|
||||
ret_map.set(abil.id, active_tooltip);
|
||||
|
||||
this.list_elem.append(active_tooltip);
|
||||
}
|
||||
return ret_map;
|
||||
}
|
||||
})().link_to(atree_node, 'atree-order').link_to(atree_scaling_tree, 'atree-merged');
|
||||
|
||||
|
||||
/**
|
||||
* Collect spells from abilities.
|
||||
*
|
||||
* Signature: AbilityCollectSpellsNode(atree-merged: Map[id, Ability]) => List[Spell]
|
||||
*/
|
||||
const atree_collect_spells = new (class extends ComputeNode {
|
||||
constructor() { super('atree-spell-collector'); }
|
||||
|
||||
compute_func(input_map) {
|
||||
const atree_merged = input_map.get('atree-merged');
|
||||
|
||||
/**
|
||||
* 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.
|
||||
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) {
|
||||
ret_spell[key] = deepcopy(effect[key]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [abil_id, abil] of atree_merged.entries()) {
|
||||
for (const effect of abil.effects) {
|
||||
switch (effect.type) {
|
||||
case 'replace_spell':
|
||||
// Already handled above.
|
||||
continue;
|
||||
case 'add_spell_prop': {
|
||||
const { base_spell, target_part = null, cost = 0, behavior = 'merge'} = effect;
|
||||
const ret_spell = ret_spells.get(base_spell);
|
||||
// TODO: unjankify this...
|
||||
if ('cost' in ret_spell) { ret_spell.cost += cost; }
|
||||
|
||||
if (target_part === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let found_part = false;
|
||||
for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
|
||||
if (part.name !== target_part) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('multipliers' in effect) {
|
||||
for (const [idx, v] of effect.multipliers.entries()) { // python: enumerate()
|
||||
part.multipliers[idx] += v;
|
||||
}
|
||||
}
|
||||
else if ('power' in effect) {
|
||||
part.power += effect.power;
|
||||
}
|
||||
else if ('hits' in effect) {
|
||||
for (const [idx, _v] of Object.entries(effect.hits)) { // looks kinda similar to multipliers case... hmm... can we unify all of these three? (make healpower a list)
|
||||
let v = translate(_v);
|
||||
if (idx in part.hits) { part.hits[idx] += v; }
|
||||
else { part.hits[idx] = v; }
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw "uhh invalid spell add effect";
|
||||
}
|
||||
found_part = true;
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
ret_spell.display = effect.display;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 'convert_spell_conv':
|
||||
const { base_spell, target_part, conversion } = effect;
|
||||
const ret_spell = ret_spells.get(base_spell);
|
||||
const elem_idx = damageClasses.indexOf(conversion);
|
||||
let filter = target_part === 'all';
|
||||
for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
|
||||
if (filter || part.name === target_part) {
|
||||
if ('multipliers' in part) {
|
||||
let total_conv = 0;
|
||||
for (let i = 1; i < 6; ++i) { // skip neutral
|
||||
total_conv += part.multipliers[i];
|
||||
}
|
||||
let new_conv = [part.multipliers[0], 0, 0, 0, 0, 0];
|
||||
new_conv[elem_idx] = total_conv;
|
||||
part.multipliers = new_conv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -784,18 +867,18 @@ const atree_scaling = new (class extends ComputeNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
return ret_effects;
|
||||
return ret_spells;
|
||||
}
|
||||
})().link_to(atree_merge, 'atree-merged').link_to(atree_make_interactives, 'atree-interactive');
|
||||
})().link_to(atree_scaling_tree, 'atree-merged');
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
@ -1207,7 +1290,7 @@ function generateTooltip(container, node_elem, ability, atree_map) {
|
|||
} else {
|
||||
cost.innerHTML = reqYes;
|
||||
}
|
||||
cost.innerHTML += "<span class = 'mc-gray'>Ability Points:</span>" + (maxAP - apUsed) + "<span class = 'mc-gray'>/" + ability.cost;
|
||||
cost.innerHTML += "<span class = 'mc-gray'>Ability Points:</span> " + (maxAP - apUsed) + "<span class = 'mc-gray'>/" + ability.cost;
|
||||
container.appendChild(cost);
|
||||
|
||||
// archetype req
|
||||
|
@ -1218,7 +1301,7 @@ function generateTooltip(container, node_elem, ability, atree_map) {
|
|||
} else {
|
||||
archReq.innerHTML = reqNo;
|
||||
}
|
||||
archReq.innerHTML += "<span class = 'mc-gray'>Min" + ability.archetype + " Archetype:</span> " + archChosen + "<span class = 'mc-gray'>/" + ability.archetype_req;
|
||||
archReq.innerHTML += "<span class = 'mc-gray'>Min " + ability.archetype + " Archetype:</span> " + archChosen + "<span class = 'mc-gray'>/" + ability.archetype_req;
|
||||
container.appendChild(archReq);
|
||||
}
|
||||
|
||||
|
@ -1230,14 +1313,14 @@ function generateTooltip(container, node_elem, ability, atree_map) {
|
|||
} else {
|
||||
dependency.innerHTML = reqNo;
|
||||
}
|
||||
dependency.innerHTML += "<span class = 'mc-gray'>Required Ability:</span>" + atree_map.get(ability.dependencies[i]).ability.display_name;
|
||||
dependency.innerHTML += "<span class = 'mc-gray'>Required Ability:</span> " + atree_map.get(ability.dependencies[i]).ability.display_name;
|
||||
container.appendChild(dependency);
|
||||
}
|
||||
|
||||
// blockers
|
||||
for (let i = 0; i < blockedBy.length; i++) {
|
||||
let blocker = make_elem("p", ["scaled-font", "my-0", "mx-1"], {});
|
||||
blocker.innerHTML = reqNo + "<span class = 'mc-gray'>Blocked By:</span>" + blockedBy[i];
|
||||
blocker.innerHTML = reqNo + "<span class = 'mc-gray'>Blocked By:</span> " + blockedBy[i];
|
||||
container.appendChild(blocker);
|
||||
}
|
||||
}
|
||||
|
@ -1269,11 +1352,11 @@ function atree_set_state(node_wrapper, new_state) {
|
|||
}
|
||||
if (new_state) {
|
||||
node_wrapper.active = true;
|
||||
drawAtlasImage(node_wrapper.img, atreeNodeAtlasImg, [atreeNodeAtlasPositions[icon], 2], atreeNodeTileSize);
|
||||
draw_atlas_image(node_wrapper.img, atree_node_atlas_img, [atree_node_atlas_positions[icon], 2], atree_node_tile_size);
|
||||
}
|
||||
else {
|
||||
node_wrapper.active = false;
|
||||
drawAtlasImage(node_wrapper.img, atreeNodeAtlasImg, [atreeNodeAtlasPositions[icon], 1], atreeNodeTileSize);
|
||||
draw_atlas_image(node_wrapper.img, atree_node_atlas_img, [atree_node_atlas_positions[icon], 1], atree_node_tile_size);
|
||||
}
|
||||
let atree_connectors_map = node_wrapper.all_connectors_ref;
|
||||
for (const parent of node_wrapper.parents) {
|
||||
|
@ -1291,7 +1374,7 @@ function atree_set_state(node_wrapper, new_state) {
|
|||
// atlas vars
|
||||
|
||||
// first key is connector type, second key is highlight, then [x, y] pair of 0-index positions in the tile atlas
|
||||
const atreeConnectorAtlasPositions = {
|
||||
const atree_connector_atlas_positions = {
|
||||
"1100": {"0000": [0, 0], "1100": [1, 0]},
|
||||
"1010": {"0000": [2, 0], "1010": [3, 0]},
|
||||
"0110": {"0000": [4, 0], "0110": [5, 0]},
|
||||
|
@ -1304,11 +1387,18 @@ const atreeConnectorAtlasPositions = {
|
|||
"1011": {"0000": [5, 2], "1011": [6, 2], "1010": [7, 2], "1001": [8, 2], "0011": [9, 2]},
|
||||
"1111": {"0000": [0, 3], "1111": [1, 3], "1110": [2, 3], "1101": [3, 3], "1100": [4, 3], "1011": [5, 3], "1010": [6, 3], "1001": [7, 3], "0111": [8, 3], "0110": [9, 3], "0101": [10, 3], "0011": [11, 3]}
|
||||
}
|
||||
const atreeConnectorTileSize = 18;
|
||||
const atreeConnectorAtlasImg = make_elem("img", [], {src: "../media/atree/connectors.png"});
|
||||
const atree_connector_tile_size = 18;
|
||||
const atree_connector_atlas_img = make_elem("img", [], {src: "../media/atree/connectors.png", loaded: false});
|
||||
atree_connector_atlas_img.addEventListener("load", () => {
|
||||
atree_connector_atlas_img.loaded = true;
|
||||
for (const to_draw of atlas_to_draw.get(atree_connector_atlas_img)) {
|
||||
draw_atlas_image(to_draw[0], atree_connector_atlas_img, to_draw[1], to_draw[2]);
|
||||
}
|
||||
atlas_to_draw.set(atree_connector_atlas_img, []);
|
||||
});
|
||||
|
||||
// just has the x position, y is based on state
|
||||
const atreeNodeAtlasPositions = {
|
||||
const atree_node_atlas_positions = {
|
||||
"node_0": 0,
|
||||
"node_1": 1,
|
||||
"node_2": 2,
|
||||
|
@ -1319,13 +1409,27 @@ const atreeNodeAtlasPositions = {
|
|||
"node_assassin": 7,
|
||||
"node_shaman": 8
|
||||
}
|
||||
const atreeNodeTileSize = 32;
|
||||
const atreeNodeAtlasImg = make_elem("img", [], {src: "../media/atree/icons.png"});
|
||||
const atree_node_tile_size = 32;
|
||||
const atree_node_atlas_img = make_elem("img", [], {src: "../media/atree/icons.png", loaded: false});
|
||||
atree_node_atlas_img.addEventListener("load", () => {
|
||||
atree_node_atlas_img.loaded = true;
|
||||
for (const to_draw of atlas_to_draw.get(atree_node_atlas_img)) {
|
||||
draw_atlas_image(to_draw[0], atree_node_atlas_img, to_draw[1], to_draw[2]);
|
||||
}
|
||||
atlas_to_draw.set(atree_node_atlas_img, []);
|
||||
});
|
||||
|
||||
function drawAtlasImage(canvas, img, pos, tileSize) {
|
||||
const atlas_to_draw = new Map();
|
||||
atlas_to_draw.set(atree_connector_atlas_img, []);
|
||||
atlas_to_draw.set(atree_node_atlas_img, []);
|
||||
function draw_atlas_image(canvas, img, pos, tile_size) {
|
||||
if (!img.loaded) {
|
||||
atlas_to_draw.get(img).push([canvas, pos, tile_size]);
|
||||
return;
|
||||
}
|
||||
let ctx = canvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, tileSize, tileSize);
|
||||
ctx.drawImage(img, tileSize * pos[0], tileSize * pos[1], tileSize, tileSize, 0, 0, tileSize, tileSize);
|
||||
ctx.clearRect(0, 0, tile_size, tile_size);
|
||||
ctx.drawImage(img, tile_size * pos[0], tile_size * pos[1], tile_size, tile_size, 0, 0, tile_size, tile_size);
|
||||
}
|
||||
|
||||
// draw the connector onto the screen
|
||||
|
@ -1335,7 +1439,7 @@ function atree_render_connection(atree_connectors_map) {
|
|||
let connector_elem = connector_info.connector;
|
||||
set_connector_type(connector_info);
|
||||
connector_info.highlight = [0, 0, 0, 0];
|
||||
drawAtlasImage(connector_elem, atreeConnectorAtlasImg, atreeConnectorAtlasPositions[connector_info.type]["0000"], atreeConnectorTileSize);
|
||||
draw_atlas_image(connector_elem, atree_connector_atlas_img, atree_connector_atlas_positions[connector_info.type]["0000"], atree_connector_tile_size);
|
||||
let target_elem = document.getElementById("atree-row-" + i.split(",")[0]).children[i.split(",")[1]];
|
||||
if (target_elem.children.length != 0) {
|
||||
// janky special case... sometimes the ability tree tries to draw a link on top of a node...
|
||||
|
@ -1387,16 +1491,15 @@ function atree_set_edge(atree_connectors_map, parent, child, state) {
|
|||
for (let i = 0; i < 4; i++) {
|
||||
render += highlight_state[i] === 0 ? "0" : "1";
|
||||
}
|
||||
drawAtlasImage(connector_elem, atreeConnectorAtlasImg, atreeConnectorAtlasPositions[ctype][render], atreeConnectorTileSize);
|
||||
// connector_elem.style.backgroundPosition = atlasBGPositionCalc(atreeConnectorAtlasPositions[ctype][render], atreeConnectorAtlasSize);
|
||||
draw_atlas_image(connector_elem, atree_connector_atlas_img, atree_connector_atlas_positions[ctype][render], atree_connector_tile_size);
|
||||
continue;
|
||||
} else {
|
||||
// lol bad overloading, [0] is just the whole state
|
||||
highlight_state[0] += state_delta;
|
||||
if (highlight_state[0] > 0) {
|
||||
drawAtlasImage(connector_elem, atreeConnectorAtlasImg, atreeConnectorAtlasPositions[ctype][ctype], atreeConnectorTileSize);
|
||||
draw_atlas_image(connector_elem, atree_connector_atlas_img, atree_connector_atlas_positions[ctype][ctype], atree_connector_tile_size);
|
||||
} else {
|
||||
drawAtlasImage(connector_elem, atreeConnectorAtlasImg, atreeConnectorAtlasPositions[ctype]["0000"], atreeConnectorTileSize);
|
||||
draw_atlas_image(connector_elem, atree_connector_atlas_img, atree_connector_atlas_positions[ctype]["0000"], atree_connector_tile_size);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
1
js/builder/atree_constants_min.js
Normal file
1
js/builder/atree_constants_min.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -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'); }
|
||||
|
||||
|
@ -601,8 +603,9 @@ class SpellDamageCalcNode extends ComputeNode {
|
|||
// TODO: move preprocessing to separate node/node chain
|
||||
for (const part of spell_parts) {
|
||||
let spell_result;
|
||||
const part_id = spell.base_spell + '.' + part.name
|
||||
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 = {
|
||||
type: "damage",
|
||||
normal_min: results[2].map(x => x[0]),
|
||||
|
@ -615,6 +618,9 @@ class SpellDamageCalcNode extends ComputeNode {
|
|||
} else if ('power' in part) {
|
||||
// TODO: wynn2 formula
|
||||
let _heal_amount = (part.power * getDefenseStats(stats)[0] * (stats.get('healPct')/100));
|
||||
if (stats.has('healPct:'+part_id)) {
|
||||
_heal_amount *= 1+(stats.get('healPct:'+part_id)/100);
|
||||
}
|
||||
spell_result = {
|
||||
type: "heal",
|
||||
heal_amount: _heal_amount
|
||||
|
@ -715,7 +721,7 @@ class BuildDisplayNode extends ComputeNode {
|
|||
// TODO: move weapon out?
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -887,6 +893,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();
|
||||
|
@ -1065,13 +1082,13 @@ function builder_graph_init() {
|
|||
// Phase 3/3: Set up atree stuff.
|
||||
|
||||
let class_node = new PlayerClassNode('builder-class').link_to(build_node);
|
||||
// These two are defined in `atree.js`
|
||||
// These two are defined in `builder/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');
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -188,7 +188,7 @@ function getCustomFromHash(hash) {
|
|||
|
||||
/** 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 attackSpeeds from build.js.
|
||||
* @dep Requires the use of attackSpeeds from `builder/build.js`.
|
||||
*/
|
||||
class Custom {
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
@ -126,15 +124,18 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
|
|||
// These do not count raw damage. I think. Easy enough to change
|
||||
let total_min = 0;
|
||||
let total_max = 0;
|
||||
let save_prop = [];
|
||||
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 damageBoost = 1 + skill_boost[i] + static_boost
|
||||
+ ((stats.get(damage_specific) + stats.get(damage_elements[i]+'DamPct')) /100);
|
||||
damages[i][0] *= Math.max(damageBoost, 0);
|
||||
damages[i][1] *= Math.max(damageBoost, 0);
|
||||
// Collect total damage post %boost
|
||||
total_min += damages[i][0];
|
||||
total_max += damages[i][1];
|
||||
}
|
||||
|
||||
let total_elem_min = total_min - damages[0][0];
|
||||
|
@ -144,6 +145,7 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
|
|||
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');
|
||||
for (let i in damages) {
|
||||
let save_obj = save_prop[i];
|
||||
let damages_obj = damages[i];
|
||||
let damage_prefix = damage_elements[i] + specific_boost_str;
|
||||
// Normie raw
|
||||
|
@ -157,22 +159,22 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
|
|||
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
|
||||
if (total_min === 0) {
|
||||
min_boost += (damages_obj[1] / total_max) * prop_raw;
|
||||
min_boost += (save_obj[1] / total_max) * prop_raw;
|
||||
}
|
||||
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
|
||||
// TODO: compute actual chance of 0 damage. For now we just copy max ratio
|
||||
if (total_elem_min === 0) {
|
||||
min_boost += (damages_obj[1] / total_elem_max) * rainbow_raw;
|
||||
min_boost += (save_obj[1] / total_elem_max) * rainbow_raw;
|
||||
}
|
||||
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[1] += max_boost * total_convert;
|
||||
|
@ -256,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
|
||||
}
|
||||
|
||||
|
||||
|
|
422
js/display.js
422
js/display.js
|
@ -72,7 +72,8 @@ function displayBuildStats(parent_id,build,command_group,stats){
|
|||
let active_elem;
|
||||
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"];
|
||||
|
||||
for (const command of display_commands) {
|
||||
|
@ -997,9 +998,9 @@ function displayFixedID(active, id, value, elemental_format, style) {
|
|||
}
|
||||
}
|
||||
|
||||
function displayPoisonDamage(overallparent_elem, build) {
|
||||
function displayPoisonDamage(overallparent_elem, statMap) {
|
||||
overallparent_elem.textContent = "";
|
||||
if (build.statMap.get('poison') <= 0) {
|
||||
if (statMap.get('poison') <= 0) {
|
||||
overallparent_elem.style = "display: none";
|
||||
return;
|
||||
}
|
||||
|
@ -1013,7 +1014,7 @@ function displayPoisonDamage(overallparent_elem, build) {
|
|||
title_elemavg.append(make_elem('span', [], { textContent: "Poison Stats" }));
|
||||
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.ceil(statMap.get("poison") * (1+skillPointsToPercentage(statMap.get('str'))) * (statMap.get("poisonPct"))/100 /3);
|
||||
|
||||
let overallpoisonDamage = make_elem("p");
|
||||
overallpoisonDamage.append(
|
||||
|
@ -1392,7 +1393,7 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -1521,417 +1522,6 @@ function displaySpellDamage(parent_elem, _overallparent_elem, stats, spell, spel
|
|||
_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) {
|
||||
//up and down arrow - done ugly
|
||||
let arrow = make_elem("img", [], { id: "arrow_" + elem.id, src: "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png" });
|
||||
|
|
|
@ -40,6 +40,11 @@ let idPrefixes = {"displayName": "",
|
|||
"hprRaw":"Health Regen Raw: ",
|
||||
"hprPct":"Health Regen %: ",
|
||||
"sdRaw":"Raw Spell Damage: ",
|
||||
"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 %: ",
|
||||
"mdRaw":"Raw Melee Damage: ",
|
||||
"mdPct":"Melee Damage %: ",
|
||||
|
@ -70,7 +75,7 @@ let idPrefixes = {"displayName": "",
|
|||
"spRaw3":"3rd Spell Cost Raw: ",
|
||||
"spPct4":"4th Spell Cost %: ",
|
||||
"spRaw4":"4th Spell Cost Raw: ",
|
||||
"rainbowRaw":"Rainbow Spell Damage Raw: ",
|
||||
"rSdRaw":"Rainbow Spell Damage Raw: ",
|
||||
"sprint":"Sprint Bonus: ",
|
||||
"sprintReg":"Sprint Regen Bonus: ",
|
||||
"jh":"Jump Height: ",
|
||||
|
@ -117,6 +122,11 @@ let idSuffixes = {"displayName": "",
|
|||
"hprRaw":"",
|
||||
"hprPct":"%",
|
||||
"sdRaw":"",
|
||||
"eSdRaw":"",
|
||||
"tSdRaw":"",
|
||||
"wSdRaw":"",
|
||||
"fSdRaw":"",
|
||||
"aSdRaw":"",
|
||||
"sdPct":"%",
|
||||
"mdRaw":"",
|
||||
"mdPct":"%",
|
||||
|
@ -309,7 +319,8 @@ let sq2_item_display_commands = [
|
|||
"str", "dex", "int", "def", "agi",
|
||||
"hpBonus",
|
||||
"hprRaw", "hprPct",
|
||||
"sdRaw", "sdPct",
|
||||
"sdRaw", "eSdRaw", "tSdRaw", "wSdRaw", "fSdRaw", "aSdRaw",
|
||||
"sdPct",
|
||||
"mdRaw", "mdPct",
|
||||
"mr", "ms",
|
||||
"ref", "thorns",
|
||||
|
@ -323,7 +334,7 @@ let sq2_item_display_commands = [
|
|||
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
|
||||
"!elemental",
|
||||
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
|
||||
"rainbowRaw",
|
||||
"rSdRaw",
|
||||
"sprint", "sprintReg",
|
||||
"jh",
|
||||
"xpb", "lb", "lq",
|
||||
|
|
|
@ -234,7 +234,7 @@ function stringify(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 itemListFooter = document.getElementById('item-list-footer');
|
||||
|
||||
|
@ -328,7 +328,11 @@ function init_items2() {
|
|||
for (let i = 0; i < searchMax; i++) {
|
||||
const result = searchResults[i];
|
||||
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) {
|
||||
const sortKeyListContainer = document.createElement('div');
|
||||
sortKeyListContainer.classList.add('row');
|
||||
|
@ -391,4 +395,7 @@ function init_items2() {
|
|||
.addEventListener('mousedown', e => scrollTo({ top: 0, behavior: 'smooth' }));
|
||||
}
|
||||
|
||||
load_init(init_items2);
|
||||
(async function() {
|
||||
await Promise.resolve(load_init());
|
||||
init_items_adv();
|
||||
})();
|
|
@ -1,4 +1,4 @@
|
|||
const DB_VERSION = 104;
|
||||
const DB_VERSION = 108;
|
||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
||||
|
||||
let db;
|
||||
|
|
|
@ -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"]:
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"category": "armor",
|
||||
"displayName": "Keratoconus",
|
||||
"bonusThunderDamage": -40,
|
||||
"bonusWaterDamage": 30,
|
||||
"bonusWaterDefense": 30,
|
||||
"bonusAirDamage": 35,
|
||||
"healthRegen": -50,
|
||||
"manaRegen": 10,
|
||||
|
@ -67,7 +67,7 @@
|
|||
"bonusEarthDamage": 24,
|
||||
"bonusAirDamage": 32,
|
||||
"attackSpeedBonus": -1,
|
||||
"manaRegen": 8,
|
||||
"manaRegen": 10,
|
||||
"reflection": 48,
|
||||
"spellCostRaw1": -3,
|
||||
"identified": false,
|
||||
|
@ -967,7 +967,6 @@
|
|||
"spellDamage": 400,
|
||||
"manaSteal": 30,
|
||||
"reflection": 20,
|
||||
"spellCostRaw3": 6,
|
||||
"xpBonus": 30,
|
||||
"identified": false
|
||||
},
|
||||
|
@ -2323,7 +2322,7 @@
|
|||
"bonusAirDefense": 20,
|
||||
"speed": 30,
|
||||
"sprint": 15,
|
||||
"spellCostRaw1": -5,
|
||||
"spellCostRaw1": -3,
|
||||
"xpBonus": 10,
|
||||
"identified": true
|
||||
},
|
||||
|
@ -2942,7 +2941,7 @@
|
|||
{"name":"Infused Hive Relik","type":"relik","level":100,"tier":"Legendary","sockets":5,"majorIds":[],"quest":"The Qira Hive","damage":"260-290","earthDamage":"0-0","thunderDamage":"0-0","waterDamage":"0-0","fireDamage":"0-0","airDamage":"0-0","attackSpeed":"FAST","category":"weapon","displayName":"Infused Hive Relik","bonusEarthDamage":20,"bonusThunderDamage":20,"bonusWaterDamage":20,"bonusFireDamage":20,"bonusAirDamage":20,"manaRegen":5,"manaSteal":5,"identified":true},
|
||||
{"name":"Tragedy","type":"wand","level":74,"tier":"Rare","sockets":2,"majorIds":[],"intelligence":25,"strengthPoints":-10,"damage":"50-65","earthDamage":"0-0","thunderDamage":"0-0","waterDamage":"40-85","fireDamage":"0-0","airDamage":"0-0","attackSpeed":"NORMAL","category":"weapon","displayName":"Tragedy","bonusWaterDamage":50,"healthBonus":-100,"manaRegen":-5,"manaSteal":-5,"identified":true},
|
||||
{"name":"Scytodidae","type":"bow","level":77,"tier":"Rare","sockets":2,"majorIds":[],"intelligence":40,"defensePoints":-20,"damage":"180-230","earthDamage":"0-0","thunderDamage":"0-0","waterDamage":"130-175","fireDamage":"0-0","airDamage":"0-0","attackSpeed":"SLOW","category":"weapon","displayName":"Scytodidae","spellDamageRaw":130,"bonusFireDefense":-10,"manaRegen":10,"manaSteal":10,"identified":true},
|
||||
{"name":"Hard Light","type":"relik","level":96,"tier":"Rare","sockets":3,"majorIds":[],"strength":23,"dexterity":23,"intelligence":23,"defense":23,"agility":23,"strengthPoints":17,"dexterityPoints":17,"intelligencePoints":17,"defensePoints":17,"agilityPoints":17,"damage":"0-0","earthDamage":"51-57","thunderDamage":"51-57","waterDamage":"51-57","fireDamage":"51-57","airDamage":"51-57","attackSpeed":"FAST","category":"weapon","displayName":"Hard Light","damageBonus":15,"spellDamage":15,"bonusEarthDefense":-25,"bonusThunderDefense":-25,"bonusWaterDefense":-25,"bonusFireDefense":-25,"bonusAirDefense":-25,"manaRegen":5,"manaSteal":5,"identified":false},
|
||||
{"name":"Boreal-Patterned Aegis","type":"chestplate","level":100,"tier":"Legendary","sockets":2,"majorIds":[],"quest":"The Qira Hive","dexterity":40,"intelligence":40,"defense":40,"strengthPoints":-30,"agilityPoints":-30,"health":3800,"thunderDefense":200,"waterDefense":200,"fireDefense":200,"category":"armor","displayName":"Anima-Infused Cuirass","bonusThunderDamage":20,"bonusWaterDamage":20,"bonusFireDamage":20,"bonusThunderDefense":15,"bonusWaterDefense":15,"bonusFireDefense":15,"manaRegen":10,"spellCostRaw1":-5,"spellCostRaw3":-5,"spellCostRaw4":-5,"identified":true}
|
||||
{"name":"Hard Light","type":"relik","level":96,"tier":"Rare","sockets":3,"majorIds":[],"strength":23,"dexterity":23,"intelligence":23,"defense":23,"agility":23,"strengthPoints":17,"dexterityPoints":17,"intelligencePoints":17,"defensePoints":17,"agilityPoints":17,"damage":"0-0","earthDamage":"51-57","thunderDamage":"51-57","waterDamage":"51-57","fireDamage":"51-57","airDamage":"51-57","attackSpeed":"FAST","category":"weapon","displayName":"Hard Light","damageBonus":15,"spellDamage":15,"bonusEarthDefense":-25,"bonusThunderDefense":-25,"bonusWaterDefense":-25,"bonusFireDefense":-25,"bonusAirDefense":-25,"manaRegen":9,"manaSteal":9,"identified":false},
|
||||
{"name":"Boreal-Patterned Aegis","type":"chestplate","level":100,"tier":"Legendary","sockets":2,"majorIds":[],"quest":"The Qira Hive","dexterity":40,"intelligence":40,"defense":40,"strengthPoints":-30,"agilityPoints":-30,"health":3800,"thunderDefense":200,"waterDefense":200,"fireDefense":200,"category":"armor","displayName":"Anima-Infused Cuirass","bonusThunderDamage":20,"bonusWaterDamage":20,"bonusFireDamage":20,"bonusThunderDefense":15,"bonusWaterDefense":15,"bonusFireDefense":15,"manaRegen":10,"spellCostRaw1":-5,"spellCostRaw3":-5,"spellCostRaw4":-5,"identified":true, "set": "Master Hive"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -79,8 +79,6 @@ for item in old_items:
|
|||
if rem >= 5:
|
||||
val += 1
|
||||
item[k] = val
|
||||
if item['name'] == "Gale's Sight":
|
||||
print(item)
|
||||
items.append(item)
|
||||
#print(f'Unknown old item: {item["name"]}!!!')
|
||||
#old_items_map[item["name"]] = item
|
||||
|
|
Loading…
Reference in a new issue