Merge branch 'atree' of https://github.com/hppeng-wynn/hppeng-wynn.github.io into mobile_ui_tweaks
|
@ -18,7 +18,7 @@
|
|||
-->
|
||||
|
||||
<title>WynnBuilder</title>
|
||||
<link rel="icon" href="./media/memes/agony.png">
|
||||
<link rel="icon" href="../media/icons/new/builder.png" type="image/icon type">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||
|
||||
|
@ -45,12 +45,15 @@
|
|||
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
|
||||
<a href = "../map/"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
|
||||
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
|
||||
<a onclick = "document.querySelector('#search-container').style.display = '';"><img src="../media/icons/new/searcher.png" alt="" title="Item Search"><b>WynnAtlas Mini</b></a>
|
||||
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
|
||||
<hr/>
|
||||
<a href = "https://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">
|
||||
<!-- 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="col-xl-6">
|
||||
<div class="row row-cols-1 mb-3 gy-4">
|
||||
|
@ -407,6 +410,8 @@
|
|||
</div>
|
||||
<div class="col text-center">
|
||||
<div id="summary-box"></div>
|
||||
<div id="err-box"></div>
|
||||
<div id="stack-box"></div>
|
||||
<div id="str-warnings"></div>
|
||||
<div id="dex-warnings"></div>
|
||||
<div id="int-warnings"></div>
|
||||
|
@ -428,7 +433,7 @@
|
|||
</div>
|
||||
<div class = "col-3 py-2">
|
||||
<button class = "col-auto button rounded scaled-font fw-bold text-light dark-5" id = "toggle-atree" onclick = "toggle_tab('atree-dropdown'); toggleButton('toggle-atree')">
|
||||
Show Ability Tree
|
||||
Edit Abilities
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-3 py-2">
|
||||
|
@ -617,10 +622,14 @@
|
|||
</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: 500px; overflow-y: auto;">
|
||||
<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>
|
||||
<div class="col mx-auto" id="atree-active">
|
||||
<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="overflow-y: auto;" id="atree-active">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -965,7 +974,12 @@
|
|||
</div>
|
||||
<div class="col-auto p-1">
|
||||
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
|
||||
War Scream (+10%)
|
||||
War Scream
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto p-1">
|
||||
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="ragnarokkr-boost" onclick="update_boosts('ragnarokkr-boost')">
|
||||
Ragnarokkr (+30%)
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto p-1">
|
||||
|
@ -979,8 +993,8 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="col-auto p-1">
|
||||
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="bash-boost" onclick="update_boosts('bash-boost')">
|
||||
Bash (+50%)
|
||||
<button class="button-boost w-100 border-0 text-white dark-8u dark-shadow-sm" id="fortitude-boost" onclick="update_boosts('fortitude-boost')">
|
||||
Fortitude (+60%)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1045,11 +1059,6 @@
|
|||
<div class="col eDam">
|
||||
Rage (Passive)
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type = "range" class = "e_slider" id = "str_boost_armor" name = "str-boost-armor" autocomplete = "off" min = '0' max = '400' value = '0' step = '1' onchange = "update_armor_powder_specials('str_boost_armor')">
|
||||
<input type="text" id="str_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
|
||||
<label id = "str_boost_armor_label" for="str-boost-armor">% Earth Dmg Boost: 0</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" id="dex-boost" style="display: none;">
|
||||
|
@ -1089,11 +1098,6 @@
|
|||
<div class="col tDam">
|
||||
Kill Streak (Passive)
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type = "range" class = "t_slider" id = "dex_boost_armor" name = "dex-boost-armor" autocomplete = "off" min = '0' max = '200' value = '0' step = '1' onchange = "update_armor_powder_specials('dex_boost_armor')">
|
||||
<input type="text" id="dex_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
|
||||
<label id = "dex_boost_armor_label" for="dex-boost-armor">% Thunder Dmg Boost: 0</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" id="int-boost">
|
||||
|
@ -1133,11 +1137,6 @@
|
|||
<div class="col wDam">
|
||||
Concentration (Passive)
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type = "range" class = "w_slider" id = "int_boost_armor" name = "dex-boost-armor" autocomplete = "off" min = '0' max = '150' value = '0' step = '1' onchange = "update_armor_powder_specials('int_boost_armor')">
|
||||
<input type="text" id="int_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
|
||||
<label id = "int_boost_armor_label" for="dex-boost-armor">% Water Dmg Boost: 0</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" id="def-boost" style="display: none;">
|
||||
|
@ -1177,11 +1176,6 @@
|
|||
<div class="col fDam">
|
||||
Endurance (Passive)
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type = "range" class = "f_slider" id = "def_boost_armor" name = "def-boost-armor" autocomplete = "off" min = '0' max = '200' value = '0' step = '1' onchange = "update_armor_powder_specials('def_boost_armor')">
|
||||
<input type="text" id="def_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
|
||||
<label id = "def_boost_armor_label" for="def-boost-armor">% Fire Dmg Boost: 0</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" id="agi-boost" style="display: none;">
|
||||
|
@ -1221,11 +1215,6 @@
|
|||
<div class="col aDam">
|
||||
Dodge (Passive)
|
||||
</div>
|
||||
<div class="col">
|
||||
<input type = "range" class = "a_slider" id = "agi_boost_armor" name = "agi-boost-armor" autocomplete = "off" min = '0' max = '150' value = '0' step = '1' onchange = "update_armor_powder_specials('agi_boost_armor')">
|
||||
<input type="text" id="agi_boost_armor_prev" autocomplete = "off" value="0" style = "display:none;">
|
||||
<label id = "agi_boost_armor_label" for="agi-boost-armor">% Air Dmg Boost: 0</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1263,21 +1252,10 @@
|
|||
<div class = "col">
|
||||
<div class = "col spell-display dark-5 rounded dark-shadow py-2 border border-dark" id="build-poison-stats">poison</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell0-infoAvg">spell1</div>
|
||||
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell0-info" style="display: none;">Spell 1</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell1-infoAvg">spell2</div>
|
||||
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell1-info" style="display: none;">Spell 2</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell2-infoAvg">spell3</div>
|
||||
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell2-info" style="display: none;">Spell 3</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell3-infoAvg">spell4</div>
|
||||
<div class = "col spell-display dark-5 rounded dark-shadow py-2" id = "spell3-info" style="display: none;">Spell 4</div>
|
||||
<div id="all-spells-display" class="row row-cols-1 gy-3 text-center scaled-font pe-0">
|
||||
<div class = "col pe-0">
|
||||
<div class = "col spell-display spell-expand dark-5 rounded dark-shadow pt-2 border border-dark" id="spell0-infoAvg">Input a weapon to see abilities!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "spell-display dark-5 rounded dark-shadow py-2 border border-dark" id = "powder-special-stats"></div>
|
||||
|
@ -1329,7 +1307,7 @@
|
|||
<div class="col-12 dark-5 scaled-font">
|
||||
<footer class="text-center">
|
||||
<div id="header2">
|
||||
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
|
||||
<p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, and <b>reschan</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
|
||||
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
|
||||
</div>
|
||||
<div id="credits">
|
||||
|
@ -1393,7 +1371,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/macy@2"></script>
|
||||
<script type="text/javascript" src="../js/utils.js"></script>
|
||||
|
@ -1404,15 +1381,9 @@
|
|||
<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/query.js"></script>
|
||||
<script type="text/javascript" src="../js/query_2.js"></script>
|
||||
<script type="text/javascript" src="../js/load.js"></script>
|
||||
<script type="text/javascript" src="../js/load_ing.js"></script>
|
||||
<script type="text/javascript" src="../js/load_tome.js"></script>
|
||||
|
@ -1424,9 +1395,6 @@
|
|||
<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/expr_parser.js"></script>
|
||||
<script type="text/javascript" src="../js/items.js"></script>
|
||||
<script type="text/javascript" src="../js/sq2items.js"></script>
|
||||
<script type="text/javascript" src="../js/optimize.js"></script>
|
||||
|
||||
<div id="graph_body" style="max-width: 100%; height: 100vh">
|
||||
|
@ -1434,6 +1402,7 @@
|
|||
<a id="saveLink">savelink</a>
|
||||
</div>
|
||||
<script src="https://d3js.org/d3.v7.js"></script>
|
||||
<script type="text/javascript" src="../js/render_compute_graph.js"></script>
|
||||
<script type="text/javascript" src="../js/debug/render_compute_graph.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
1418
builder/index.html
1315
builder/index_full.html
Normal file
311
clean.json
|
@ -76,6 +76,7 @@
|
|||
"spRaw1-base": -5,
|
||||
"category": "armor",
|
||||
"displayName": "Anaerobic",
|
||||
"slots": 4,
|
||||
"hp": 3850,
|
||||
"wDef": -150,
|
||||
"aDef": 100,
|
||||
|
@ -118,10 +119,10 @@
|
|||
"classReq": "Shaman",
|
||||
"dexReq": 55,
|
||||
"defReq": 50,
|
||||
"hprPct": -204,
|
||||
"ms": 13,
|
||||
"atkTier": 1,
|
||||
"hpBonus": -1150,
|
||||
"hprRaw": -204,
|
||||
"sdRaw": 184,
|
||||
"spRaw1": -7,
|
||||
"id": 3614
|
||||
|
@ -336,7 +337,7 @@
|
|||
"poison": 600,
|
||||
"thorns": 25,
|
||||
"slots": 3,
|
||||
"hp": 3450,
|
||||
"hp": 4350,
|
||||
"fDef": 120,
|
||||
"wDef": 120,
|
||||
"aDef": 200,
|
||||
|
@ -393,8 +394,8 @@
|
|||
"fDef": 50,
|
||||
"wDef": 50,
|
||||
"lvl": 101,
|
||||
"dexReq": 45,
|
||||
"intReq": 45,
|
||||
"defReq": 45,
|
||||
"hprPct": 17,
|
||||
"sdPct": 5,
|
||||
"str": -3,
|
||||
|
@ -651,6 +652,7 @@
|
|||
"tier": "Legendary",
|
||||
"majorIds": [],
|
||||
"quest": "The Qira Hive",
|
||||
"set": "Master Hive",
|
||||
"category": "accessory",
|
||||
"displayName": "Prowess",
|
||||
"hp": 425,
|
||||
|
@ -873,6 +875,7 @@
|
|||
"type": "helmet",
|
||||
"tier": "Legendary",
|
||||
"quest": "The Qira Hive",
|
||||
"set": "Master Hive",
|
||||
"category": "armor",
|
||||
"slots": 2,
|
||||
"hp": 3000,
|
||||
|
@ -1027,13 +1030,13 @@
|
|||
"mdPct": 40,
|
||||
"sdRaw": -40000,
|
||||
"spPct1": -40,
|
||||
"spRaw1": -40,
|
||||
"spRaw1": -400,
|
||||
"spPct2": -40,
|
||||
"spRaw2": -40,
|
||||
"spRaw2": -400,
|
||||
"spPct3": -40,
|
||||
"spRaw3": -40,
|
||||
"spRaw3": -400,
|
||||
"spPct4": -40,
|
||||
"spRaw4": -40,
|
||||
"spRaw4": -400,
|
||||
"id": 3021
|
||||
},
|
||||
{
|
||||
|
@ -1971,7 +1974,7 @@
|
|||
"type": "wand",
|
||||
"tier": "Mythic",
|
||||
"majorIds": [],
|
||||
"mr-base": -49,
|
||||
"mr-base": -45,
|
||||
"spRaw1-base": 4,
|
||||
"spRaw2-base": -299,
|
||||
"category": "weapon",
|
||||
|
@ -1988,7 +1991,7 @@
|
|||
"lvl": 99,
|
||||
"agiReq": 130,
|
||||
"hprPct": -200,
|
||||
"mr": -49,
|
||||
"mr": -45,
|
||||
"ref": 90,
|
||||
"agi": 20,
|
||||
"expd": 50,
|
||||
|
@ -2074,11 +2077,11 @@
|
|||
"basedps": 1350,
|
||||
"slots": 3,
|
||||
"nDam": "0-0",
|
||||
"fDam": "215-325",
|
||||
"wDam": "230-310",
|
||||
"aDam": "245-295",
|
||||
"tDam": "260-280",
|
||||
"eDam": "200-340",
|
||||
"fDam": "185-295",
|
||||
"wDam": "200-280",
|
||||
"aDam": "215-265",
|
||||
"tDam": "230-250",
|
||||
"eDam": "170-310",
|
||||
"atkSpd": "VERY_SLOW",
|
||||
"lvl": 96,
|
||||
"strReq": 45,
|
||||
|
@ -2086,9 +2089,9 @@
|
|||
"intReq": 45,
|
||||
"agiReq": 45,
|
||||
"defReq": 45,
|
||||
"mr": -4,
|
||||
"mr": -20,
|
||||
"sdPct": 30,
|
||||
"ms": -4,
|
||||
"ms": -20,
|
||||
"int": 50,
|
||||
"spPct1": -27,
|
||||
"spPct2": -27,
|
||||
|
@ -2177,6 +2180,62 @@
|
|||
"sprintReg": 43,
|
||||
"id": 3643
|
||||
},
|
||||
{
|
||||
"name": "Panic Zealot",
|
||||
"tier": "Fabled",
|
||||
"type": "relik",
|
||||
"material": "273:7",
|
||||
"majorIds": [
|
||||
"FURIOUS_EFFIGY"
|
||||
],
|
||||
"category": "weapon",
|
||||
"slots": 3,
|
||||
"lore": "They must know what you went through. They must suffer the same as you did.",
|
||||
"drop": "never",
|
||||
"restrict": "Untradable",
|
||||
"nDam": "46-60",
|
||||
"fDam": "0-0",
|
||||
"wDam": "0-0",
|
||||
"aDam": "43-63",
|
||||
"tDam": "0-0",
|
||||
"eDam": "0-0",
|
||||
"atkSpd": "SUPER_FAST",
|
||||
"lvl": 101,
|
||||
"agiReq": 85,
|
||||
"spd": 30,
|
||||
"atkTier": 3,
|
||||
"hpBonus": -5000,
|
||||
"tDamPct": -30,
|
||||
"spPct1": -100,
|
||||
"spPct2": -100,
|
||||
"spPct3": -100,
|
||||
"id": 3600
|
||||
},
|
||||
{
|
||||
"name": "Ambivalence",
|
||||
"tier": "Legendary",
|
||||
"material": "259:29",
|
||||
"category": "accessory",
|
||||
"drop": "never",
|
||||
"restrict": "Untradable",
|
||||
"fDef": 70,
|
||||
"aDef": 70,
|
||||
"tDef": 70,
|
||||
"lvl": 100,
|
||||
"dexReq": 40,
|
||||
"agiReq": 40,
|
||||
"defReq": 40,
|
||||
"sdPct": 250,
|
||||
"int": -100,
|
||||
"wDamPct": 50,
|
||||
"type": "necklace",
|
||||
"fixID": true,
|
||||
"spPct1": 130,
|
||||
"spPct2": 85,
|
||||
"spPct3": 130,
|
||||
"spPct4": 100,
|
||||
"id": 3618
|
||||
},
|
||||
{
|
||||
"name": "Dondasch",
|
||||
"tier": "Legendary",
|
||||
|
@ -2714,35 +2773,6 @@
|
|||
"dex": 30,
|
||||
"id": 19
|
||||
},
|
||||
{
|
||||
"name": "Panic Zealot",
|
||||
"tier": "Fabled",
|
||||
"type": "relik",
|
||||
"majorIds": [
|
||||
"FURIOUS_EFFIGY"
|
||||
],
|
||||
"category": "weapon",
|
||||
"slots": 3,
|
||||
"drop": "never",
|
||||
"restrict": "Untradable",
|
||||
"nDam": "46-60",
|
||||
"fDam": "0-0",
|
||||
"wDam": "0-0",
|
||||
"aDam": "43-63",
|
||||
"tDam": "0-0",
|
||||
"eDam": "0-0",
|
||||
"atkSpd": "SUPER_FAST",
|
||||
"lvl": 101,
|
||||
"agiReq": 85,
|
||||
"spd": 30,
|
||||
"atkTier": 3,
|
||||
"hpBonus": -5000,
|
||||
"tDamPct": -30,
|
||||
"spPct1": -70,
|
||||
"spPct2": -70,
|
||||
"spPct3": -70,
|
||||
"id": 3600
|
||||
},
|
||||
{
|
||||
"name": "Redrock Bandanna",
|
||||
"tier": "Rare",
|
||||
|
@ -8836,7 +8866,7 @@
|
|||
"def": 11,
|
||||
"hpBonus": -300,
|
||||
"spPct2": 25,
|
||||
"spPct4": -23,
|
||||
"spPct4": -24,
|
||||
"id": 359
|
||||
},
|
||||
{
|
||||
|
@ -11449,9 +11479,9 @@
|
|||
"def": -20,
|
||||
"atkTier": -1,
|
||||
"hprRaw": -200,
|
||||
"spPct2": -31,
|
||||
"spPct2": -32,
|
||||
"spPct3": -21,
|
||||
"spPct4": -23,
|
||||
"spPct4": -24,
|
||||
"id": 482
|
||||
},
|
||||
{
|
||||
|
@ -11521,7 +11551,7 @@
|
|||
"spd": -8,
|
||||
"hpBonus": 1750,
|
||||
"hprRaw": 125,
|
||||
"spPct1": -47,
|
||||
"spPct1": -48,
|
||||
"id": 490
|
||||
},
|
||||
{
|
||||
|
@ -14682,7 +14712,7 @@
|
|||
"aDamPct": 12,
|
||||
"tDamPct": 15,
|
||||
"fixID": true,
|
||||
"spPct3": -9,
|
||||
"spPct3": -10,
|
||||
"id": 629
|
||||
},
|
||||
{
|
||||
|
@ -16771,7 +16801,7 @@
|
|||
"aDamPct": 21,
|
||||
"fixID": true,
|
||||
"spPct2": -54,
|
||||
"spPct3": -33,
|
||||
"spPct3": -34,
|
||||
"id": 726
|
||||
},
|
||||
{
|
||||
|
@ -17184,7 +17214,7 @@
|
|||
"wDefPct": -45,
|
||||
"eDefPct": -55,
|
||||
"spPct2": -14,
|
||||
"spPct3": -9,
|
||||
"spPct3": -10,
|
||||
"id": 2264
|
||||
},
|
||||
{
|
||||
|
@ -17898,30 +17928,6 @@
|
|||
"fixID": true,
|
||||
"id": 776
|
||||
},
|
||||
{
|
||||
"name": "Ambivalence",
|
||||
"tier": "Legendary",
|
||||
"category": "accessory",
|
||||
"drop": "never",
|
||||
"restrict": "Untradable",
|
||||
"fDef": 70,
|
||||
"aDef": 70,
|
||||
"tDef": 70,
|
||||
"lvl": 100,
|
||||
"dexReq": 40,
|
||||
"agiReq": 40,
|
||||
"defReq": 40,
|
||||
"sdPct": 250,
|
||||
"int": -100,
|
||||
"wDamPct": 50,
|
||||
"type": "necklace",
|
||||
"fixID": true,
|
||||
"spPct1": 91,
|
||||
"spPct2": 61,
|
||||
"spPct3": 91,
|
||||
"spPct4": 70,
|
||||
"id": 3618
|
||||
},
|
||||
{
|
||||
"name": "The Evolved",
|
||||
"tier": "Legendary",
|
||||
|
@ -18098,12 +18104,13 @@
|
|||
"lvl": 100,
|
||||
"dexReq": 55,
|
||||
"defReq": 55,
|
||||
"sdPct": 61600,
|
||||
"mdPct": 14000,
|
||||
"ls": 700,
|
||||
"ms": 15,
|
||||
"int": -25,
|
||||
"spd": 10,
|
||||
"tSdRaw": 500,
|
||||
"fSdRaw": 500,
|
||||
"aSdRaw": 500,
|
||||
"fixID": true,
|
||||
"spRaw3": -10,
|
||||
"id": 781
|
||||
|
@ -18180,7 +18187,7 @@
|
|||
"mdRaw": 1997,
|
||||
"fixID": true,
|
||||
"spPct1": 23,
|
||||
"spPct2": 16,
|
||||
"spPct2": 15,
|
||||
"spPct3": 32,
|
||||
"spPct4": 23,
|
||||
"id": 782
|
||||
|
@ -20686,7 +20693,7 @@
|
|||
"spd": 10,
|
||||
"hpBonus": -300,
|
||||
"spPct1": 25,
|
||||
"spPct3": -23,
|
||||
"spPct3": -24,
|
||||
"jh": 1,
|
||||
"id": 898
|
||||
},
|
||||
|
@ -24363,9 +24370,9 @@
|
|||
"int": -40,
|
||||
"spd": 8,
|
||||
"spPct1": -17,
|
||||
"spPct2": -9,
|
||||
"spPct2": -10,
|
||||
"spPct3": -17,
|
||||
"spPct4": -9,
|
||||
"spPct4": -10,
|
||||
"id": 1070
|
||||
},
|
||||
{
|
||||
|
@ -25537,7 +25544,7 @@
|
|||
"aDamPct": -33,
|
||||
"tDamPct": -33,
|
||||
"eDamPct": -33,
|
||||
"spPct3": -22,
|
||||
"spPct3": -23,
|
||||
"id": 1143
|
||||
},
|
||||
{
|
||||
|
@ -28186,7 +28193,7 @@
|
|||
"str": 4,
|
||||
"dex": 4,
|
||||
"mdRaw": 52,
|
||||
"spPct1": 19,
|
||||
"spPct1": 18,
|
||||
"id": 1261
|
||||
},
|
||||
{
|
||||
|
@ -36455,7 +36462,7 @@
|
|||
"dex": 4,
|
||||
"sdRaw": 13,
|
||||
"mdRaw": 13,
|
||||
"spPct1": 19,
|
||||
"spPct1": 18,
|
||||
"id": 1658
|
||||
},
|
||||
{
|
||||
|
@ -37209,7 +37216,7 @@
|
|||
"def": 15,
|
||||
"fDamPct": 40,
|
||||
"wDamPct": 40,
|
||||
"spPct1": -22,
|
||||
"spPct1": -23,
|
||||
"id": 1689
|
||||
},
|
||||
{
|
||||
|
@ -44564,7 +44571,7 @@
|
|||
"fDamPct": 45,
|
||||
"wDamPct": -25,
|
||||
"eDamPct": -25,
|
||||
"spPct3": -22,
|
||||
"spPct3": -23,
|
||||
"id": 2063
|
||||
},
|
||||
{
|
||||
|
@ -47311,7 +47318,7 @@
|
|||
"ms": 5,
|
||||
"lb": 10,
|
||||
"str": -5,
|
||||
"spPct1": -24,
|
||||
"spPct1": -25,
|
||||
"id": 2182
|
||||
},
|
||||
{
|
||||
|
@ -50036,8 +50043,8 @@
|
|||
"agi": 7,
|
||||
"def": 7,
|
||||
"wDamPct": -15,
|
||||
"spPct1": -9,
|
||||
"spPct3": -9,
|
||||
"spPct1": -10,
|
||||
"spPct3": -10,
|
||||
"id": 2327
|
||||
},
|
||||
{
|
||||
|
@ -62443,7 +62450,7 @@
|
|||
"hprRaw": -40,
|
||||
"sdRaw": 95,
|
||||
"fixID": true,
|
||||
"spPct3": -9,
|
||||
"spPct3": -10,
|
||||
"id": 2915
|
||||
},
|
||||
{
|
||||
|
@ -66515,7 +66522,7 @@
|
|||
"hprRaw": 150,
|
||||
"spPct1": -7,
|
||||
"spPct2": -14,
|
||||
"spPct3": -9,
|
||||
"spPct3": -10,
|
||||
"id": 3094
|
||||
},
|
||||
{
|
||||
|
@ -70196,7 +70203,7 @@
|
|||
"lb": 25,
|
||||
"hprRaw": -101,
|
||||
"spPct2": -31,
|
||||
"spPct4": -9,
|
||||
"spPct4": -10,
|
||||
"jh": 2,
|
||||
"id": 3621
|
||||
},
|
||||
|
@ -70933,7 +70940,7 @@
|
|||
"fDamPct": -30,
|
||||
"wDamPct": 20,
|
||||
"tDefPct": -25,
|
||||
"spPct3": -23,
|
||||
"spPct3": -24,
|
||||
"id": 3309
|
||||
},
|
||||
{
|
||||
|
@ -71361,9 +71368,9 @@
|
|||
"spd": 10,
|
||||
"fDamPct": 10,
|
||||
"wDamPct": -15,
|
||||
"spPct1": -9,
|
||||
"spPct1": -10,
|
||||
"spPct3": -7,
|
||||
"spPct4": -9,
|
||||
"spPct4": -10,
|
||||
"id": 3327
|
||||
},
|
||||
{
|
||||
|
@ -72629,7 +72636,7 @@
|
|||
"int": -24,
|
||||
"agi": 12,
|
||||
"spd": 15,
|
||||
"spPct1": -9,
|
||||
"spPct1": -10,
|
||||
"spPct3": -7,
|
||||
"spPct4": -17,
|
||||
"id": 3385
|
||||
|
@ -73244,7 +73251,7 @@
|
|||
"ms": 5,
|
||||
"def": -12,
|
||||
"spd": 12,
|
||||
"spPct1": -22,
|
||||
"spPct1": -23,
|
||||
"id": 3413
|
||||
},
|
||||
{
|
||||
|
@ -76325,8 +76332,8 @@
|
|||
"sdPct": 14,
|
||||
"mdPct": 14,
|
||||
"jh": 1,
|
||||
"mr": -1,
|
||||
"ms": -1
|
||||
"mr": -5,
|
||||
"ms": -5
|
||||
},
|
||||
{
|
||||
"hprRaw": 50,
|
||||
|
@ -76354,19 +76361,19 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"ms": 1,
|
||||
"ms": 5,
|
||||
"dex": 2,
|
||||
"sdRaw": 15,
|
||||
"mdRaw": 5
|
||||
},
|
||||
{
|
||||
"ms": 1,
|
||||
"ms": 5,
|
||||
"dex": 6,
|
||||
"sdRaw": 35,
|
||||
"mdRaw": 10
|
||||
},
|
||||
{
|
||||
"ms": 3,
|
||||
"ms": 15,
|
||||
"dex": 20,
|
||||
"sdRaw": 65,
|
||||
"mdRaw": 70
|
||||
|
@ -76426,10 +76433,10 @@
|
|||
{},
|
||||
{},
|
||||
{
|
||||
"mr": 5,
|
||||
"mr": 25,
|
||||
"sdPct": 75,
|
||||
"mdPct": 75,
|
||||
"ms": 5,
|
||||
"ms": 25,
|
||||
"ls": 400,
|
||||
"hprRaw": 600
|
||||
}
|
||||
|
@ -76554,24 +76561,24 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"mr": -1,
|
||||
"ms": 2,
|
||||
"mr": -5,
|
||||
"ms": 10,
|
||||
"sdRaw": 40,
|
||||
"wDamPct": 5,
|
||||
"tDamPct": 5,
|
||||
"eDamPct": -34
|
||||
},
|
||||
{
|
||||
"mr": -2,
|
||||
"ms": 4,
|
||||
"mr": -10,
|
||||
"ms": 20,
|
||||
"sdRaw": 115,
|
||||
"wDamPct": 10,
|
||||
"tDamPct": 10,
|
||||
"eDamPct": -67
|
||||
},
|
||||
{
|
||||
"mr": -3,
|
||||
"ms": 6,
|
||||
"mr": -15,
|
||||
"ms": 30,
|
||||
"sdRaw": 230,
|
||||
"wDamPct": 32,
|
||||
"tDamPct": 32,
|
||||
|
@ -76608,7 +76615,7 @@
|
|||
"spRegen": 15
|
||||
},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"sdPct": 25,
|
||||
"mdPct": 25,
|
||||
"xpb": 50,
|
||||
|
@ -76694,7 +76701,7 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"xpb": 40,
|
||||
"def": 25,
|
||||
"fDamPct": 20,
|
||||
|
@ -76838,21 +76845,21 @@
|
|||
{},
|
||||
{
|
||||
"hprPct": -10,
|
||||
"mr": 1,
|
||||
"mr": 5,
|
||||
"sdPct": 6,
|
||||
"ref": 10,
|
||||
"thorns": 8
|
||||
},
|
||||
{
|
||||
"hprPct": -20,
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"sdPct": 14,
|
||||
"ref": 35,
|
||||
"thorns": 24
|
||||
},
|
||||
{
|
||||
"hprPct": -30,
|
||||
"mr": 4,
|
||||
"mr": 20,
|
||||
"sdPct": 30,
|
||||
"ref": 75,
|
||||
"thorns": 70
|
||||
|
@ -76868,7 +76875,7 @@
|
|||
{},
|
||||
{
|
||||
"mdPct": 30,
|
||||
"ms": 2,
|
||||
"ms": 10,
|
||||
"spd": 25,
|
||||
"spPct2": -40
|
||||
}
|
||||
|
@ -76892,46 +76899,46 @@
|
|||
"lb": 5
|
||||
},
|
||||
{
|
||||
"mr": 1,
|
||||
"mr": 5,
|
||||
"xpb": 10,
|
||||
"lb": 10,
|
||||
"spRaw2": -1,
|
||||
"spRaw2": -5,
|
||||
"hpBonus": 125
|
||||
},
|
||||
{
|
||||
"mr": 1,
|
||||
"mr": 5,
|
||||
"xpb": 15,
|
||||
"lb": 15,
|
||||
"spRaw2": -1,
|
||||
"spRaw2": -5,
|
||||
"hpBonus": 425
|
||||
},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"xpb": 35,
|
||||
"lb": 35,
|
||||
"hpBonus": 1325,
|
||||
"spRaw2": -1,
|
||||
"spRaw4": -1
|
||||
"spRaw2": -5,
|
||||
"spRaw4": -5
|
||||
},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"xpb": 55,
|
||||
"lb": 55,
|
||||
"hpBonus": 2575,
|
||||
"spRaw2": -1,
|
||||
"spRaw4": -1
|
||||
"spRaw2": -5,
|
||||
"spRaw4": -5
|
||||
},
|
||||
{
|
||||
"mr": 3,
|
||||
"mr": 15,
|
||||
"xpb": 80,
|
||||
"lb": 80,
|
||||
"hpBonus": 4450,
|
||||
"spRaw1": -1,
|
||||
"spRaw2": -1,
|
||||
"spRaw4": -1
|
||||
"spRaw1": -5,
|
||||
"spRaw2": -5,
|
||||
"spRaw4": -5
|
||||
},
|
||||
{
|
||||
"mr": 4,
|
||||
"mr": 20,
|
||||
"xpb": 100,
|
||||
"lb": 100,
|
||||
"str": 15,
|
||||
|
@ -76940,10 +76947,10 @@
|
|||
"agi": 15,
|
||||
"def": 15,
|
||||
"hpBonus": 8270,
|
||||
"spRaw1": -1,
|
||||
"spRaw2": -1,
|
||||
"spRaw3": -1,
|
||||
"spRaw4": -1
|
||||
"spRaw1": -5,
|
||||
"spRaw2": -5,
|
||||
"spRaw3": -5,
|
||||
"spRaw4": -5
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -77007,7 +77014,7 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"sdPct": 15,
|
||||
"mdPct": -15,
|
||||
"sdRaw": 30,
|
||||
|
@ -77275,7 +77282,7 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"mdPct": -24,
|
||||
"int": 5,
|
||||
"wDamPct": 10,
|
||||
|
@ -77283,7 +77290,7 @@
|
|||
"wDefPct": 16
|
||||
},
|
||||
{
|
||||
"mr": 5,
|
||||
"mr": 25,
|
||||
"mdPct": -54,
|
||||
"int": 15,
|
||||
"wDamPct": 20,
|
||||
|
@ -77291,7 +77298,7 @@
|
|||
"wDefPct": 36
|
||||
},
|
||||
{
|
||||
"mr": 8,
|
||||
"mr": 40,
|
||||
"mdPct": -90,
|
||||
"int": 25,
|
||||
"wDamPct": 40,
|
||||
|
@ -77317,7 +77324,7 @@
|
|||
"aDefPct": 20,
|
||||
"tDefPct": 20,
|
||||
"eDefPct": 20,
|
||||
"ms": 1
|
||||
"ms": 5
|
||||
},
|
||||
{
|
||||
"xpb": 50,
|
||||
|
@ -77327,7 +77334,7 @@
|
|||
"aDefPct": 50,
|
||||
"tDefPct": 50,
|
||||
"eDefPct": 50,
|
||||
"ms": 2
|
||||
"ms": 10
|
||||
},
|
||||
{
|
||||
"xpb": 75,
|
||||
|
@ -77338,7 +77345,7 @@
|
|||
"tDefPct": 100,
|
||||
"eDefPct": 100,
|
||||
"sdPct": 40,
|
||||
"ms": 6
|
||||
"ms": 30
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -77352,7 +77359,7 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"mr": 1,
|
||||
"mr": 5,
|
||||
"sdPct": -10,
|
||||
"mdPct": -15,
|
||||
"def": 7,
|
||||
|
@ -77361,7 +77368,7 @@
|
|||
"aDamPct": 15
|
||||
},
|
||||
{
|
||||
"mr": 3,
|
||||
"mr": 15,
|
||||
"sdPct": -20,
|
||||
"mdPct": -40,
|
||||
"def": 15,
|
||||
|
@ -77370,7 +77377,7 @@
|
|||
"aDamPct": 40
|
||||
},
|
||||
{
|
||||
"mr": 6,
|
||||
"mr": 30,
|
||||
"sdPct": -40,
|
||||
"mdPct": -85,
|
||||
"def": 40,
|
||||
|
@ -77432,7 +77439,7 @@
|
|||
"sdPct": -33,
|
||||
"mdPct": -33,
|
||||
"ls": 90,
|
||||
"ms": 2,
|
||||
"ms": 10,
|
||||
"sdRaw": 160,
|
||||
"mdRaw": 105,
|
||||
"atkTier": 1
|
||||
|
@ -77453,7 +77460,7 @@
|
|||
"hprRaw": 90
|
||||
},
|
||||
{
|
||||
"mr": 5,
|
||||
"mr": 25,
|
||||
"int": 20,
|
||||
"def": 20,
|
||||
"hpBonus": 1500,
|
||||
|
@ -77473,19 +77480,19 @@
|
|||
"bonuses": [
|
||||
{},
|
||||
{
|
||||
"mr": 1,
|
||||
"mr": 5,
|
||||
"xpb": 5,
|
||||
"lb": 10,
|
||||
"hpBonus": 55
|
||||
},
|
||||
{
|
||||
"mr": 2,
|
||||
"mr": 10,
|
||||
"xpb": 10,
|
||||
"lb": 25,
|
||||
"hpBonus": 170
|
||||
},
|
||||
{
|
||||
"mr": 4,
|
||||
"mr": 20,
|
||||
"xpb": 25,
|
||||
"lb": 50,
|
||||
"int": 20,
|
||||
|
@ -77741,4 +77748,4 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,265 +31,252 @@
|
|||
<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 py-5 vh-100 mx-0 mx-lg-auto">
|
||||
<div class = "col">
|
||||
<div class = "row g-3">
|
||||
<div class = "col-lg-5 col-sm-12 text-center">
|
||||
<div class = "row gx-5 mb-2">
|
||||
<div class = "col-lg-6 col-sm-12">
|
||||
<div id = "recipe-dropdown" class = "row h-100 dark-shadow dark-6 rounded">
|
||||
<div id = "recipe-img-loc" class = "col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon">
|
||||
<img id = "recipe-img" class = "img-fluid rounded Crafted-shadow" src = "../media/items/new/generic-potion.png">
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<div class = "row align-items-center">
|
||||
<div class = "col-4 px-0">
|
||||
<p class = "text-right mb-0 scaled-font fw-bold">Type:</p>
|
||||
</div>
|
||||
<div class = "col-7 px-0">
|
||||
<input class="recipeinput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="recipe-choices" id="recipe-choice" name="recipe-choice" placeholder="Potion"/>
|
||||
<datalist id="recipe-choices">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row align-items-center">
|
||||
<div class = "col-4 px-0">
|
||||
<p class = "text-right mb-0 scaled-font fw-bold">Lv:</p>
|
||||
</div>
|
||||
<div class = "col-7 px-0">
|
||||
<input class="levelinput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="level-choices" id="level-choice" name="level-choice" placeholder="103-105" />
|
||||
<datalist id="level-choices">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mt-5">
|
||||
<div class="row row-cols-1 row-cols-lg-3 gy-5">
|
||||
<div class="col col-lg-5">
|
||||
<!--crafter ui-->
|
||||
<div class="row row-cols-1 row-cols-lg-2 gx-5 gy-4 gy-lg-2">
|
||||
<div class="col" id="recipe-dropdown">
|
||||
<div class="row dark-shadow dark-5 rounded">
|
||||
<div id = "recipe-img-loc" class = "col-auto px-lg-1 g-0 dark-7 rounded-end my-auto text-center scaled-item-icon">
|
||||
<img id = "recipe-img" class = "img-fluid rounded Crafted-shadow" src = "../media/items/new/generic-potion.png">
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-6 col-sm-12">
|
||||
<div id = "atkSpdChoices" class = "row h-100 dark-shadow dark-6 rounded">
|
||||
<div class = "col py-2">
|
||||
<div class = "row h-50 align-items-center">
|
||||
<p class = "text-right mb-0 scaled-font fw-bold">Attack Speed</p>
|
||||
<div class = "col ps-3">
|
||||
<div class = "row row-cols-2 align-items-center">
|
||||
<div class = "col-4 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Type:</p>
|
||||
</div>
|
||||
<div class = "row h-50">
|
||||
<div class = "col-4 pl-1">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "slow-atk-button" onclick = "toggleAtkSpd('slow-atk-button')">
|
||||
Slow
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-4 px-0">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "normal-atk-button" onclick = "toggleAtkSpd('normal-atk-button')">
|
||||
Normal
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-4 pr-1">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "fast-atk-button" onclick = "toggleAtkSpd('fast-atk-button')">
|
||||
Fast
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-8 px-0">
|
||||
<input class="recipeinput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="recipe-choices" id="recipe-choice" name="recipe-choice" placeholder="Potion"/>
|
||||
<datalist id="recipe-choices">
|
||||
</datalist>
|
||||
</div>
|
||||
<div class = "col-4 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Lv:</p>
|
||||
</div>
|
||||
<div class = "col-8 px-0">
|
||||
<input class="levelinput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="level-choices" id="level-choice" name="level-choice" placeholder="103-105" />
|
||||
<datalist id="level-choices">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row gx-5 mb-2">
|
||||
<div class = "col-lg-6 col-sm-12 justify-content-center">
|
||||
<div class = "row dark-shadow dark-6 rounded py-1 align-items-center">
|
||||
<div class = "col-6 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold" id = "mat-1">Mat 1 Tier:</p>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-1" onclick = "toggleMaterial('mat-1-1')">1</button>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-2" onclick = "toggleMaterial('mat-1-2')">2</button>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-3" onclick = "toggleMaterial('mat-1-3')">3</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-6 col-sm-12 justify-content-center">
|
||||
<div class = "row dark-shadow dark-6 rounded py-1 align-items-center">
|
||||
<div class = "col-6 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold" id = "mat-2">Mat 2 Tier:</p>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-1" onclick = "toggleMaterial('mat-2-1')">1</button>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-2" onclick = "toggleMaterial('mat-2-2')">2</button>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-3" onclick = "toggleMaterial('mat-2-3')">3</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row gx-5 mb-1">
|
||||
<div class = "col">
|
||||
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 1:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-1" id="ing-choice-1" name="ing-choice-1" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-1">
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div id = "atkSpdChoices" class = "row row-cols-1 dark-shadow dark-5 rounded">
|
||||
<div class="col pt-1">
|
||||
<p class = "text-center scaled-font fw-bold mb-1">Attack Speed</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 2:</p>
|
||||
<div class="col mb-2">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-auto px-2">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "slow-atk-button" onclick = "toggleAtkSpd('slow-atk-button')">
|
||||
Slow
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto px-2">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "normal-atk-button" onclick = "toggleAtkSpd('normal-atk-button')">
|
||||
Normal
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-auto px-2">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "fast-atk-button" onclick = "toggleAtkSpd('fast-atk-button')">
|
||||
Fast
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-2" id="ing-choice-2" name="ing-choice-2" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-2">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row gx-5 mb-1">
|
||||
<div class = "col">
|
||||
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 3:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-3" id="ing-choice-3" name="ing-choice-3" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-3">
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded py-1 align-items-center">
|
||||
<div class = "col-6 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold text-center" id = "mat-1">Mat 1 Tier:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 4:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-4" id="ing-choice-4" name="ing-choice-4" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-4">
|
||||
</datalist>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-1" onclick = "toggleMaterial('mat-1-1')">1</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row gx-5 mb-1">
|
||||
<div class = "col">
|
||||
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 5:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-5" id="ing-choice-5" name="ing-choice-5" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-5">
|
||||
</datalist>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-2" onclick = "toggleMaterial('mat-1-2')">2</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col">
|
||||
<div class = "row h-100 dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 6:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-6" id="ing-choice-6" name="ing-choice-6" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-6">
|
||||
</datalist>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-1-3" onclick = "toggleMaterial('mat-1-3')">3</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded py-1 align-items-center">
|
||||
<div class = "col-6 px-0">
|
||||
<p class = "mb-0 scaled-font fw-bold text-center" id = "mat-2">Mat 2 Tier:</p>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-1" onclick = "toggleMaterial('mat-2-1')">1</button>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-2" onclick = "toggleMaterial('mat-2-2')">2</button>
|
||||
</div>
|
||||
<div class = "col px-0">
|
||||
<button class = "button Star rounded scaled-font fw-bold text-light dark-5" id = "mat-2-3" onclick = "toggleMaterial('mat-2-3')">3</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded align-items-center just">
|
||||
<div class = "col-3 px-0 ps-2">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 1:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-1" id="ing-choice-1" name="ing-choice-1" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-1">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class = "row rounded dark-shadow dark-6 py-2 gy-3">
|
||||
<div class = "col-lg-2 col-sm-6">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "resetFields()">
|
||||
Reset
|
||||
</button>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0 ps-2">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 2:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-2" id="ing-choice-2" name="ing-choice-2" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-2">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-3 col-sm-6">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "copy-hash-button" onclick = "copyRecipeHash()">
|
||||
Copy Hash
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0 ps-2">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 3:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-3" id="ing-choice-3" name="ing-choice-3" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-3">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-4 col-sm-6">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "copy-button" onclick = "copyRecipe()">
|
||||
Copy Short
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0 ps-2">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 4:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-4" id="ing-choice-4" name="ing-choice-4" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-4">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-3 col-sm-6">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "share-button" onclick = "shareRecipe()">
|
||||
Copy Long
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0 ps-2">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 5:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-5" id="ing-choice-5" name="ing-choice-5" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-5">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class = "row dark-shadow dark-6 rounded align-items-center">
|
||||
<div class = "col-3 px-0 ps-2">
|
||||
<p class = "mb-0 scaled-font fw-bold">Ing 6:</p>
|
||||
</div>
|
||||
<div class = "col-9 px-0">
|
||||
<input class="inginput border-dark text-light dark-10 rounded scaled-font form-control form-control-sm" list="ing-choices-6" id="ing-choice-6" name="ing-choice-6" placeholder="No Ingredient" />
|
||||
<datalist id="ing-choices-6">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-4">
|
||||
<div class = "recipe hide-container-block px-3 col rounded dark-6 text-light scaled-font p-3 border-dark dark-shadow g-0" style = "display:none">
|
||||
<div class = "row recipe-stats">
|
||||
<div class = "col" id = "recipe-stats"></div>
|
||||
</div>
|
||||
<div class = "row craft-warnings">
|
||||
<div class = "" id = "craft-warnings"></div>
|
||||
</div>
|
||||
<div class = "row rounded dark-shadow dark-6 mt-3 p-2 align-items-center justify-content-center">
|
||||
<div class = "col-auto mx-auto">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "resetFields()">
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-auto mx-auto">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "copy-hash-button" onclick = "copyRecipeHash()">
|
||||
Copy Hash
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-auto mx-auto">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "copy-button" onclick = "copyRecipe()">
|
||||
Copy Short
|
||||
</button>
|
||||
</div>
|
||||
<div class = "col-auto mx-auto">
|
||||
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "share-button" onclick = "shareRecipe()">
|
||||
Copy Long
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "col-lg-3">
|
||||
<div class = "crafted row hide-container-block" style = "display:none">
|
||||
<div class = "craft-stats">
|
||||
<div class = "col rounded dark-6 text-light scaled-font p-3 border-dark dark-shadow g-0" id = "craft-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-lg-4">
|
||||
<div class = "recipe hide-container-block px-3 col rounded dark-6 text-light scaled-font p-3 border-dark dark-shadow g-0" style = "display:none">
|
||||
<div class = "row row-cols-1 recipe-stats " id = "recipe-stats">
|
||||
</div>
|
||||
<div class = "row craft-warnings">
|
||||
<div class = "" id = "craft-warnings"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col ingredients-container hide-container-grid" id = "ingreds" style = "display:none">
|
||||
<div class = "col-lg-6 col-sm-12 hide-container-grid" id = "ingreds">
|
||||
<div class = "row mb-3">
|
||||
<p class="box-title hide-container-block">
|
||||
Ingredients
|
||||
</p>
|
||||
</div>
|
||||
<div class = "row mb-3">
|
||||
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-1">
|
||||
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-1-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-2">
|
||||
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-2-stats"></div>
|
||||
</div>
|
||||
<div class="col col-lg-3">
|
||||
<div class = "crafted row hide-container-block" style = "display:none">
|
||||
<div class = "craft-stats">
|
||||
<div class = "col rounded dark-6 text-light scaled-font p-3 border-dark dark-shadow g-0" id = "craft-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row mb-3">
|
||||
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-3">
|
||||
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-3-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-4">
|
||||
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-4-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class = "row mb-3">
|
||||
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-5">
|
||||
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-5-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col-sm scaled-font" id = "ing-6">
|
||||
<div class = "rounded col g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-6-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col dark-5 scaled-font">
|
||||
<footer class="text-center">
|
||||
<div id="header2">
|
||||
<p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, and <b>reschan</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
|
||||
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
|
||||
<div class="row my-3">
|
||||
<div class="col col-lg-6 ingredients-container hide-container-grid" id = "ingreds" style = "display:none">
|
||||
<div class = "row mb-3">
|
||||
<div class="col">
|
||||
<p class="box-title hide-container-block">
|
||||
Ingredients
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="credits">
|
||||
<a href="../credits.txt" class="link">Additional credits</a>
|
||||
<div class="row row-cols-1 row-cols-lg-2 g-3">
|
||||
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-1">
|
||||
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-1-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-2">
|
||||
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-2-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-3">
|
||||
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-3-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-4">
|
||||
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-4-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-5">
|
||||
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-5-stats"></div>
|
||||
</div>
|
||||
<div class="ing-stats col-lg-6 col scaled-font" id = "ing-6">
|
||||
<div class = "rounded g-0 dark-6 border border-3 border-dark dark-shadow p-3" id = "ing-6-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-2">
|
||||
<div class="col dark-5 scaled-font">
|
||||
<footer class="text-center">
|
||||
<div id="header2">
|
||||
<p>Made by <b class = "hppeng">hppeng</b>, <b class = "ferricles">ferricles</b>, and <b>reschan</b> with <a href = "../atlas" target = "_blank" class = "atlas link">Atlas Inc</a> (JavaScript required to function, nothing works without js)</p>
|
||||
<p>Hard refresh the page (Ctrl+Shift+R on windows/chrome) if it isn't updating correctly.</p>
|
||||
</div>
|
||||
<div id="credits">
|
||||
<a href="../credits.txt" class="link">Additional credits</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../js/query.js"></script>
|
||||
|
|
|
@ -7,14 +7,15 @@ The game, of course
|
|||
|
||||
Additional Contributors, in no particular order:
|
||||
- Kiocifer (Icons!)
|
||||
- IncinerateMe (helping transition to 1.20.3 / CI helper)
|
||||
- IncinerateMe (helping transition to 1.20.3 / atree / CI helper)
|
||||
- puppy (dog)
|
||||
- SockMower (ability tree encode/decode optimization)
|
||||
- ITechnically (coding emotional support / misc)
|
||||
- touhoku (best IM)
|
||||
- HeyZeer0 (huge help in getting our damage formulas right)
|
||||
- blankman (fin444 github) (beautifying atree visuals)
|
||||
- Lennon (Skill point formula reversing)
|
||||
- Phanta (WynnAtlas custom expression parser / item search)
|
||||
- nbcss (Crafted Item mechanics reverse engineering)
|
||||
- nbcss (and WIM team) (Crafted Item mechanics reverse engineering, testing)
|
||||
- dr_carlos (Hiding UI elements properly, fade animations, proper error handling)
|
||||
- Atlas Inc discord (feedback, ideas, damage calc, etc)
|
||||
|
|
|
@ -448,36 +448,6 @@ a:hover {
|
|||
border-color: #fff;
|
||||
}
|
||||
|
||||
/* atree connector rotations */
|
||||
.rotate-90 {
|
||||
-webkit-transform: rotate(90deg);
|
||||
-moz-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
-o-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.rotate-180 {
|
||||
-webkit-transform: rotate(180deg);
|
||||
-moz-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
-o-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.rotate-270 {
|
||||
-webkit-transform: rotate(270deg);
|
||||
-moz-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
-o-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.rotate-flip {
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.hide-scroll {
|
||||
|
@ -488,16 +458,6 @@ a:hover {
|
|||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
|
||||
.atree-selected {
|
||||
outline: 5px solid rgba(95, 214, 223, 0.8);
|
||||
}
|
||||
|
||||
.atree-circle {
|
||||
border-radius:50%;
|
||||
-moz-border-radius:50%;
|
||||
-webkit-border-radius:50%;
|
||||
}
|
||||
|
||||
.hppeng{
|
||||
color: #20c2b6;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,6 @@
|
|||
<script type="text/javascript" src="/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/js/loadheader.js"></script>
|
||||
<script type="text/javascript" src="/js/icons.js"></script>
|
||||
<script type="text/javascript" src="/js/dps_vis.js"></script>
|
||||
<script type="text/javascript" src="/js/debug/dps_vis.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -200,8 +200,8 @@
|
|||
"lvl": 75,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": -22,
|
||||
|
@ -9834,7 +9834,7 @@
|
|||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -10533,7 +10533,7 @@
|
|||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -10882,8 +10882,8 @@
|
|||
"maximum": 15
|
||||
},
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -11906,8 +11906,8 @@
|
|||
"maximum": -5
|
||||
},
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"gXp": {
|
||||
"minimum": 4,
|
||||
|
@ -14594,8 +14594,8 @@
|
|||
"lvl": 76,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"spd": {
|
||||
"minimum": -12,
|
||||
|
@ -15404,8 +15404,8 @@
|
|||
"lvl": 28,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"xpb": {
|
||||
"minimum": -10,
|
||||
|
@ -15569,8 +15569,8 @@
|
|||
"lvl": 102,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"hpBonus": {
|
||||
"minimum": -260,
|
||||
|
@ -15684,8 +15684,8 @@
|
|||
"lvl": 87,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdRaw": {
|
||||
"minimum": 65,
|
||||
|
@ -15762,8 +15762,8 @@
|
|||
"lvl": 65,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -16037,8 +16037,8 @@
|
|||
"lvl": 77,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"hpBonus": {
|
||||
"minimum": -775,
|
||||
|
@ -16270,8 +16270,8 @@
|
|||
"lvl": 50,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": 8,
|
||||
|
@ -16355,8 +16355,8 @@
|
|||
"lvl": 90,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": 6,
|
||||
|
@ -16396,8 +16396,8 @@
|
|||
"lvl": 90,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"mdRaw": {
|
||||
"minimum": 50,
|
||||
|
@ -16734,8 +16734,8 @@
|
|||
"maximum": 20
|
||||
},
|
||||
"ms": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
},
|
||||
"fDamPct": {
|
||||
"minimum": 10,
|
||||
|
@ -16777,8 +16777,8 @@
|
|||
"lvl": 99,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": 10,
|
||||
|
@ -16944,8 +16944,8 @@
|
|||
"maximum": 10
|
||||
},
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -17488,12 +17488,12 @@
|
|||
"lvl": 23,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"ms": {
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -18623,8 +18623,8 @@
|
|||
"maximum": -100
|
||||
},
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"wDamPct": {
|
||||
"minimum": 10,
|
||||
|
@ -18908,8 +18908,8 @@
|
|||
"lvl": 29,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": -8,
|
||||
|
@ -18947,8 +18947,8 @@
|
|||
"lvl": 105,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"agi": {
|
||||
"minimum": 8,
|
||||
|
@ -19535,8 +19535,8 @@
|
|||
"lvl": 104,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"gXp": {
|
||||
"minimum": 3,
|
||||
|
@ -21060,8 +21060,8 @@
|
|||
"lvl": 75,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"int": {
|
||||
"minimum": 3,
|
||||
|
@ -21387,12 +21387,12 @@
|
|||
"lvl": 78,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"ms": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
},
|
||||
"wDamPct": {
|
||||
"minimum": 5,
|
||||
|
@ -21550,8 +21550,8 @@
|
|||
"lvl": 93,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdRaw": {
|
||||
"minimum": 45,
|
||||
|
@ -21595,8 +21595,8 @@
|
|||
"lvl": 20,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdRaw": {
|
||||
"minimum": 8,
|
||||
|
@ -21958,8 +21958,8 @@
|
|||
"lvl": 43,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": 11,
|
||||
|
@ -22216,8 +22216,8 @@
|
|||
"lvl": 75,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": 15,
|
||||
|
@ -22714,8 +22714,8 @@
|
|||
"lvl": 80,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": -20,
|
||||
|
@ -22911,8 +22911,8 @@
|
|||
"lvl": 8,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"ref": {
|
||||
"minimum": 2,
|
||||
|
@ -22988,8 +22988,8 @@
|
|||
"lvl": 45,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
"minimum": 10,
|
||||
"maximum": 10
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -23583,8 +23583,8 @@
|
|||
"lvl": 42,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -23878,8 +23878,8 @@
|
|||
"lvl": 50,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": -12,
|
||||
|
@ -23957,8 +23957,8 @@
|
|||
"lvl": 55,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"spd": {
|
||||
"minimum": 8,
|
||||
|
@ -24035,8 +24035,8 @@
|
|||
"lvl": 43,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": -2,
|
||||
"maximum": -2
|
||||
"minimum": -10,
|
||||
"maximum": -10
|
||||
},
|
||||
"sdPct": {
|
||||
"minimum": 24,
|
||||
|
@ -24120,8 +24120,8 @@
|
|||
"maximum": 14
|
||||
},
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"hprRaw": {
|
||||
"minimum": 24,
|
||||
|
@ -24160,8 +24160,8 @@
|
|||
"lvl": 92,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"sdRaw": {
|
||||
"minimum": 110,
|
||||
|
@ -24373,8 +24373,8 @@
|
|||
"lvl": 90,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"spd": {
|
||||
"minimum": 16,
|
||||
|
@ -25193,8 +25193,8 @@
|
|||
"maximum": 20
|
||||
},
|
||||
"mr": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -25249,8 +25249,8 @@
|
|||
"maximum": 20
|
||||
},
|
||||
"mr": {
|
||||
"minimum": -1,
|
||||
"maximum": -1
|
||||
"minimum": -5,
|
||||
"maximum": -5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -25285,8 +25285,8 @@
|
|||
"lvl": 83,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
"minimum": 10,
|
||||
"maximum": 10
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -25544,8 +25544,8 @@
|
|||
"lvl": 10,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -26028,8 +26028,8 @@
|
|||
"lvl": 47,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"tDamPct": {
|
||||
"minimum": 6,
|
||||
|
@ -26200,8 +26200,8 @@
|
|||
"lvl": 87,
|
||||
"ids": {
|
||||
"mr": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
},
|
||||
"eDamPct": {
|
||||
"minimum": 10,
|
||||
|
@ -26769,8 +26769,8 @@
|
|||
"maximum": 12
|
||||
},
|
||||
"ms": {
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
"minimum": 10,
|
||||
"maximum": 10
|
||||
},
|
||||
"atkTier": {
|
||||
"minimum": -1,
|
||||
|
@ -26971,8 +26971,8 @@
|
|||
"lvl": 68,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 2,
|
||||
"maximum": 2
|
||||
"minimum": 10,
|
||||
"maximum": 10
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
@ -27344,8 +27344,8 @@
|
|||
"lvl": 25,
|
||||
"ids": {
|
||||
"ms": {
|
||||
"minimum": 1,
|
||||
"maximum": 1
|
||||
"minimum": 5,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"itemIDs": {
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
<script type="text/javascript" src="/js/build_utils.js"></script>
|
||||
<script type="text/javascript" src="/js/icons.js"></script>
|
||||
<script type="text/javascript" src="/js/damage_calc.js"></script>
|
||||
<script type="text/javascript" src="/js/powders.js"></script>
|
||||
<!-- <script type="text/javascript" src="/js/powders.js"></script> -->
|
||||
<script type="text/javascript" src="/js/load.js"></script>
|
||||
<script type="text/javascript" src="/js/load_ing.js"></script>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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
|
||||
|
|
520
js/atree.js
|
@ -76,6 +76,7 @@ stat_scaling: {
|
|||
"slider": bool,
|
||||
"slider_name": Optional[str],
|
||||
"slider_step": Optional[float],
|
||||
round: Optional[bool] // Control floor behavior. True for stats and false for slider by default
|
||||
slider_behavior: Optional[str] // One of: "merge", "modify". default: merge
|
||||
// merge: add if exist, make new part if not exist
|
||||
// modify: change existing part. do nothing if not exist
|
||||
|
@ -97,35 +98,35 @@ const elem_mastery_abil = { display_name: "Elemental Mastery", id: 998, properti
|
|||
|
||||
// TODO: Range numbers
|
||||
const default_abils = {
|
||||
wand: [{
|
||||
Mage: [{
|
||||
display_name: "Mage Melee",
|
||||
id: 999,
|
||||
desc: "Mage basic attack.",
|
||||
properties: {range: 5000},
|
||||
effects: [default_spells.wand[0]]
|
||||
}, elem_mastery_abil ],
|
||||
spear: [{
|
||||
Warrior: [{
|
||||
display_name: "Warrior Melee",
|
||||
id: 999,
|
||||
desc: "Warrior basic attack.",
|
||||
properties: {range: 2},
|
||||
effects: [default_spells.spear[0]]
|
||||
}, elem_mastery_abil ],
|
||||
bow: [{
|
||||
Archer: [{
|
||||
display_name: "Archer Melee",
|
||||
id: 999,
|
||||
desc: "Archer basic attack.",
|
||||
properties: {range: 20},
|
||||
effects: [default_spells.bow[0]]
|
||||
}, elem_mastery_abil ],
|
||||
dagger: [{
|
||||
Assassin: [{
|
||||
display_name: "Assassin Melee",
|
||||
id: 999,
|
||||
desc: "Assassin basic attack.",
|
||||
properties: {range: 2},
|
||||
effects: [default_spells.dagger[0]]
|
||||
}, elem_mastery_abil ],
|
||||
relik: [{
|
||||
Shaman: [{
|
||||
display_name: "Shaman Melee",
|
||||
id: 999,
|
||||
desc: "Shaman basic attack.",
|
||||
|
@ -251,18 +252,18 @@ const atree_state_node = new (class extends ComputeNode {
|
|||
* 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(build: Build, atree: ATree, atree-state: RenderedATree) => Map[id, Ability]
|
||||
* 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 build = input_map.get('build');
|
||||
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[build.weapon.statMap.get('type')]) {
|
||||
for (const abil of default_abils[player_class]) {
|
||||
let tmp_abil = deepcopy(abil);
|
||||
if (!('desc' in tmp_abil)) {
|
||||
tmp_abil.desc = [];
|
||||
|
@ -310,6 +311,56 @@ const atree_merge = new (class extends ComputeNode {
|
|||
}
|
||||
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
|
||||
|
||||
/**
|
||||
* Check if an atree node can be activated.
|
||||
*
|
||||
* Return: [yes/no, hard error, reason]
|
||||
*/
|
||||
function abil_can_activate(atree_node, atree_state, reachable, archetype_count, points_remain) {
|
||||
const {parents, ability} = atree_node;
|
||||
if (parents.length === 0) {
|
||||
return [true, false, ""];
|
||||
}
|
||||
let failed_deps = [];
|
||||
for (const dep_id of ability.dependencies) {
|
||||
if (!atree_state.get(dep_id).active) { failed_deps.push(dep_id) }
|
||||
}
|
||||
if (failed_deps.length > 0) {
|
||||
const dep_strings = failed_deps.map(i => '"' + atree_state.get(i).ability.display_name + '"');
|
||||
return [false, true, 'missing dep: ' + dep_strings.join(", ")];
|
||||
}
|
||||
let blocking_ids = [];
|
||||
for (const blocker_id of ability.blockers) {
|
||||
if (atree_state.get(blocker_id).active) { blocking_ids.push(blocker_id); }
|
||||
}
|
||||
if (blocking_ids.length > 0) {
|
||||
const blockers_strings = blocking_ids.map(i => '"' + atree_state.get(i).ability.display_name + '"');
|
||||
return [false, true, 'blocked by: '+blockers_strings.join(", ")];
|
||||
}
|
||||
let node_reachable = false;
|
||||
for (const parent of parents) {
|
||||
if (reachable.has(parent.ability.id)) {
|
||||
node_reachable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!node_reachable) {
|
||||
return [false, false, 'not reachable'];
|
||||
}
|
||||
if ('archetype' in ability && ability.archetype !== "") {
|
||||
if ('archetype_req' in ability && ability.archetype_req !== 0) {
|
||||
const others = (archetype_count.get(ability.archetype) || 0);
|
||||
if (others < ability.archetype_req) {
|
||||
return [false, false, ability.archetype+': '+others+' < '+ability.archetype_req];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ability.cost > points_remain) {
|
||||
return [false, false, "not enough ability points left"];
|
||||
}
|
||||
return [true, false, ""];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate ability tree.
|
||||
* Return list of errors for rendering.
|
||||
|
@ -322,13 +373,24 @@ const atree_validate = new (class extends ComputeNode {
|
|||
compute_func(input_map) {
|
||||
const atree_state = input_map.get('atree-state');
|
||||
const atree_order = input_map.get('atree');
|
||||
const level = parseInt(input_map.get('level'));
|
||||
|
||||
if (atree_order.length == 0) { return [0, false, ['no atree data']]; }
|
||||
|
||||
let atree_to_add = [];
|
||||
let atree_not_present = [];
|
||||
// mark all selected nodes as bright, and mark all other nodes as dark.
|
||||
// also initialize the "to check" list, and the "not present" list.
|
||||
for (const node of atree_order) {
|
||||
const abil = node.ability;
|
||||
if (atree_state.get(abil.id).active) { atree_to_add.push([node, 'not reachable', false]); }
|
||||
if (atree_state.get(abil.id).active) {
|
||||
atree_to_add.push([node, 'not reachable', false]);
|
||||
atree_state.get(abil.id).img.src = '../media/atree/' + abil.display.icon + '_selected.png';
|
||||
}
|
||||
else {
|
||||
atree_not_present.push(abil.id);
|
||||
atree_state.get(abil.id).img.src = '../media/atree/' + abil.display.icon + '_blocked.png';
|
||||
}
|
||||
}
|
||||
|
||||
let reachable = new Set();
|
||||
|
@ -337,50 +399,13 @@ const atree_validate = new (class extends ComputeNode {
|
|||
while (true) {
|
||||
let _add = [];
|
||||
for (const [node, fail_reason, fail_hardness] of atree_to_add) {
|
||||
const {parents, ability} = node;
|
||||
if (parents.length === 0) {
|
||||
reachable.add(ability.id);
|
||||
// root abil has no archetype.
|
||||
abil_points_total += ability.cost;
|
||||
continue;
|
||||
}
|
||||
let failed_deps = [];
|
||||
for (const dep_id of ability.dependencies) {
|
||||
if (!atree_state.get(dep_id).active) { failed_deps.push(dep_id) }
|
||||
}
|
||||
if (failed_deps.length > 0) {
|
||||
const dep_strings = failed_deps.map(i => '"' + atree_state.get(i).ability.display_name + '"');
|
||||
_add.push([node, 'missing dep: ' + dep_strings.join(", "), true]);
|
||||
continue;
|
||||
}
|
||||
let blocking_ids = [];
|
||||
for (const blocker_id of ability.blockers) {
|
||||
if (atree_state.get(blocker_id).active) { blocking_ids.push(blocker_id); }
|
||||
}
|
||||
if (blocking_ids.length > 0) {
|
||||
const blockers_strings = blocking_ids.map(i => '"' + atree_state.get(i).ability.display_name + '"');
|
||||
_add.push([node, 'blocked by: '+blockers_strings.join(", "), true]);
|
||||
continue;
|
||||
}
|
||||
let node_reachable = false;
|
||||
for (const parent of parents) {
|
||||
if (reachable.has(parent.ability.id)) {
|
||||
node_reachable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!node_reachable) {
|
||||
_add.push([node, 'not reachable', false])
|
||||
const {ability} = node;
|
||||
const [success, hard_error, reason] = abil_can_activate(node, atree_state, reachable, archetype_count, 9999);
|
||||
if (!success) {
|
||||
_add.push([node, reason, hard_error]);
|
||||
continue;
|
||||
}
|
||||
if ('archetype' in ability && ability.archetype !== "") {
|
||||
if ('archetype_req' in ability && ability.archetype_req !== 0) {
|
||||
const others = archetype_count.get(ability.archetype);
|
||||
if (others < ability.archetype_req) {
|
||||
_add.push([node, ability.archetype+': '+others+' < '+ability.archetype_req, false])
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let val = 1;
|
||||
if (archetype_count.has(ability.archetype)) {
|
||||
val = archetype_count.get(ability.archetype) + 1;
|
||||
|
@ -395,13 +420,38 @@ const atree_validate = new (class extends ComputeNode {
|
|||
}
|
||||
atree_to_add = _add;
|
||||
}
|
||||
const atree_level_table = ['lvl0wtf',1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,9,10,11,11,12,12,13,14,14,15,16,16,17,17,18,18,19,19,20,20,20,21,21,22,22,23,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,34,34,35,35,35,36,36,36,37,37,37,38,38,38,38,39,39,39,39,40,40,40,40,41,41,41,41,42,42,42,42,43,43,43,43,44,44,44,44,45,45,45];
|
||||
let AP_cap;
|
||||
if (isNaN(level)) {
|
||||
AP_cap = 45;
|
||||
}
|
||||
else {
|
||||
AP_cap = atree_level_table[level];
|
||||
}
|
||||
document.getElementById('active_AP_cap').textContent = AP_cap;
|
||||
document.getElementById("active_AP_cost").textContent = abil_points_total;
|
||||
const ap_left = AP_cap - abil_points_total;
|
||||
|
||||
// using the "not present" list, highlight one-step reachable nodes.
|
||||
for (const node_id of atree_not_present) {
|
||||
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) {
|
||||
node.img.src = '../media/atree/'+node.ability.display.icon+'.png';
|
||||
}
|
||||
}
|
||||
|
||||
let hard_error = false;
|
||||
let errors = [];
|
||||
if (abil_points_total > AP_cap) {
|
||||
errors.push('too many ability points assigned! ('+abil_points_total+' > '+AP_cap+')');
|
||||
}
|
||||
for (const [node, fail_reason, fail_hardness] of atree_to_add) {
|
||||
if (fail_hardness) { hard_error = true; }
|
||||
errors.push(node.ability.display_name + ": " + fail_reason);
|
||||
}
|
||||
return [abil_points_total, hard_error, errors];
|
||||
|
||||
return [hard_error, errors];
|
||||
}
|
||||
})().link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
|
||||
|
||||
|
@ -420,12 +470,11 @@ const atree_render_active = 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 [abil_points_total, hard_error, errors] = input_map.get('atree-errors');
|
||||
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?
|
||||
document.getElementById("active_AP_cost").textContent = abil_points_total;
|
||||
|
||||
if (errors.length > 0) {
|
||||
let errorbox = document.createElement('div');
|
||||
errorbox.classList.add("rounded-bottom", "dark-4", "border", "p-0", "mx-2", "my-4", "dark-shadow");
|
||||
|
@ -446,11 +495,6 @@ const atree_render_active = new (class extends ComputeNode {
|
|||
const atree_warning = make_elem("p", ["warning", "small-text"], {textContent: error});
|
||||
errorbox.appendChild(atree_warning);
|
||||
}
|
||||
if (abil_points_total > 45) {
|
||||
const error = 'too many ability points assigned! ('+abil_points_total+' > 45)';
|
||||
const atree_warning = make_elem("p", ["warning", "small-text"], {textContent: error});
|
||||
errorbox.appendChild(atree_warning);
|
||||
}
|
||||
}
|
||||
const ret_map = new Map();
|
||||
const to_render_id = [999, 998];
|
||||
|
@ -495,7 +539,7 @@ const atree_collect_spells = new (class extends ComputeNode {
|
|||
|
||||
compute_func(input_map) {
|
||||
const atree_merged = input_map.get('atree-merged');
|
||||
const [abil_points_total, hard_error, errors] = input_map.get('atree-errors');
|
||||
const [hard_error, errors] = input_map.get('atree-errors');
|
||||
if (hard_error) { return []; }
|
||||
|
||||
let ret_spells = new Map();
|
||||
|
@ -527,7 +571,8 @@ const atree_collect_spells = new (class extends ComputeNode {
|
|||
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; }
|
||||
// TODO: unjankify this...
|
||||
if ('cost' in ret_spell) { ret_spell.cost += cost; }
|
||||
|
||||
if (target_part === null) {
|
||||
continue;
|
||||
|
@ -623,6 +668,7 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
*/
|
||||
// Map<str, slider_info>
|
||||
const slider_map = new Map();
|
||||
const button_map = new Map();
|
||||
|
||||
// first, pull out all the sliders.
|
||||
for (const [abil_id, ability] of merged_abils.entries()) {
|
||||
|
@ -630,8 +676,10 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
if (effect['type'] === "stat_scaling" && effect['slider'] === true) {
|
||||
const { slider_name, slider_behavior = 'merge', slider_max, slider_step } = effect;
|
||||
if (slider_map.has(slider_name)) {
|
||||
const slider_info = slider_map.get(slider_name);
|
||||
slider_info.max += slider_max;
|
||||
if (slider_max !== undefined) {
|
||||
const slider_info = slider_map.get(slider_name);
|
||||
slider_info.max += slider_max;
|
||||
}
|
||||
}
|
||||
else if (slider_behavior === 'merge') {
|
||||
slider_map.set(slider_name, {
|
||||
|
@ -644,6 +692,12 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
});
|
||||
}
|
||||
}
|
||||
if (effect['type'] === "raw_stat" && effect['toggle']) {
|
||||
const { toggle: toggle_name } = effect;
|
||||
button_map.set(toggle_name, {
|
||||
abil: ability
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// next, render the sliders onto the abilities.
|
||||
|
@ -653,7 +707,23 @@ const atree_make_interactives = new (class extends ComputeNode {
|
|||
slider_info.slider = document.getElementById(slider_info.id);
|
||||
slider_info.slider.addEventListener("change", (e) => atree_stats.mark_dirty().update());
|
||||
}
|
||||
return slider_map;
|
||||
for (const [button_name, button_info] of button_map.entries()) {
|
||||
let button = make_elem('button', ["button-boost", "border-0", "text-white", "dark-8u", "dark-shadow-sm"], {
|
||||
id: button_info.abil.id,
|
||||
textContent: button_name
|
||||
});
|
||||
button.addEventListener("click", (e) => {
|
||||
if (button.classList.contains("toggleOn")) {
|
||||
button.classList.remove("toggleOn");
|
||||
} else {
|
||||
button.classList.add("toggleOn");
|
||||
}
|
||||
atree_stats.mark_dirty().update()
|
||||
});
|
||||
button_info.button = button;
|
||||
atree_html.get(button_info.abil.id).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');
|
||||
|
||||
|
@ -670,7 +740,7 @@ const atree_stats = new (class extends ComputeNode {
|
|||
compute_func(input_map) {
|
||||
const atree_merged = input_map.get('atree-merged');
|
||||
const item_stats = input_map.get('build').statMap;
|
||||
const interactive_map = input_map.get('atree-interactive');
|
||||
const [slider_map, button_map] = input_map.get('atree-interactive');
|
||||
|
||||
let ret_effects = new Map();
|
||||
for (const [abil_id, abil] of atree_merged.entries()) {
|
||||
|
@ -681,8 +751,10 @@ const atree_stats = new (class extends ComputeNode {
|
|||
case 'stat_scaling':
|
||||
if (effect.slider) {
|
||||
if ('output' in effect) { // sometimes nodes will modify slider without having effect.
|
||||
const slider_val = interactive_map.get(effect.slider_name).slider.value;
|
||||
const slider_val = slider_map.get(effect.slider_name).slider.value;
|
||||
const {round = true} = effect;
|
||||
let total = parseInt(slider_val) * effect.scaling[0];
|
||||
if (round) { total = Math.floor(round_near(total)); }
|
||||
if ('max' in effect && total > effect.max) { total = effect.max; }
|
||||
if (Array.isArray(effect.output)) {
|
||||
for (const output of effect.output) {
|
||||
|
@ -701,9 +773,12 @@ const atree_stats = new (class extends ComputeNode {
|
|||
else {
|
||||
// TODO: type: prop?
|
||||
let total = 0;
|
||||
const {round = true} = effect;
|
||||
for (const [scaling, input] of zip2(effect.scaling, effect.inputs)) {
|
||||
total += scaling * item_stats.get(input.name);
|
||||
}
|
||||
if (round) { total = Math.floor(round_near(total)); }
|
||||
if (total < 0) { total = 0; } // Normal stat scaling will not go negative.
|
||||
if ('max' in effect && total > effect.max) { total = effect.max; }
|
||||
// TODO: output (list...)
|
||||
if (Array.isArray(effect.output)) {
|
||||
|
@ -722,6 +797,10 @@ const atree_stats = new (class extends ComputeNode {
|
|||
continue;
|
||||
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
|
||||
|
@ -731,15 +810,16 @@ const atree_stats = new (class extends ComputeNode {
|
|||
}
|
||||
continue;
|
||||
case 'add_spell_prop':
|
||||
continue;
|
||||
// TODO unjankify....
|
||||
// costs are converted to raw cost ID
|
||||
const { base_spell, cost = 0} = effect;
|
||||
if (cost) {
|
||||
const key = "spRaw"+base_spell;
|
||||
if (ret_effects.has(key)) { ret_effects.set(key, ret_effects.get(key) + cost); }
|
||||
else { ret_effects.set(key, cost); }
|
||||
}
|
||||
continue;
|
||||
// const { base_spell, cost = 0} = effect;
|
||||
// if (cost) {
|
||||
// const key = "spRaw"+base_spell;
|
||||
// if (ret_effects.has(key)) { ret_effects.set(key, ret_effects.get(key) + cost); }
|
||||
// else { ret_effects.set(key, cost); }
|
||||
// }
|
||||
// continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -828,6 +908,12 @@ class AbilityTreeEnsureNodesNode extends ComputeNode {
|
|||
*/
|
||||
function render_AT(UI_elem, list_elem, tree) {
|
||||
console.log("constructing ability tree UI");
|
||||
|
||||
// increase padding, since images are larger than the space provided
|
||||
UI_elem.style.paddingRight = "calc(var(--bs-gutter-x) * 1)";
|
||||
UI_elem.style.paddingLeft = "calc(var(--bs-gutter-x) * 1)";
|
||||
UI_elem.style.paddingTop = "calc(var(--bs-gutter-x) * .5)";
|
||||
|
||||
// add in the "Active" title to atree
|
||||
let active_row = make_elem("div", ["row", "item-title", "mx-auto", "justify-content-center"]);
|
||||
let active_word = make_elem("div", ["col-auto"], {textContent: "Active Abilities:"});
|
||||
|
@ -837,7 +923,7 @@ function render_AT(UI_elem, list_elem, tree) {
|
|||
let active_AP_cost = make_elem("div", ["col-auto", "mx-0", "px-0"], {id: "active_AP_cost", textContent: "0"});
|
||||
|
||||
let active_AP_slash = make_elem("div", ["col-auto", "mx-0", "px-0"], {textContent: "/"});
|
||||
let active_AP_cap = make_elem("div", ["col-auto", "mx-0", "px-0"], {id: "active_AP_cap", textContent: "45"});
|
||||
let active_AP_cap = make_elem("div", ["col-auto", "mx-0", "px-0"], {id: "active_AP_cap"});
|
||||
let active_AP_end = make_elem("div", ["col-auto", "mx-0", "px-0"], {textContent: " AP"});
|
||||
|
||||
active_AP_container.appendChild(active_AP_subcontainer);
|
||||
|
@ -877,6 +963,7 @@ function render_AT(UI_elem, list_elem, tree) {
|
|||
for (let k = 0; k < 9; k++) {
|
||||
col = document.createElement('div');
|
||||
col.classList.add('col', 'px-0');
|
||||
col.style = "position: relative; aspect-ratio: 1/1;"
|
||||
row.appendChild(col);
|
||||
}
|
||||
UI_elem.appendChild(row);
|
||||
|
@ -894,7 +981,7 @@ function render_AT(UI_elem, list_elem, tree) {
|
|||
const parent_id = parent_abil.id;
|
||||
|
||||
let connect_elem = document.createElement("div");
|
||||
connect_elem.style = "background-size: cover; width: 100%; height: 100%;";
|
||||
connect_elem.style = "background-size: cover; width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated;";
|
||||
// connect up
|
||||
for (let i = ability.display.row - 1; i > parent_abil.display.row; i--) {
|
||||
const coord = i + "," + ability.display.col;
|
||||
|
@ -932,102 +1019,52 @@ function render_AT(UI_elem, list_elem, tree) {
|
|||
let node_elem = document.createElement('div');
|
||||
let icon = ability.display.icon;
|
||||
if (icon === undefined) {
|
||||
icon = "node_0";
|
||||
icon = "node";
|
||||
}
|
||||
let node_img = document.createElement('img');
|
||||
node_img.src = '../media/atree/'+icon+'.png';
|
||||
node_img.style = "width: 100%; height: 100%;";
|
||||
node_img.style = "width: 200%; height: 200%; position: absolute; top: -50%; left: -50%; image-rendering: pixelated; z-index: 1;";
|
||||
node_elem.appendChild(node_img);
|
||||
node_elem.classList.add("atree-circle");
|
||||
|
||||
// add node tooltip
|
||||
node_elem.addEventListener('mouseover', function(e) {
|
||||
if (e.target !== this) {return;}
|
||||
let tooltip = this.children[0];
|
||||
tooltip.style.top = this.getBoundingClientRect().bottom + window.scrollY * 1.02 + "px";
|
||||
tooltip.style.left = this.parentElement.parentElement.getBoundingClientRect().left + (elem.getBoundingClientRect().width * .2 / 2) + "px";
|
||||
tooltip.style.display = "block";
|
||||
});
|
||||
node_wrap.img = node_img;
|
||||
|
||||
node_elem.addEventListener('mouseout', function(e) {
|
||||
if (e.target !== this) {return;}
|
||||
let tooltip = this.children[0];
|
||||
tooltip.style.display = "none";
|
||||
});
|
||||
|
||||
//node tooltip and active tooltip have common parts - let's make them first
|
||||
|
||||
let tooltip_title = document.createElement('div');
|
||||
tooltip_title.classList.add("row", "justify-content-center", "fw-bold");
|
||||
tooltip_title.textContent = ability.display_name;
|
||||
|
||||
let tooltip_archetype;
|
||||
if ('archetype' in ability && ability.archetype !== "") {
|
||||
tooltip_archetype = document.createElement('div');
|
||||
tooltip_archetype.classList.add("row", "mx-1", "text-start");
|
||||
tooltip_archetype.textContent = "(Archetype: " + ability.archetype+")";
|
||||
}
|
||||
|
||||
let tooltip_desc = document.createElement('div');
|
||||
tooltip_desc.classList.add("row", "mx-1", "text-wrap");
|
||||
tooltip_desc.textContent = ability.desc;
|
||||
|
||||
let tooltip_cost = document.createElement('div');
|
||||
tooltip_cost.classList.add("row", "mx-1", "text-start");
|
||||
tooltip_cost.textContent = "Cost: " + ability.cost + " AP";
|
||||
|
||||
//create node tooltip
|
||||
let node_tooltip = document.createElement('div');
|
||||
node_tooltip.classList.add("rounded-bottom", "dark-4", "border", "p-0", "my-1", "dark-shadow", "scaled-font", "container");
|
||||
node_tooltip.style.display = "none";
|
||||
node_tooltip.append(tooltip_title, tooltip_archetype ? tooltip_archetype : "", tooltip_desc);
|
||||
|
||||
//active = copy of node
|
||||
let active_tooltip = node_tooltip.cloneNode(true);
|
||||
|
||||
//node tooltip specific stuff that we don't want to be copied
|
||||
node_tooltip.style.position = "absolute";
|
||||
node_tooltip.style.zIndex = "100";
|
||||
node_tooltip.appendChild(tooltip_cost);
|
||||
|
||||
//add in anything new for active tooltips
|
||||
active_tooltip.id = "atree-ab-" + ability.id;
|
||||
|
||||
if (ability.blockers.length > 0) {
|
||||
let active_tooltip_blockers = document.createElement("div");
|
||||
active_tooltip_blockers.classList.add("row", "mx-1", "text-start");
|
||||
active_tooltip_blockers.textContent = "Blockers: " + ability.blockers.join(", ");
|
||||
active_tooltip.append(active_tooltip_blockers);
|
||||
}
|
||||
|
||||
//append node and active tooltips to corresponding parent elems
|
||||
node_elem.appendChild(node_tooltip);
|
||||
//list_elem.appendChild(active_tooltip); NOTE: moved to `atree_render_active`
|
||||
// create hitbox
|
||||
// this is necessary since images exceed the size of their square, but should only be interactible within that square
|
||||
let hitbox = document.createElement("div");
|
||||
hitbox.style = "position: absolute; cursor: pointer; left: 0; top: 0; width: 100%; height: 100%; z-index: 2;"
|
||||
node_elem.appendChild(hitbox);
|
||||
|
||||
node_wrap.elem = node_elem;
|
||||
node_wrap.all_connectors_ref = atree_connectors_map;
|
||||
|
||||
node_elem.addEventListener('click', function(e) {
|
||||
hitbox.addEventListener('click', function(e) {
|
||||
if (e.target !== this && e.target!== this.children[0]) {return;}
|
||||
atree_set_state(node_wrap, !node_wrap.active);
|
||||
atree_state_node.mark_dirty().update();
|
||||
});
|
||||
|
||||
// add tooltip
|
||||
|
||||
node_elem.addEventListener('mouseover', function(e) {
|
||||
if (e.target !== this && e.target!== this.children[0]) {return;}
|
||||
let tooltip = this.children[this.children.length - 1];
|
||||
tooltip.style.top = this.getBoundingClientRect().bottom + window.scrollY * 1.02 + "px";
|
||||
tooltip.style.left = this.parentElement.parentElement.getBoundingClientRect().left + (elem.getBoundingClientRect().width * .2 / 2) + "px";
|
||||
tooltip.style.maxWidth = UI_elem.getBoundingClientRect().width * .95 + "px";
|
||||
tooltip.style.display = "block";
|
||||
// tooltips are being changed to generate on mouseover for fin444's future style updates
|
||||
// this is being implemented before those updates since it helps with a hotfix
|
||||
hitbox.addEventListener('mouseover', function(e) {
|
||||
if (e.target !== this) {
|
||||
return;
|
||||
}
|
||||
if (node_wrap.tooltip_elem) {
|
||||
node_wrap.tooltip_elem.remove();
|
||||
delete node_wrap.tooltip_elem;
|
||||
}
|
||||
node_wrap.tooltip_elem = generateTooltip(UI_elem, node_elem, ability);
|
||||
});
|
||||
|
||||
node_elem.addEventListener('mouseout', function(e) {
|
||||
if (e.target !== this && e.target!== this.children[0]) {return;}
|
||||
let tooltip = this.children[this.children.length - 1];
|
||||
tooltip.style.display = "none";
|
||||
hitbox.addEventListener('mouseout', function(e) {
|
||||
if (e.target !== this) {
|
||||
return;
|
||||
}
|
||||
if (node_wrap.tooltip_elem) {
|
||||
node_wrap.tooltip_elem.remove();
|
||||
delete node_wrap.tooltip_elem;
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("atree-row-" + ability.display.row).children[ability.display.col].appendChild(node_elem);
|
||||
|
@ -1037,6 +1074,44 @@ function render_AT(UI_elem, list_elem, tree) {
|
|||
return atree_map;
|
||||
};
|
||||
|
||||
function generateTooltip(UI_elem, node_elem, ability) {
|
||||
let tooltip = document.createElement('div');
|
||||
tooltip.classList.add("rounded-bottom", "dark-4", "border", "p-0", "mx-2", "my-4", "dark-shadow");
|
||||
|
||||
// tooltip text formatting
|
||||
|
||||
let tooltip_title = document.createElement('b');
|
||||
tooltip_title.classList.add("scaled-font");
|
||||
tooltip_title.innerHTML = ability.display_name;
|
||||
tooltip.appendChild(tooltip_title);
|
||||
|
||||
if ('archetype' in ability && ability.archetype !== "") {
|
||||
let tooltip_archetype = document.createElement('p');
|
||||
tooltip_archetype.classList.add("scaled-font");
|
||||
tooltip_archetype.innerHTML = "(Archetype: " + ability.archetype+")";
|
||||
tooltip.appendChild(tooltip_archetype);
|
||||
}
|
||||
|
||||
let tooltip_desc = document.createElement('p');
|
||||
tooltip_desc.classList.add("scaled-font-sm", "my-0", "mx-1", "text-wrap");
|
||||
tooltip_desc.textContent = ability.desc;
|
||||
tooltip.appendChild(tooltip_desc);
|
||||
|
||||
let tooltip_cost = document.createElement('p');
|
||||
tooltip_cost.classList.add("scaled-font-sm", "my-0", "mx-1", "text-start");
|
||||
tooltip_cost.textContent = "Cost: " + ability.cost + " AP";
|
||||
tooltip.appendChild(tooltip_cost);
|
||||
|
||||
tooltip.style.position = "absolute";
|
||||
tooltip.style.zIndex = "100";
|
||||
tooltip.style.top = (node_elem.getBoundingClientRect().top + window.pageYOffset + 50) + "px";
|
||||
tooltip.style.left = UI_elem.getBoundingClientRect().left + "px";
|
||||
tooltip.style.width = UI_elem.getBoundingClientRect().width * 0.95 + "px";
|
||||
|
||||
UI_elem.appendChild(tooltip);
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
// resolve connector conflict, when they occupy the same cell.
|
||||
function resolve_connector(atree_connectors_map, pos, new_connector) {
|
||||
if (!atree_connectors_map.has(pos)) {
|
||||
|
@ -1050,35 +1125,10 @@ function resolve_connector(atree_connectors_map, pos, new_connector) {
|
|||
}
|
||||
|
||||
function set_connector_type(connector_info) { // left right up down
|
||||
const connections = connector_info.connections;
|
||||
const connector_elem = connector_info.connector;
|
||||
let connector_dict = {
|
||||
"1100": {type: "line", rotate: 90},
|
||||
"1010": {type: "angle", rotate: 0},
|
||||
"1001": {type: "angle", rotate: 270},
|
||||
"0110": {type: "angle", rotate: 90},
|
||||
"0101": {type: "angle", rotate: 180},
|
||||
"0011": {type: "line", rotate: 0},
|
||||
"1110": {type: "t", rotate: 180},
|
||||
"1101": {type: "t", rotate: 0},
|
||||
"1011": {type: "t", rotate: 90},
|
||||
"0111": {type: "t", rotate: 270},
|
||||
"1111": {type: "c", rotate: 0}
|
||||
connector_info.type = "";
|
||||
for (let i = 0; i < 4; i++) {
|
||||
connector_info.type += connector_info.connections[i] == 0 ? "0" : "1";
|
||||
}
|
||||
|
||||
let lookup_str = "";
|
||||
for (let i of connections) {
|
||||
if (i != 0) {
|
||||
lookup_str += 1;
|
||||
} else {
|
||||
lookup_str += 0;
|
||||
}
|
||||
}
|
||||
|
||||
connector_info.type = connector_dict[lookup_str].type;
|
||||
connector_info.rotate = connector_dict[lookup_str].rotate;
|
||||
connector_elem.classList.add("rotate-" + connector_dict[lookup_str].rotate);
|
||||
|
||||
}
|
||||
|
||||
// draw the connector onto the screen
|
||||
|
@ -1103,13 +1153,17 @@ function atree_render_connection(atree_connectors_map) {
|
|||
|
||||
// toggle the state of a node.
|
||||
function atree_set_state(node_wrapper, new_state) {
|
||||
let icon = node_wrapper.ability.display.icon;
|
||||
if (icon === undefined) {
|
||||
icon = "node";
|
||||
}
|
||||
if (new_state) {
|
||||
node_wrapper.active = true;
|
||||
node_wrapper.elem.classList.add("atree-selected");
|
||||
node_wrapper.elem.children[0].src = "../media/atree/" + icon + "_selected.png";
|
||||
}
|
||||
else {
|
||||
node_wrapper.active = false;
|
||||
node_wrapper.elem.classList.remove("atree-selected");
|
||||
node_wrapper.elem.children[0].src = "../media/atree/" + icon + ".png";
|
||||
}
|
||||
let atree_connectors_map = node_wrapper.all_connectors_ref;
|
||||
for (const parent of node_wrapper.parents) {
|
||||
|
@ -1124,21 +1178,6 @@ function atree_set_state(node_wrapper, new_state) {
|
|||
}
|
||||
};
|
||||
|
||||
// refresh all connector to default state, then try to calculate the connector for all node
|
||||
function atree_update_connector() {
|
||||
atree_connectors_map.forEach((v) => {
|
||||
if (v.length != 0) {
|
||||
let connector_elem = document.createElement("img");
|
||||
connector_elem.style = "width: 100%; height: 100%;";
|
||||
connector_elem.src = '../media/atree/connect_' + v[0].type + '.png'
|
||||
v[0].replaceChildren(connector_elem);
|
||||
}
|
||||
});
|
||||
atree_map.forEach((v) => {
|
||||
atree_compute_highlight(v);
|
||||
});
|
||||
}
|
||||
|
||||
function atree_set_edge(atree_connectors_map, parent, child, state) {
|
||||
const connectors = child.connectors.get(parent);
|
||||
const parent_row = parent.ability.display.row;
|
||||
|
@ -1156,9 +1195,13 @@ function atree_set_edge(atree_connectors_map, parent, child, state) {
|
|||
let connector_img_elem = document.createElement("img");
|
||||
connector_img_elem.style = "width: 100%; height: 100%;";
|
||||
const ctype = connector_info.type;
|
||||
const rotate = connector_info.rotate;
|
||||
if (ctype === 't' || ctype === 'c') {
|
||||
// c, t
|
||||
let num_1s = 0;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (ctype.charAt(i) == "1") {
|
||||
num_1s++;
|
||||
}
|
||||
}
|
||||
if (num_1s > 2) { // t branch or 4-way
|
||||
const [connector_row, connector_col] = connector_label.split(',').map(x => parseInt(x));
|
||||
|
||||
if (connector_row === parent_row) {
|
||||
|
@ -1174,19 +1217,22 @@ function atree_set_edge(atree_connectors_map, parent, child, state) {
|
|||
highlight_state[child_side_idx] += state_delta;
|
||||
}
|
||||
|
||||
let render_state = highlight_state.map(x => (x > 0 ? 1 : 0));
|
||||
|
||||
let connector_img = atree_parse_connector(render_state, ctype, rotate);
|
||||
connector_img_elem.src = connector_img.img
|
||||
connector_elem.className = "";
|
||||
connector_elem.classList.add("rotate-" + connector_img.rotate);
|
||||
let render = "";
|
||||
for (let i = 0; i < 4; i++) {
|
||||
render += highlight_state[i] === 0 ? "0" : "1";
|
||||
}
|
||||
if (render == "0000") {
|
||||
connector_img_elem.src = "../media/atree/connect_" + ctype + ".png";
|
||||
} else {
|
||||
connector_img_elem.src = "../media/atree/connect_" + ctype + "_" + render + ".png";
|
||||
}
|
||||
connector_elem.replaceChildren(connector_img_elem);
|
||||
continue;
|
||||
}
|
||||
// lol bad overloading, [0] is just the whole state
|
||||
highlight_state[0] += state_delta;
|
||||
if (highlight_state[0] > 0) {
|
||||
connector_img_elem.src = '../media/atree/highlight_'+ctype+'.png';
|
||||
connector_img_elem.src = '../media/atree/connect_' + ctype + '_1.png';
|
||||
connector_elem.replaceChildren(connector_img_elem);
|
||||
}
|
||||
else {
|
||||
|
@ -1195,61 +1241,3 @@ function atree_set_edge(atree_connectors_map, parent, child, state) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse a sequence of left, right, up, down to appropriate connector image
|
||||
function atree_parse_connector(orient, type, rotate) {
|
||||
// left, right, up, down
|
||||
|
||||
let c_connector_dict = {
|
||||
"1100": {attrib: "_2_l", rotate: 0},
|
||||
"1010": {attrib: "_2_a", rotate: 0},
|
||||
"1001": {attrib: "_2_a", rotate: 270},
|
||||
"0110": {attrib: "_2_a", rotate: 90},
|
||||
"0101": {attrib: "_2_a", rotate: 180},
|
||||
"0011": {attrib: "_2_l", rotate: 90},
|
||||
"1110": {attrib: "_3", rotate: 0},
|
||||
"1101": {attrib: "_3", rotate: 180},
|
||||
"1011": {attrib: "_3", rotate: 270},
|
||||
"0111": {attrib: "_3", rotate: 90},
|
||||
"1111": {attrib: "", rotate: 0}
|
||||
};
|
||||
|
||||
let t_connector_dict = {
|
||||
0: {
|
||||
"1100": {attrib: "_2_l"},
|
||||
"1001": {attrib: "_2_a_f"},
|
||||
"0101": {attrib: "_2_a"},
|
||||
"1101": {attrib: "_3"},
|
||||
},
|
||||
90: {
|
||||
"1010": {attrib: "_2_a_f"},
|
||||
"1001": {attrib: "_2_a"},
|
||||
"0011": {attrib: "_2_l"},
|
||||
"1011": {attrib: "_3"}
|
||||
},
|
||||
270: {
|
||||
"0110": {attrib: "_2_a"},
|
||||
"0101": {attrib: "_2_a_f"},
|
||||
"0011": {attrib: "_2_l"},
|
||||
"0111": {attrib: "_3"}
|
||||
}
|
||||
};
|
||||
|
||||
let res = "";
|
||||
for (let i of orient) {
|
||||
res += i;
|
||||
}
|
||||
if (res === "0000") {
|
||||
return {img: "../media/atree/connect_" + type + ".png", rotate: rotate};
|
||||
}
|
||||
|
||||
let ret;
|
||||
if (type == "c") {
|
||||
ret = c_connector_dict[res];
|
||||
} else {
|
||||
ret = t_connector_dict[rotate][res];
|
||||
ret.rotate = rotate;
|
||||
};
|
||||
ret.img = "../media/atree/highlight_" + type + ret.attrib + ".png";
|
||||
return ret;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 9,
|
||||
"col": 6,
|
||||
"icon": "node_4"
|
||||
"icon": "node_archer"
|
||||
},
|
||||
"properties": {
|
||||
"charges": 2,
|
||||
|
@ -51,7 +51,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 7,
|
||||
"col": 4,
|
||||
"icon": "node_4"
|
||||
"icon": "node_archer"
|
||||
},
|
||||
"properties": {
|
||||
"aoe": 0,
|
||||
|
@ -76,7 +76,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"icon": "node_4"
|
||||
"icon": "node_archer"
|
||||
},
|
||||
"properties": {
|
||||
"aoe": 4.5,
|
||||
|
@ -88,7 +88,6 @@ const atrees = {
|
|||
"cost": 50,
|
||||
"base_spell": 3,
|
||||
"spell_type": "damage",
|
||||
"scaling": "spell",
|
||||
"display": "Total Damage",
|
||||
"parts": [
|
||||
{
|
||||
|
@ -228,7 +227,7 @@ const atrees = {
|
|||
"dependencies": [],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
"display": { "row": 9, "col": 2, "icon": "node_4"},
|
||||
"display": { "row": 9, "col": 2, "icon": "node_archer"},
|
||||
"properties": { "range": 16 },
|
||||
"effects": [
|
||||
{
|
||||
|
@ -237,7 +236,6 @@ const atrees = {
|
|||
"cost": 40,
|
||||
"base_spell": 1,
|
||||
"spell_type": "damage",
|
||||
"scaling": "spell",
|
||||
"display": "Total Damage",
|
||||
"parts": [
|
||||
{
|
||||
|
@ -318,23 +316,13 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 10,
|
||||
"col": 1,
|
||||
"icon": "node_0"
|
||||
"icon": "node_1"
|
||||
},
|
||||
"properties": {
|
||||
"aoe": 8,
|
||||
"duration": 120
|
||||
},
|
||||
"effects": [{
|
||||
"type": "stat_bonus",
|
||||
"toggle": "Windy Feet",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "spd",
|
||||
"value": 20
|
||||
}
|
||||
]
|
||||
}]
|
||||
"effects": []
|
||||
},
|
||||
{
|
||||
"display_name": "Basaltic Trap",
|
||||
|
@ -888,7 +876,7 @@ const atrees = {
|
|||
"effects": [
|
||||
{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 8,
|
||||
"base_spell": 7,
|
||||
"target_part": "Trap Damage",
|
||||
"cost": 0,
|
||||
"multipliers": [-80, 0, 0, 0, 0, 0]
|
||||
|
@ -922,18 +910,12 @@ const atrees = {
|
|||
"cost": 1,
|
||||
"display": { "row": 2, "col": 4, "icon": "node_0"},
|
||||
"properties": {},
|
||||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "mdPct",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"effects": [{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 0,
|
||||
"target_part": "Single Shot",
|
||||
"multipliers": [5, 0, 0, 0, 0, 0]
|
||||
}]
|
||||
},
|
||||
{
|
||||
"display_name": "Cheaper Arrow Bomb",
|
||||
|
@ -1233,17 +1215,7 @@ const atrees = {
|
|||
"properties": {
|
||||
"duration": 60
|
||||
},
|
||||
"effects": [{
|
||||
"type": "stat_bonus",
|
||||
"toggle": "Windy Feet",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "spdPct",
|
||||
"value": 20
|
||||
}
|
||||
]
|
||||
}]
|
||||
"effects": []
|
||||
},
|
||||
{
|
||||
"display_name": "Refined Gunpowder",
|
||||
|
@ -1961,7 +1933,7 @@ const atrees = {
|
|||
"parents": ["Cheaper Arrow Shield", "Cheaper Escape (2)"],
|
||||
"dependencies": ["Phantom Ray"],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
"cost": 2,
|
||||
"display": {
|
||||
"row": 34,
|
||||
"col": 5,
|
||||
|
@ -1992,7 +1964,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"icon": "node_4"
|
||||
"icon": "node_warrior"
|
||||
},
|
||||
"properties": {
|
||||
"aoe": 4,
|
||||
|
@ -2040,18 +2012,12 @@ const atrees = {
|
|||
"properties": {
|
||||
"melee_range": 1
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "mdPct",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"effects": [{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 0,
|
||||
"target_part": "melee",
|
||||
"multipliers": [5, 0, 0, 0, 0, 0]
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -2124,7 +2090,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 6,
|
||||
"col": 4,
|
||||
"icon": "node_4"
|
||||
"icon": "node_warrior"
|
||||
},
|
||||
"properties": {},
|
||||
"effects": [{
|
||||
|
@ -2273,7 +2239,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 8,
|
||||
"col": 2,
|
||||
"icon": "node_4"
|
||||
"icon": "node_warrior"
|
||||
},
|
||||
"properties": {
|
||||
"aoe": 3,
|
||||
|
@ -2336,7 +2302,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 8,
|
||||
"col": 6,
|
||||
"icon": "node_4"
|
||||
"icon": "node_warrior"
|
||||
},
|
||||
"properties": {
|
||||
"duration": 30,
|
||||
|
@ -2789,7 +2755,7 @@ const atrees = {
|
|||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"toggle": true,
|
||||
"toggle": "Activate Mantle",
|
||||
"bonuses": [{ "type": "stat", "name": "defMult.Mantle", "value": 70}]
|
||||
}
|
||||
]
|
||||
|
@ -2845,18 +2811,12 @@ const atrees = {
|
|||
"properties": {
|
||||
"melee_range": 1
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "mdPct",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"effects": [{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 0,
|
||||
"target_part": "melee",
|
||||
"multipliers": [5, 0, 0, 0, 0, 0]
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -3141,11 +3101,6 @@ const atrees = {
|
|||
"type": "add_spell_prop",
|
||||
"base_spell": 4,
|
||||
"cost": 10
|
||||
},
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"toggle": true,
|
||||
"bonuses": [ {"type": "stat", "name": "damMult.Ragnarokkr", "value": 30} ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3482,7 +3437,7 @@ const atrees = {
|
|||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"toggle": true,
|
||||
"toggle": "Activate Armor Breaker",
|
||||
"bonuses": [ {"type": "stat", "name": "damMult.ArmorBreaker", "value": 30} ]
|
||||
}
|
||||
]
|
||||
|
@ -3939,7 +3894,7 @@ const atrees = {
|
|||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"toggle": true,
|
||||
"toggle": "Activate Brink",
|
||||
"bonuses": [{ "type": "stat", "name": "defMult.Brink", "value": 40}]
|
||||
}
|
||||
]
|
||||
|
@ -4000,7 +3955,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"icon": "node_4"
|
||||
"icon": "node_mage"
|
||||
},
|
||||
"properties": {
|
||||
"aoe": 5,
|
||||
|
@ -4034,7 +3989,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 6,
|
||||
"col": 4,
|
||||
"icon": "node_4"
|
||||
"icon": "node_mage"
|
||||
},
|
||||
"properties": {
|
||||
"range": 12
|
||||
|
@ -4055,7 +4010,7 @@ const atrees = {
|
|||
"dependencies": [],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
"display": { "row": 8, "col": 2, "icon": "node_4"},
|
||||
"display": { "row": 8, "col": 2, "icon": "node_mage"},
|
||||
"properties": { "aoe": 5 },
|
||||
"effects": [
|
||||
{
|
||||
|
@ -4083,7 +4038,7 @@ const atrees = {
|
|||
"display": {
|
||||
"row": 8,
|
||||
"col": 6,
|
||||
"icon": "node_4"
|
||||
"icon": "node_mage"
|
||||
},
|
||||
"properties": {
|
||||
"range": 18,
|
||||
|
@ -4441,7 +4396,7 @@ const atrees = {
|
|||
"display_name": "Wind Slash",
|
||||
"desc": "When using Teleport, slash through the air and deal damage to enemies you pierce.",
|
||||
"archetype": "Riftwalker",
|
||||
"base_abil": "teleport",
|
||||
"base_abil": "Teleport",
|
||||
"parents": ["Air Mastery", "Thunderstorm"],
|
||||
"dependencies": ["Teleport"],
|
||||
"blockers": [],
|
||||
|
@ -4514,6 +4469,13 @@ const atrees = {
|
|||
"target_part": "Meteor Damage",
|
||||
"behavior": "modify",
|
||||
"multipliers": [30, 90, 0, 0, 0, 0]
|
||||
},
|
||||
{
|
||||
"type": "add_spell_prop",
|
||||
"base_spell": 3,
|
||||
"target_part": "Lightning Damage",
|
||||
"behavior": "modify",
|
||||
"multipliers": [30, 90, 0, 0, 0, 0]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -4586,17 +4548,39 @@ const atrees = {
|
|||
"archetype": "Riftwalker",
|
||||
"archetype_req": 3,
|
||||
"parents": ["Wind Slash", "Thunderstorm"],
|
||||
"dependencies": [],
|
||||
"dependencies": ["Ice Snake"],
|
||||
"blockers": [],
|
||||
"cost": 2,
|
||||
"display": {
|
||||
"row": 15,
|
||||
"col": 1,
|
||||
"icon": "node_3",
|
||||
"__TODO": "hppeng please fix"
|
||||
"icon": "node_3"
|
||||
},
|
||||
"properties": { "max": 5 },
|
||||
"effects": []
|
||||
"effects": [
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"output": {
|
||||
"type": "stat",
|
||||
"name": "nConvBase:4.Ice Snake Damage"
|
||||
},
|
||||
"scaling": [20],
|
||||
"slider_step": 1,
|
||||
"slider_max": 5
|
||||
},
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"output": {
|
||||
"type": "stat",
|
||||
"name": "wConvBase:4.Ice Snake Damage"
|
||||
},
|
||||
"scaling": [10]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Ophanim",
|
||||
|
@ -4715,7 +4699,7 @@ const atrees = {
|
|||
{
|
||||
"display_name": "Eye Piercer",
|
||||
"desc": "Teleport will blind enemies, confusing them for a short amount of time.",
|
||||
"base_abil": "Heal",
|
||||
"base_abil": "Teleport",
|
||||
"parents": ["Cheaper Heal"],
|
||||
"dependencies": ["Teleport"],
|
||||
"blockers": [],
|
||||
|
@ -4734,7 +4718,7 @@ const atrees = {
|
|||
"base_abil": "Windsweeper",
|
||||
"archetype": "Riftwalker",
|
||||
"archetype_req": 0,
|
||||
"parents": ["Cheaper Heal"],
|
||||
"parents": ["Cheaper Heal", "Purification"],
|
||||
"dependencies": ["Windsweeper"],
|
||||
"blockers": [],
|
||||
"cost": 2,
|
||||
|
@ -4744,8 +4728,40 @@ const atrees = {
|
|||
"icon": "node_1"
|
||||
},
|
||||
"properties": {},
|
||||
"effects": [],
|
||||
"__TODO": "I still don't really know how to do this, for the record though the ratio is 20% neutral 20% earth per stack"
|
||||
"effects": [
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"output": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "nConvBase:3.Meteor Damage"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "eConvBase:3.Meteor Damage"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "nConvBase:3.Per Orb"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "eConvBase:3.Per Orb"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "nConvBase:3.Lightning Damage"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "eConvBase:3.Lightning Damage"
|
||||
}
|
||||
],
|
||||
"scaling": [15]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Larger Heal",
|
||||
|
@ -4799,7 +4815,7 @@ const atrees = {
|
|||
"display_name": "Cheaper Ice Snake",
|
||||
"desc": "Reduce the Mana cost of Ice Snake.",
|
||||
"base_abil": "Ice Snake",
|
||||
"parents": ["Eye Piercer"],
|
||||
"parents": ["Eye Piercer", "Fortitude"],
|
||||
"dependencies": [],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
|
@ -4815,7 +4831,8 @@ const atrees = {
|
|||
"display_name": "Cheaper Teleport II",
|
||||
"desc": "Reduce the Mana cost of Teleport.",
|
||||
"base_abil": "Teleport",
|
||||
"parents": ["Purification", "Fortitude"],
|
||||
"parents": ["Purification"],
|
||||
"_parents": ["Purification", "Fortitude"],
|
||||
"dependencies": [],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
|
@ -4845,17 +4862,9 @@ const atrees = {
|
|||
"icon": "node_2"
|
||||
},
|
||||
"properties": {
|
||||
"duration": 4
|
||||
"duration": 5
|
||||
},
|
||||
"effects": [{
|
||||
"type": "raw_stat",
|
||||
"toggle": true,
|
||||
"bonuses": [{
|
||||
"type": "stat",
|
||||
"name": "damMult.Fortitude",
|
||||
"value": 100
|
||||
}]
|
||||
}]
|
||||
"effects": []
|
||||
},
|
||||
{
|
||||
"display_name": "Pyrokinesis",
|
||||
|
@ -4927,7 +4936,6 @@ const atrees = {
|
|||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"toggle": true,
|
||||
"bonuses": [{
|
||||
"type": "prop",
|
||||
"abil": "Teleport",
|
||||
|
@ -4940,7 +4948,7 @@ const atrees = {
|
|||
"behavior": "modify",
|
||||
"target_part": "Total Damage",
|
||||
"base_spell": 2,
|
||||
"hits": {"Wind Slash": 1}
|
||||
"hits": {"Wind Slash": 1, "Explosion Damage": 1}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -4999,10 +5007,11 @@ const atrees = {
|
|||
"effects": [{
|
||||
"type": "stat_scaling",
|
||||
"slider": false,
|
||||
"round": false,
|
||||
"inputs": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "wDam"
|
||||
"name": "wDamPct"
|
||||
}
|
||||
],
|
||||
"output": {
|
||||
|
@ -5028,7 +5037,56 @@ const atrees = {
|
|||
"icon": "node_2"
|
||||
},
|
||||
"properties": {},
|
||||
"effects": []
|
||||
"effects": [
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"output": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "nConvBase:2.Wind Slash"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "nConvBase:2.Explosion Damage"
|
||||
}
|
||||
],
|
||||
"scaling": [30]
|
||||
},
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"output": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "tConvBase:2.Wind Slash"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "tConvBase:2.Explosion Damage"
|
||||
}
|
||||
],
|
||||
"scaling": [10]
|
||||
},
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"output": [
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "aConvBase:2.Wind Slash"
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"name": "aConvBase:2.Explosion Damage"
|
||||
}
|
||||
],
|
||||
"scaling": [5]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Healthier Ophanim I",
|
||||
|
@ -5204,15 +5262,25 @@ const atrees = {
|
|||
"icon": "node_0"
|
||||
},
|
||||
"properties": {},
|
||||
"effects": [{
|
||||
"type": "raw_stat",
|
||||
"bonuses": [{
|
||||
"type": "prop",
|
||||
"abil": "Windsweeper",
|
||||
"name": "max",
|
||||
"value": 5
|
||||
}]
|
||||
}]
|
||||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "prop",
|
||||
"abil": "Windsweeper",
|
||||
"name": "max",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"slider_max": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Cheaper Ice Snake II",
|
||||
|
@ -5337,7 +5405,7 @@ const atrees = {
|
|||
"effects": [
|
||||
{
|
||||
"type": "add_spell_prop",
|
||||
"target_part": "Ice Snake",
|
||||
"target_part": "Ice Snake Damage",
|
||||
"base_spell": 4,
|
||||
"multipliers": [0, 0, 0, 0, 0, 20]
|
||||
}
|
||||
|
@ -5348,8 +5416,8 @@ const atrees = {
|
|||
"desc": "When sprinting, create an area that increases the speed of all allies the longer they run in it. (Step out or stop running to cancel)",
|
||||
"archetype": "Riftwalker",
|
||||
"archetype_req": 7,
|
||||
"parents": ["Cheaper Ice Snake II", "Explosive Entrance"],
|
||||
"dependencies": ["Ice Snake"],
|
||||
"parents": ["Cheaper Ice Snake II"],
|
||||
"dependencies": [],
|
||||
"blockers": [],
|
||||
"cost": 2,
|
||||
"display": {
|
||||
|
@ -5366,7 +5434,8 @@ const atrees = {
|
|||
"archetype": "Light Bender",
|
||||
"archetype_req": 0,
|
||||
"base_abil": "Ophanim",
|
||||
"parents": ["Explosive Entrance", "Cheaper Meteor II"],
|
||||
"parents": ["Cheaper Meteor II"],
|
||||
"_parents": ["Explosive Entrance", "Cheaper Meteor II"],
|
||||
"dependencies": ["Ophanim"],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
|
@ -5420,6 +5489,7 @@ const atrees = {
|
|||
"archetype": "Riftwalker",
|
||||
"archetype_req": 0,
|
||||
"parents": ["Time Dilation"],
|
||||
"_parents": ["Time Dilation", "Dynamic Faith"],
|
||||
"dependencies": ["Windsweeper"],
|
||||
"blockers": [],
|
||||
"cost": 1,
|
||||
|
@ -5429,7 +5499,25 @@ const atrees = {
|
|||
"icon": "node_0"
|
||||
},
|
||||
"properties": {},
|
||||
"effects": []
|
||||
"effects": [
|
||||
{
|
||||
"type": "raw_stat",
|
||||
"bonuses": [
|
||||
{
|
||||
"type": "prop",
|
||||
"abil": "Windsweeper",
|
||||
"name": "max",
|
||||
"value": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "stat_scaling",
|
||||
"slider": true,
|
||||
"slider_name": "Winded",
|
||||
"slider_max": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Dynamic Faith",
|
||||
|
@ -5553,7 +5641,7 @@ const atrees = {
|
|||
"display_name": "Arcane Overflow",
|
||||
"desc": "Arcane Transfer will allow you to overflow your mana over its maximum limits.",
|
||||
"archetype": "Arcanist",
|
||||
"archetype_req": 12,
|
||||
"archetype_req": 11,
|
||||
"base_abil": "Arcane Transfer",
|
||||
"parents": ["Larger Mana Bank III"],
|
||||
"dependencies": ["Arcane Transfer"],
|
||||
|
@ -5622,6 +5710,7 @@ const atrees = {
|
|||
{
|
||||
"type": "add_spell_prop",
|
||||
"target_part": "Orb Damage",
|
||||
"base_spell": 5,
|
||||
"hits": { "Single Orb": 2 }
|
||||
}
|
||||
]
|
||||
|
|
|
@ -30,7 +30,6 @@ class Build{
|
|||
this.level = level;
|
||||
} else if (typeof level === "string") {
|
||||
this.level = level;
|
||||
errors.push(new IncorrectInput(level, "a number", "level-choice"));
|
||||
} else {
|
||||
errors.push("Level is not a string or number.");
|
||||
}
|
||||
|
@ -129,6 +128,8 @@ class Build{
|
|||
}
|
||||
}
|
||||
statMap.set("poisonPct", 100);
|
||||
statMap.set("critDamPct", 100);
|
||||
statMap.set("healPct", 100);
|
||||
|
||||
// The stuff relevant for damage calculation!!! @ferricles
|
||||
statMap.set("atkSpd", this.weapon.statMap.get("atkSpd"));
|
||||
|
|
|
@ -7,8 +7,6 @@ const url_tag = location.hash.slice(1);
|
|||
|
||||
const BUILD_VERSION = "7.0.19";
|
||||
|
||||
let player_build;
|
||||
|
||||
|
||||
// THIS IS SUPER DANGEROUS, WE SHOULD NOT BE KEEPING THIS IN SO MANY PLACES
|
||||
let editable_item_fields = [ "sdPct", "sdRaw", "mdPct", "mdRaw", "poison",
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
let player_build;
|
||||
let build_powders;
|
||||
|
||||
function getItemNameFromID(id) { return idMap.get(id); }
|
||||
function getTomeNameFromID(id) { return tomeIDMap.get(id); }
|
||||
|
||||
function parsePowdering(powder_info) {
|
||||
// TODO: Make this run in linear instead of quadratic time... ew
|
||||
let powdering = [];
|
||||
|
@ -124,7 +130,6 @@ function decodeBuild(url_tag) {
|
|||
for (let i in tomes) {
|
||||
let tome_str = info[1].charAt(i);
|
||||
let tome_name = getTomeNameFromID(Base64.toInt(tome_str));
|
||||
console.log(tome_name);
|
||||
setValue(tomeInputs[i], tome_name);
|
||||
}
|
||||
info[1] = info[1].slice(7);
|
||||
|
|
|
@ -1,18 +1,3 @@
|
|||
let build_powders;
|
||||
|
||||
function getItemNameFromID(id) {
|
||||
if (redirectMap.has(id)) {
|
||||
return getItemNameFromID(redirectMap.get(id));
|
||||
}
|
||||
return idMap.get(id);
|
||||
}
|
||||
|
||||
function getTomeNameFromID(id) {
|
||||
if (tomeRedirectMap.has(id)) {
|
||||
return getTomeNameFromID(tomeRedirectMap.get(id));
|
||||
}
|
||||
return tomeIDMap.get(id);
|
||||
}
|
||||
|
||||
function populateBuildList() {
|
||||
const buildList = document.getElementById("build-choice");
|
||||
|
@ -148,7 +133,6 @@ function toggle_tab(tab) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function toggle_boost_tab(tab) {
|
||||
for (const i of skp_order) {
|
||||
document.querySelector("#"+i+"-boost").style.display = "none";
|
||||
|
@ -156,13 +140,10 @@ function toggle_boost_tab(tab) {
|
|||
}
|
||||
document.querySelector("#"+tab+"-boost").style.display = "";
|
||||
document.getElementById(tab + "-boost-tab").classList.add("selected-btn");
|
||||
|
||||
}
|
||||
|
||||
let tabs = ['overall-stats', 'offensive-stats', 'defensive-stats'];
|
||||
function show_tab(tab) {
|
||||
//console.log(itemFilters)
|
||||
|
||||
//hide all tabs, then show the tab of the div clicked and highlight the correct button
|
||||
for (const i in tabs) {
|
||||
document.querySelector("#" + tabs[i]).style.display = "none";
|
||||
|
@ -331,51 +312,6 @@ function init_autocomplete() {
|
|||
}));
|
||||
}
|
||||
|
||||
let filter_loc = ["filter1", "filter2", "filter3", "filter4"];
|
||||
for (const i of filter_loc) {
|
||||
dropdowns.set(i+"-choice", new autoComplete({
|
||||
data: {
|
||||
src: sq2ItemFilters,
|
||||
},
|
||||
selector: "#"+i+"-choice",
|
||||
wrapper: false,
|
||||
resultsList: {
|
||||
tabSelect: true,
|
||||
noResults: true,
|
||||
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
|
||||
element: (list, data) => {
|
||||
// dynamic result loc
|
||||
console.log(i);
|
||||
list.style.zIndex = "100";
|
||||
let position = document.getElementById(i+"-dropdown").getBoundingClientRect();
|
||||
window_pos = document.getElementById("search-container").getBoundingClientRect();
|
||||
list.style.top = position.bottom - window_pos.top + 5 +"px";
|
||||
list.style.left = position.x - window_pos.x +"px";
|
||||
list.style.width = position.width+"px";
|
||||
|
||||
if (!data.results.length) {
|
||||
message = document.createElement('li');
|
||||
message.classList.add('scaled-font');
|
||||
message.textContent = "No filters found!";
|
||||
list.prepend(message);
|
||||
}
|
||||
},
|
||||
},
|
||||
resultItem: {
|
||||
class: "scaled-font search-item",
|
||||
selected: "dark-5",
|
||||
},
|
||||
events: {
|
||||
input: {
|
||||
selection: (event) => {
|
||||
if (event.detail.selection.value) {
|
||||
event.target.value = event.detail.selection.value;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function collapse_element(elmnt) {
|
||||
|
@ -405,6 +341,7 @@ function init() {
|
|||
for (const eq of equipment_keys) {
|
||||
document.querySelector("#"+eq+"-tooltip").addEventListener("click", () => collapse_element('#'+eq+'-tooltip'));
|
||||
}
|
||||
// Armor Specials
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
const powder_special = powderSpecialStats[i];
|
||||
const elem_name = damageClasses[i+1]; // skip neutral
|
||||
|
@ -448,6 +385,15 @@ function init() {
|
|||
});
|
||||
decodeBuild(url_tag);
|
||||
builder_graph_init();
|
||||
for (const item_node of item_nodes) {
|
||||
if (item_node.get_value() === null) {
|
||||
// likely DB load failure...
|
||||
if (confirm('One or more items failed to load correctly. This could be due to a corrupted build link, or (more likely) a database load failure. Would you like to reload?')) {
|
||||
hardReload();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onerror = function(message, source, lineno, colno, error) {
|
||||
|
|
|
@ -23,7 +23,7 @@ let boosts_node = new (class extends ComputeNode {
|
|||
let elem = document.getElementById(key + "-boost")
|
||||
if (elem.classList.contains("toggleOn")) {
|
||||
damage_boost += value;
|
||||
if (key === "warscream") { def_boost += .20 }
|
||||
if (key === "warscream") { def_boost += .10 }
|
||||
if (key === "vanish") { def_boost += .15 }
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +192,13 @@ class ItemInputNode extends InputNode {
|
|||
|
||||
for (const [i, x] of zip2(equipment_inputs, replace_items)) { setValue(i, x); }
|
||||
|
||||
for (const node of item_nodes) { calcSchedule(node, 10); }
|
||||
for (const node of item_nodes) {
|
||||
if (node !== this) {
|
||||
// save a tiny bit of compute
|
||||
calcSchedule(node, 10);
|
||||
}
|
||||
}
|
||||
// Needed to push the weapon node's updates forward
|
||||
return this.compute_func(input_map);
|
||||
}
|
||||
return null;
|
||||
|
@ -401,7 +407,10 @@ class BuildAssembleNode extends ComputeNode {
|
|||
input_map.get('guildTome1-input')
|
||||
];
|
||||
let weapon = input_map.get('weapon-input');
|
||||
let level = input_map.get('level-input');
|
||||
let level = parseInt(input_map.get('level-input'));
|
||||
if (isNaN(level)) {
|
||||
level = 106;
|
||||
}
|
||||
|
||||
let all_none = weapon.statMap.has('NONE');
|
||||
for (const item of equipments) {
|
||||
|
@ -567,7 +576,7 @@ class SpellDamageCalcNode extends ComputeNode {
|
|||
}
|
||||
} else if ('power' in part) {
|
||||
// TODO: wynn2 formula
|
||||
let _heal_amount = (part.power * getDefenseStats(stats)[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100)));
|
||||
let _heal_amount = (part.power * getDefenseStats(stats)[0] * (stats.get('healPct')/100));
|
||||
spell_result = {
|
||||
type: "heal",
|
||||
heal_amount: _heal_amount
|
||||
|
@ -1007,6 +1016,9 @@ function builder_graph_init() {
|
|||
|
||||
// Level input node.
|
||||
let level_input = new InputNode('level-input', document.getElementById('level-choice'));
|
||||
|
||||
// linking to atree verification
|
||||
atree_validate.link_to(level_input, 'level');
|
||||
|
||||
// "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display.
|
||||
build_node = new BuildAssembleNode();
|
||||
|
@ -1036,9 +1048,6 @@ function builder_graph_init() {
|
|||
|
||||
// Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
|
||||
|
||||
let build_disp_node = new BuildDisplayNode()
|
||||
build_disp_node.link_to(build_node, 'build');
|
||||
|
||||
// Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap)
|
||||
stat_agg_node = new AggregateStatsNode();
|
||||
edit_agg_node = new AggregateEditableIDNode();
|
||||
|
@ -1065,16 +1074,13 @@ function builder_graph_init() {
|
|||
skp_inputs.push(node);
|
||||
}
|
||||
stat_agg_node.link_to(edit_agg_node);
|
||||
build_disp_node.link_to(stat_agg_node, 'stats');
|
||||
|
||||
// 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`
|
||||
atree_node.link_to(class_node, 'player-class');
|
||||
atree_merge.link_to(build_node, 'build');
|
||||
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
|
||||
.link_to(atree_collect_spells, 'spells');
|
||||
atree_merge.link_to(class_node, 'player-class');
|
||||
atree_stats.link_to(build_node, 'build');
|
||||
stat_agg_node.link_to(atree_stats, 'atree-stats');
|
||||
|
||||
|
@ -1089,6 +1095,9 @@ function builder_graph_init() {
|
|||
armor_powder_node.update();
|
||||
level_input.update();
|
||||
|
||||
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
|
||||
.link_to(atree_collect_spells, 'spells');
|
||||
|
||||
// kinda janky, manually set atree and update. Some wasted compute here
|
||||
if (atree_data !== null && atree_node.value !== null) { // janky check if atree is valid
|
||||
const atree_state = atree_state_node.value;
|
||||
|
@ -1114,10 +1123,14 @@ function builder_graph_init() {
|
|||
|
||||
// Also do something similar for skill points
|
||||
|
||||
let build_disp_node = new BuildDisplayNode()
|
||||
build_disp_node.link_to(build_node, 'build');
|
||||
build_disp_node.link_to(stat_agg_node, 'stats');
|
||||
|
||||
for (const node of edit_input_nodes) {
|
||||
node.update();
|
||||
}
|
||||
|
||||
|
||||
let skp_output = new SkillPointSetterNode(edit_input_nodes);
|
||||
skp_output.link_to(build_node);
|
||||
|
||||
|
@ -1133,5 +1146,6 @@ function builder_graph_init() {
|
|||
// this will propagate the update to the `stat_agg_node`, and then to damage calc
|
||||
|
||||
console.log("Set up graph");
|
||||
graph_live_update = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
let all_nodes = [];
|
||||
let node_debug_stack = [];
|
||||
class ComputeNode {
|
||||
/**
|
||||
* Make a generic compute node.
|
||||
|
@ -33,6 +34,7 @@ class ComputeNode {
|
|||
if (this.dirty === 0) {
|
||||
return;
|
||||
}
|
||||
node_debug_stack.push(this.name);
|
||||
if (this.dirty == 2) {
|
||||
let calc_inputs = new Map();
|
||||
for (const input of this.inputs) {
|
||||
|
@ -44,6 +46,7 @@ class ComputeNode {
|
|||
for (const child of this.children) {
|
||||
child.mark_input_clean(this.name, this.value);
|
||||
}
|
||||
node_debug_stack.pop();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -174,17 +177,20 @@ class ValueCheckComputeNode extends ComputeNode {
|
|||
|
||||
}
|
||||
|
||||
let graph_live_update = false;
|
||||
/**
|
||||
* Schedule a ComputeNode to be updated.
|
||||
*
|
||||
* @param node : ComputeNode to schedule an update for.
|
||||
*/
|
||||
function calcSchedule(node, timeout) {
|
||||
if (!graph_live_update) return;
|
||||
if (node.update_task !== null) {
|
||||
clearTimeout(node.update_task);
|
||||
}
|
||||
node.mark_dirty();
|
||||
node.update_task = setTimeout(function() {
|
||||
node_debug_stack = [];
|
||||
node.update();
|
||||
node.update_task = null;
|
||||
}, timeout);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.10], ["bash", 0.50] ]);
|
||||
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.00], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]);
|
||||
|
||||
function get_base_dps(item) {
|
||||
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];
|
||||
|
@ -26,7 +26,7 @@ function get_base_dps(item) {
|
|||
}
|
||||
|
||||
|
||||
function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, ignore_speed=false, part_filter=undefined) {
|
||||
function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ignore_speed=false, part_filter=undefined) {
|
||||
// TODO: Roll all the loops together maybe
|
||||
|
||||
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
|
||||
|
@ -40,7 +40,28 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
}
|
||||
let present = deepcopy(weapon.get(damage_present_key));
|
||||
|
||||
// Also theres prop and rainbow!!
|
||||
const damage_elements = ['n'].concat(skp_elements); // netwfa
|
||||
|
||||
// 2. Conversions.
|
||||
// 2.0: First, modify conversions.
|
||||
let conversions = deepcopy(_conversions);
|
||||
if (part_filter !== undefined) {
|
||||
const conv_postfix = ':'+part_filter;
|
||||
for (let i in damage_elements) {
|
||||
const stat_name = damage_elements[i]+'ConvBase'+conv_postfix;
|
||||
if (stats.has(stat_name)) {
|
||||
conversions[i] += stats.get(stat_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i in damage_elements) {
|
||||
const stat_name = damage_elements[i]+'ConvBase';
|
||||
if (stats.has(stat_name)) {
|
||||
conversions[i] += stats.get(stat_name);
|
||||
}
|
||||
}
|
||||
|
||||
// 2.1. First, apply neutral conversion (scale weapon damage). Keep track of total weapon damage here.
|
||||
let damages = [];
|
||||
const neutral_convert = conversions[0] / 100;
|
||||
|
@ -56,7 +77,7 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
|
||||
// 2.2. Next, apply elemental conversions using damage computed in step 1.1.
|
||||
// Also, track which elements are present. (Add onto those present in the weapon itself.)
|
||||
let total_convert = 0; //TODO get confirmation that this is how raw works.
|
||||
let total_convert = 0;
|
||||
for (let i = 1; i <= 5; ++i) {
|
||||
if (conversions[i] > 0) {
|
||||
const conv_frac = conversions[i]/100;
|
||||
|
@ -68,9 +89,6 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
}
|
||||
total_convert += conversions[0]/100;
|
||||
|
||||
// Also theres prop and rainbow!!
|
||||
const damage_elements = ['n'].concat(skp_elements); // netwfa
|
||||
|
||||
if (!ignore_speed) {
|
||||
// 3. Apply attack speed multiplier. Ignored for melee single hit
|
||||
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(weapon.get("atkSpd"))];
|
||||
|
@ -81,7 +99,7 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
}
|
||||
|
||||
// 4. Add additive damage. TODO: Is there separate additive damage?
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
for (let i in damage_elements) {
|
||||
if (present[i]) {
|
||||
damages[i][0] += stats.get(damage_elements[i]+'DamAddMin');
|
||||
damages[i][1] += stats.get(damage_elements[i]+'DamAddMax');
|
||||
|
@ -104,10 +122,10 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
// These do not count raw damage. I think. Easy enough to change
|
||||
let total_min = 0;
|
||||
let total_max = 0;
|
||||
for (let i in damages) {
|
||||
let damage_prefix = damage_elements[i] + specific_boost_str;
|
||||
for (let i in damage_elements) {
|
||||
let damage_specific = damage_elements[i] + specific_boost_str + 'Pct';
|
||||
let damageBoost = 1 + skill_boost[i] + static_boost
|
||||
+ ((stats.get(damage_prefix+'Pct') + stats.get(damage_elements[i]+'DamPct')) /100);
|
||||
+ ((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
|
||||
|
@ -133,13 +151,21 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
let min_boost = raw_boost;
|
||||
let max_boost = raw_boost;
|
||||
if (total_max > 0) { // TODO: what about total negative all raw?
|
||||
if (total_min > 0) {
|
||||
// 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;
|
||||
}
|
||||
else {
|
||||
min_boost += (damages_obj[0] / total_min) * prop_raw;
|
||||
}
|
||||
max_boost += (damages_obj[1] / total_max) * prop_raw;
|
||||
}
|
||||
if (i != 0 && total_elem_max > 0) { // rainraw TODO above
|
||||
if (total_elem_min > 0) {
|
||||
// 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;
|
||||
}
|
||||
else {
|
||||
min_boost += (damages_obj[0] / total_elem_min) * rainbow_raw;
|
||||
}
|
||||
max_boost += (damages_obj[1] / total_elem_max) * rainbow_raw;
|
||||
|
@ -167,12 +193,14 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
|
|||
damage_mult *= (1 + v/100);
|
||||
}
|
||||
|
||||
const crit_mult = stats.get("critDamPct")/100;
|
||||
|
||||
for (const damage of damages) {
|
||||
const res = [
|
||||
damage[0] * strBoost * damage_mult, // Normal min
|
||||
damage[1] * strBoost * damage_mult, // Normal max
|
||||
damage[0] * (strBoost + 1) * damage_mult, // Crit min
|
||||
damage[1] * (strBoost + 1) * damage_mult, // Crit max
|
||||
damage[0] * (strBoost + crit_mult) * damage_mult, // Crit min
|
||||
damage[1] * (strBoost + crit_mult) * damage_mult, // Crit max
|
||||
];
|
||||
damages_results.push(res);
|
||||
total_dam_norm[0] += res[0];
|
||||
|
|
277
js/display.js
|
@ -5,13 +5,10 @@ function apply_elemental_format(p_elem, id, suffix) {
|
|||
let parts = idPrefixes[id].split(/ (.*)/);
|
||||
let element_prefix = parts[0];
|
||||
let desc = parts[1];
|
||||
let i_elem = document.createElement('span');
|
||||
i_elem.classList.add(element_prefix);
|
||||
i_elem.textContent = element_prefix;
|
||||
let i_elem = make_elem('span', [element_prefix], {textContent: element_prefix});
|
||||
p_elem.appendChild(i_elem);
|
||||
|
||||
let i_elem2 = document.createElement('span');
|
||||
i_elem2.textContent = " " + desc + suffix;
|
||||
let i_elem2 = make_elem('span', [], {textContent: " "+desc+suffix});
|
||||
p_elem.appendChild(i_elem2);
|
||||
}
|
||||
|
||||
|
@ -19,17 +16,14 @@ function displaySetBonuses(parent_id,build) {
|
|||
setHTML(parent_id, "");
|
||||
let parent_div = document.getElementById(parent_id);
|
||||
|
||||
let set_summary_elem = document.createElement('p');
|
||||
set_summary_elem.classList.add('text-center');
|
||||
set_summary_elem.textContent = "Set Bonuses";
|
||||
let set_summary_elem = make_elem('p', ['text-center'], {textContent: "Set Bonuses"});
|
||||
parent_div.append(set_summary_elem);
|
||||
|
||||
for (const [setName, count] of build.activeSetCounts) {
|
||||
const active_set = sets.get(setName);
|
||||
if (active_set["hidden"]) { continue; }
|
||||
|
||||
let set_elem = document.createElement('p');
|
||||
set_elem.id = "set-"+setName;
|
||||
let set_elem = make_elem('p', [], {id: "set-"+setName});
|
||||
set_summary_elem.append(set_elem);
|
||||
|
||||
const bonus = active_set.bonuses[count-1];
|
||||
|
@ -111,36 +105,28 @@ function displayBuildStats(parent_id,build,command_group,stats){
|
|||
}
|
||||
displayFixedID(parent_div, id, id_val, elemental_format, style);
|
||||
if (id === "poison" && id_val > 0) {
|
||||
let row = document.createElement('div');
|
||||
row.classList.add("row")
|
||||
let value_elem = document.createElement('div');
|
||||
value_elem.classList.add('col');
|
||||
value_elem.classList.add('text-end');
|
||||
let row = make_elem('div', ['row']);
|
||||
let value_elem = make_elem('div', ['col', 'text-end']);
|
||||
|
||||
let prefix_elem = document.createElement('b');
|
||||
prefix_elem.textContent = "\u279C With Strength: ";
|
||||
let number_elem = document.createElement('b');
|
||||
number_elem.classList.add(style);
|
||||
number_elem.textContent = (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id];
|
||||
let prefix_elem = make_elem('b', [], {textContent: "\u279C With Strength: "});
|
||||
let number_elem = make_elem('b', [style], {
|
||||
textContent: (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id]
|
||||
});
|
||||
value_elem.append(prefix_elem);
|
||||
value_elem.append(number_elem);
|
||||
row.appendChild(value_elem);
|
||||
parent_div.appendChild(row);
|
||||
}
|
||||
else if (id === "ls" && id_val != 0) {
|
||||
let row = document.createElement('div');
|
||||
row.classList.add("row")
|
||||
let value_elem = document.createElement('div');
|
||||
value_elem.classList.add('col');
|
||||
value_elem.classList.add('text-end');
|
||||
let row = make_elem('div', ['row']);
|
||||
let value_elem = make_elem('div', ['col', 'text-end']);
|
||||
|
||||
let prefix_elem = document.createElement('b');
|
||||
prefix_elem.textContent = "\u279C Effective LS: ";
|
||||
let prefix_elem = make_elem('b', [], {textContent: "\u279C Effective LS: "});
|
||||
|
||||
let defStats = getDefenseStats(stats);
|
||||
let number_elem = document.createElement('b');
|
||||
number_elem.classList.add(style);
|
||||
number_elem.textContent = Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s";
|
||||
let number_elem = make_elem('b', [style], {
|
||||
textContent: Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s"
|
||||
});
|
||||
value_elem.append(prefix_elem);
|
||||
value_elem.append(number_elem);
|
||||
row.appendChild(value_elem);
|
||||
|
@ -195,8 +181,7 @@ function displayExpandedItem(item, parent_id){
|
|||
elemental_format = !elemental_format;
|
||||
}
|
||||
else if (command === "!spacer") {
|
||||
let spacer = document.createElement('div');
|
||||
spacer.classList.add("row", "my-2");
|
||||
let spacer = make_elem('div', ["row", "my-2"], {});
|
||||
parent_div.appendChild(spacer);
|
||||
continue;
|
||||
}
|
||||
|
@ -205,49 +190,41 @@ function displayExpandedItem(item, parent_id){
|
|||
let id = command;
|
||||
if(nonRolledIDs.includes(id)){//nonRolledID & non-0/non-null/non-und ID
|
||||
if (!item.get(id)) {
|
||||
if (! (item.get("crafted") && skp_order.includes(id) &&
|
||||
if (!(item.get("crafted") && skp_order.includes(id) &&
|
||||
(item.get("maxRolls").get(id) || item.get("minRolls").get(id)))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (id === "slots") {
|
||||
let p_elem = document.createElement("div");
|
||||
p_elem.classList.add("col");
|
||||
|
||||
let p_elem = make_elem("div", ["col"]);
|
||||
|
||||
// PROPER POWDER DISPLAYING
|
||||
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
|
||||
|
||||
let powderPrefix = document.createElement("b");
|
||||
powderPrefix.textContent = "Powder Slots: " + item.get(id) + " [";
|
||||
p_elem.appendChild(powderPrefix);
|
||||
p_elem.appendChild(make_elem("b", [], {
|
||||
textContent: "Powder Slots: " + item.get(id) + " ["
|
||||
}));
|
||||
|
||||
let powders = item.get("powders");
|
||||
for (let i = 0; i < powders.length; i++) {
|
||||
let powder = document.createElement("b");
|
||||
powder.textContent = numerals.get((powders[i]%6)+1)+" ";
|
||||
powder.classList.add(damageClasses[Math.floor(powders[i]/6)+1]+"_powder");
|
||||
p_elem.appendChild(powder);
|
||||
p_elem.appendChild(make_elem("b", [damageClasses[Math.floor(powders[i]/6)+1]+"_powder"], {
|
||||
textContent: numerals.get((powders[i]%6)+1)+" "
|
||||
}));
|
||||
}
|
||||
|
||||
let powderSuffix = document.createElement("b");
|
||||
powderSuffix.textContent = "]";
|
||||
p_elem.appendChild(powderSuffix);
|
||||
p_elem.appendChild(make_elem("b", [], { textContent: "]" }));
|
||||
parent_div.appendChild(p_elem);
|
||||
} else if (id === "set") {
|
||||
if (item.get("hideSet")) { continue; }
|
||||
|
||||
let p_elem = document.createElement("div");
|
||||
p_elem.classList.add("col");
|
||||
p_elem.textContent = "Set: " + item.get(id).toString();
|
||||
parent_div.appendChild(p_elem);
|
||||
parent_div.appendChild(make_elem("div", ["col"], { textContent: "Set: " + item.get(id).toString() }));
|
||||
} else if (id === "majorIds") {
|
||||
//console.log(item.get(id));
|
||||
for (let majorID of item.get(id)) {
|
||||
let p_elem = document.createElement("div");
|
||||
p_elem.classList.add("col");
|
||||
let p_elem = make_elem("div", ['col']);
|
||||
|
||||
let title_elem = document.createElement("b");
|
||||
let b_elem = document.createElement("b");
|
||||
let title_elem = make_elem("b");
|
||||
let b_elem = make_elem("b");
|
||||
if (majorID.includes(":")) {
|
||||
let name = majorID.substring(0, majorID.indexOf(":")+1);
|
||||
let mid = majorID.substring(majorID.indexOf(":")+1);
|
||||
|
@ -268,20 +245,30 @@ function displayExpandedItem(item, parent_id){
|
|||
parent_div.appendChild(p_elem);
|
||||
}
|
||||
} else if (id === "lvl" && item.get("tier") === "Crafted") {
|
||||
let p_elem = document.createElement("div");
|
||||
p_elem.classList.add("col");
|
||||
p_elem.textContent = "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id);
|
||||
parent_div.appendChild(p_elem);
|
||||
parent_div.appendChild(make_elem("div", ["col"], {
|
||||
textContent: "Combat Level Min: " + item.get("lvlLow") + "-" + item.get(id)
|
||||
}));
|
||||
} else if (id === "displayName") {
|
||||
let row = document.createElement("div");
|
||||
let row = make_elem("div", ["row", "justify-content-center"]);
|
||||
|
||||
let a_elem = document.createElement("a");
|
||||
row.classList.add("row", "justify-content-center");
|
||||
a_elem.classList.add("col-auto", "text-center", "item-title", "p-0");
|
||||
a_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal");
|
||||
// a_elem.style.textGrow = 1;
|
||||
|
||||
row.appendChild(a_elem);
|
||||
let nolink_row = make_elem("div", ["row", "justify-content-center"]);
|
||||
nolink_row.style.display = "none";
|
||||
|
||||
const tier_class = item.has("tier") ? item.get("tier").replace(" ","") : "Normal";
|
||||
let item_link;
|
||||
if (item.get("custom")) {
|
||||
item_link = "../custom/#" + item.get("hash");
|
||||
} else if (item.get("crafted")) {
|
||||
a_item_link = "../crafter/#" + item.get("hash");
|
||||
} else {
|
||||
item_link = "../item/#" + item.get("displayName");
|
||||
}
|
||||
const item_name_elem = make_elem("a", ["col-auto", "text-center", "item-title", "p-0", tier_class], {
|
||||
textContent: item.get('displayName')
|
||||
});
|
||||
nolink_row.appendChild(item_name_elem.cloneNode(true));
|
||||
item_name_elem.href = item_link;
|
||||
row.appendChild(item_name_elem);
|
||||
|
||||
/*
|
||||
FUNCTIONALITY FOR THIS FEATURE HAS SINCE BEEN REMOVED (WITH SQ2).
|
||||
|
@ -295,50 +282,23 @@ function displayExpandedItem(item, parent_id){
|
|||
//plusminus.style.flexGrow = 0;
|
||||
//plusminus.textContent = "\u2795";
|
||||
//row.appendChild(plusminus);
|
||||
|
||||
if (item.get("custom")) {
|
||||
a_elem.href = "../custom/#" + item.get("hash");
|
||||
a_elem.textContent = item.get("displayName");
|
||||
} else if (item.get("crafted")) {
|
||||
a_elem.href = "../crafter/#" + item.get("hash");
|
||||
a_elem.textContent = item.get(id);
|
||||
} else {
|
||||
a_elem.href = "../item/#" + item.get("displayName");
|
||||
a_elem.textContent = item.get("displayName");
|
||||
}
|
||||
parent_div.appendChild(row);
|
||||
|
||||
let nolink_row = document.createElement("div");
|
||||
let p_elem = document.createElement("p");
|
||||
nolink_row.classList.add("row", "justify-content-center");
|
||||
nolink_row.style.display = "none";
|
||||
p_elem.classList.add("col-auto", "text-center", "item-title", "p-0");
|
||||
p_elem.classList.add(item.has("tier") ? item.get("tier").replace(" ","") : "Normal");
|
||||
if (item.get("custom")) {
|
||||
p_elem.textContent = item.get("displayName");
|
||||
} else if (item.get("crafted")) {
|
||||
p_elem.textContent = item.get(id);
|
||||
} else {
|
||||
p_elem.textContent = item.get("displayName");
|
||||
}
|
||||
|
||||
nolink_row.appendChild(p_elem);
|
||||
parent_div.appendChild(nolink_row);
|
||||
|
||||
let img = document.createElement("img");
|
||||
if (item && item.has("type")) {
|
||||
img.src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png";
|
||||
img.alt = item.get("type");
|
||||
img.style = " z=index: 1; position: relative;";
|
||||
let container = document.createElement("div");
|
||||
if (item.has("type")) {
|
||||
let img = make_elem("img", [], {
|
||||
src: "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + item.get("type") + ".png",
|
||||
alt: item.get("type"),
|
||||
style: " z=index: 1; position: relative;"
|
||||
});
|
||||
let container = make_elem("div");
|
||||
|
||||
let bckgrd = document.createElement("div");
|
||||
bckgrd.classList.add("col", "px-0", "d-flex", "align-items-center", "justify-content-center");// , "no-collapse");
|
||||
bckgrd.style = "border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "hsl(0, 0%, 16%) 80%); margin-left: auto; margin-right: auto;"
|
||||
bckgrd.classList.add("scaled-bckgrd");
|
||||
parent_div.appendChild(container);
|
||||
container.appendChild(bckgrd);
|
||||
let bckgrd = make_elem("div", ["col", "px-0", "d-flex", "align-items-center", "justify-content-center", 'scaled-bckgrd'], { // , "no-collapse"
|
||||
style: "border-radius: 50%;background-image: radial-gradient(closest-side, " + colorMap.get(item.get("tier")) + " 20%," + "hsl(0, 0%, 16%) 80%); margin-left: auto; margin-right: auto;"
|
||||
});
|
||||
bckgrd.appendChild(img);
|
||||
container.appendChild(bckgrd);
|
||||
parent_div.appendChild(container);
|
||||
}
|
||||
} else {
|
||||
if (id.endsWith('Dam_')) {
|
||||
|
@ -365,8 +325,7 @@ function displayExpandedItem(item, parent_id){
|
|||
p_elem.style = "font-style: italic";
|
||||
} else if (skp_order.includes(id)) { //id = str, dex, int, def, or agi
|
||||
if ( item.get("tier") !== "Crafted") {
|
||||
row = document.createElement("div");
|
||||
row.classList.add("col");
|
||||
row = make_elem("div", ["col"]);
|
||||
|
||||
let title = document.createElement("b");
|
||||
title.textContent = idPrefixes[id] + " ";
|
||||
|
@ -550,9 +509,6 @@ function displayExpandedItem(item, parent_id){
|
|||
*/
|
||||
function displayRecipeStats(craft, parent_id) {
|
||||
let elem = document.getElementById(parent_id);
|
||||
if (!elem.classList.contains("col")) {
|
||||
elem.classList.add("col");
|
||||
}
|
||||
|
||||
//local vars
|
||||
elem.textContent = "";
|
||||
|
@ -565,91 +521,85 @@ function displayRecipeStats(craft, parent_id) {
|
|||
let effectiveness = craft["statMap"].get("ingredEffectiveness");
|
||||
|
||||
let title = document.createElement("div");
|
||||
title.classList.add("row", "box-title", "fw-bold", "justify-content-center");
|
||||
title.classList.add("col", "box-title", "fw-bold", "justify-content-center", "scaled-font");
|
||||
title.textContent = "Recipe Stats";
|
||||
elem.appendChild(title);
|
||||
|
||||
let mats = document.createElement("div");
|
||||
mats.classList.add("row");
|
||||
mats.classList.add("col");
|
||||
mats.textContent = "Crafting Materials: ";
|
||||
elem.appendChild(mats);
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let tier = mat_tiers[i];
|
||||
let row = document.createElement("div");
|
||||
row.classList.add("row", "px-0", "mx-0");
|
||||
let b = document.createElement("div");
|
||||
let col = document.createElement("div");
|
||||
col.classList.add("col", "ps-4");
|
||||
let b = document.createElement("span");
|
||||
let mat = recipe.get("materials")[i];
|
||||
b.textContent = "- " + mat.get("amount") + "x " + mat.get("item").split(" ").slice(1).join(" ");
|
||||
b.classList.add("col");
|
||||
row.appendChild(b);
|
||||
col.appendChild(b);
|
||||
|
||||
let starsB = document.createElement("div");
|
||||
starsB.classList.add("T1-bracket", "col-auto", "px-0");
|
||||
let starsContainer = document.createElement("span");
|
||||
let starsB = document.createElement("span");
|
||||
starsB.classList.add("T1-bracket", "px-0");
|
||||
starsB.textContent = "[";
|
||||
row.appendChild(starsB);
|
||||
starsContainer.appendChild(starsB);
|
||||
for(let j = 0; j < 3; j ++) {
|
||||
let star = document.createElement("div");
|
||||
star.classList.add("col-auto", "px-0");
|
||||
let star = document.createElement("span");
|
||||
star.classList.add("px-0");
|
||||
star.textContent = "\u272B";
|
||||
if(j < tier) {
|
||||
star.classList.add("T1");
|
||||
} else {
|
||||
star.classList.add("T0");
|
||||
}
|
||||
row.append(star);
|
||||
starsContainer.append(star);
|
||||
}
|
||||
let starsE = document.createElement("div");
|
||||
starsE.classList.add("T1-bracket", "col-auto", "px-0");
|
||||
let starsE = document.createElement("span");
|
||||
starsE.classList.add("T1-bracket", "px-0");
|
||||
starsE.textContent = "]";
|
||||
row.appendChild(starsE);
|
||||
starsContainer.appendChild(starsE);
|
||||
|
||||
elem.appendChild(row);
|
||||
col.appendChild(starsContainer);
|
||||
|
||||
elem.appendChild(col);
|
||||
}
|
||||
|
||||
let ingredTable = document.createElement("div");
|
||||
ingredTable.classList.add("row");
|
||||
ingredTable.classList.add("col", "mt-2");
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let row = document.createElement("div");
|
||||
row.classList.add("row", "g-1", "justify-content-center");
|
||||
let ingredContainer = document.createElement("div");
|
||||
ingredContainer.classList.add("row", "row-cols-2", "g-3");
|
||||
for (let i = 0; i < 6; i++) {
|
||||
let ingredCell = document.createElement("div");
|
||||
ingredCell.classList.add("col");
|
||||
|
||||
|
||||
for (let j = 0; j < 2; j++) {
|
||||
if (j == 1) {
|
||||
let spacer = document.createElement("div");
|
||||
spacer.classList.add("col-1");
|
||||
row.appendChild(spacer);
|
||||
}
|
||||
let ingredName = ingreds[2 * i + j];
|
||||
let col = document.createElement("div");
|
||||
col.classList.add("col-5", "rounded", "dark-6", "border", "border-3", "dark-shadow");
|
||||
let ingredTextContainer = document.createElement("div");
|
||||
ingredTextContainer.classList.add("border", "border-3", "rounded")
|
||||
|
||||
let temp_row = document.createElement("div");
|
||||
temp_row.classList.add("row");
|
||||
col.appendChild(temp_row);
|
||||
let ingredName = ingreds[i];
|
||||
let ingred_text = document.createElement("p");
|
||||
ingred_text.classList.add("mb-2", "ps-2");
|
||||
ingred_text.textContent = ingredName;
|
||||
ingredTextContainer.appendChild(ingred_text);
|
||||
|
||||
let ingred_div = document.createElement("div");
|
||||
ingred_div.classList.add("col");
|
||||
ingred_div.textContent = ingredName;
|
||||
temp_row.appendChild(ingred_div);
|
||||
|
||||
let eff_div = document.createElement("div");
|
||||
eff_div.classList.add("col-auto");
|
||||
let e = effectiveness[2 * i + j];
|
||||
if (e > 0) {
|
||||
eff_div.classList.add("positive");
|
||||
} else if (e < 0) {
|
||||
eff_div.classList.add("negative");
|
||||
}
|
||||
eff_div.textContent = "[" + e + "%]";
|
||||
|
||||
temp_row.appendChild(eff_div);
|
||||
|
||||
row.appendChild(col);
|
||||
let eff_div = document.createElement("p");
|
||||
eff_div.classList.add("mb-2", "ps-2");
|
||||
let e = effectiveness[i];
|
||||
if (e > 0) {
|
||||
eff_div.classList.add("positive");
|
||||
} else if (e < 0) {
|
||||
eff_div.classList.add("negative");
|
||||
}
|
||||
ingredTable.appendChild(row);
|
||||
eff_div.textContent = "[" + e + "%]";
|
||||
ingredTextContainer.appendChild(eff_div);
|
||||
|
||||
ingredCell.appendChild(ingredTextContainer);
|
||||
|
||||
ingredContainer.appendChild(ingredCell);
|
||||
}
|
||||
ingredTable.appendChild(ingredContainer);
|
||||
elem.appendChild(ingredTable);
|
||||
}
|
||||
|
||||
|
@ -877,7 +827,7 @@ function displayExpandedIngredient(ingred, parent_id) {
|
|||
row.appendChild(title);
|
||||
for(const skill of ingred.get("skills")) {
|
||||
let skill_div = document.createElement("div");
|
||||
skill_div.classList.add("row");
|
||||
skill_div.classList.add("row", "ps-4");
|
||||
skill_div.textContent = skill.charAt(0) + skill.substring(1).toLowerCase();
|
||||
row.appendChild(skill_div);
|
||||
}
|
||||
|
@ -1900,6 +1850,7 @@ function displayIDProbabilities(parent_id, item, amp) {
|
|||
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
|
||||
|
|
25
js/item.js
|
@ -20,14 +20,17 @@ function init_itempage() {
|
|||
//displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view");
|
||||
try{
|
||||
item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []);
|
||||
displaysq2ExpandedItem(item, "item-view");
|
||||
displaysq2AdditionalInfo("additional-info", item);
|
||||
displaysq2IDCosts("identification-costs", item);
|
||||
if (item.get("set") && sets[item.get("set")]) {
|
||||
displaysq2AllSetBonuses("set-bonus-info",item.get("set"));
|
||||
if (item.get('category') === 'weapon') {
|
||||
item.set('powders', []);
|
||||
apply_weapon_powders(item);
|
||||
}
|
||||
console.log(item);
|
||||
displaysq2IDProbabilities("identification-probabilities", item, amp_state);
|
||||
displayExpandedItem(item, "item-view");
|
||||
displayAdditionalInfo("additional-info", item);
|
||||
displayIDCosts("identification-costs", item);
|
||||
if (item.get("set") && sets[item.get("set")]) {
|
||||
displayAllSetBonuses("set-bonus-info",item.get("set"));
|
||||
}
|
||||
displayIDProbabilities("identification-probabilities", item, amp_state);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log(error.stack);
|
||||
|
@ -56,6 +59,8 @@ function toggleAmps(button_id) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
load_init(init_itempage);
|
||||
//load_ing_init(init);
|
||||
(async function() {
|
||||
let load_promises = [ load_init() ];
|
||||
await Promise.all(load_promises);
|
||||
init_itempage();
|
||||
})();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const DB_VERSION = 94;
|
||||
const DB_VERSION = 97;
|
||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
||||
|
||||
let db;
|
||||
|
|
|
@ -9,13 +9,13 @@ let iload_complete = false;
|
|||
let ings;
|
||||
let recipes;
|
||||
|
||||
let ingMap;
|
||||
let ingMap = new Map();
|
||||
let ingList = [];
|
||||
|
||||
let recipeMap;
|
||||
let recipeList = [];
|
||||
|
||||
let ingIDMap;
|
||||
let ingIDMap = new Map();
|
||||
let recipeIDMap;
|
||||
|
||||
/*
|
||||
|
@ -163,39 +163,41 @@ async function load_ing_init() {
|
|||
}
|
||||
|
||||
function init_ing_maps() {
|
||||
ingMap = new Map();
|
||||
recipeMap = new Map();
|
||||
ingIDMap = new Map();
|
||||
recipeIDMap = new Map();
|
||||
|
||||
let ing = Object();
|
||||
ing.name = "No Ingredient";
|
||||
ing.displayName = "No Ingredient";
|
||||
ing.tier = 0;
|
||||
ing.lvl = 0;
|
||||
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"];
|
||||
ing.ids= {};
|
||||
ing.itemIDs = {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
|
||||
ing.consumableIDs = {"dura": 0, "charges": 0};
|
||||
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
|
||||
ing.id = 4000;
|
||||
ingMap.set(ing["displayName"], ing);
|
||||
ingList.push(ing["displayName"]);
|
||||
ingIDMap.set(ing["id"], ing["displayName"]);
|
||||
let ing = {
|
||||
name: "No Ingredient",
|
||||
displayName: "No Ingredient",
|
||||
tier: 0,
|
||||
lvl: 0,
|
||||
skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING", "COOKING", "ALCHEMISM", "SCRIBING"],
|
||||
ids: {},
|
||||
itemIDs: {"dura": 0, "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,},
|
||||
consumableIDs: {"dura": 0, "charges": 0},
|
||||
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0},
|
||||
id: 4000
|
||||
};
|
||||
ingMap.set(ing.displayName, ing);
|
||||
ingList.push(ing.displayName);
|
||||
ingIDMap.set(ing.id, ing.displayName);
|
||||
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
|
||||
for (let i = 0; i < 5; i ++) {
|
||||
for (const powderIng of powderIngreds) {
|
||||
let ing = Object();
|
||||
ing.name = "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1);
|
||||
ing.displayName = ing.name
|
||||
ing.tier = 0;
|
||||
ing.lvl = 0;
|
||||
ing.skills = ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"];
|
||||
ing.ids = {};
|
||||
ing.isPowder = true;
|
||||
ing.pid = 6*i + powderIngreds.indexOf(powderIng);
|
||||
let ing = {
|
||||
name: "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1),
|
||||
tier: 0,
|
||||
lvl: 0,
|
||||
skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"],
|
||||
ids: {},
|
||||
isPowder: true,
|
||||
pid: 6*i + powderIngreds.indexOf(powderIng),
|
||||
itemIDs: {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0},
|
||||
consumableIDs: {"dura": 0, "charges": 0},
|
||||
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}
|
||||
};
|
||||
ing.id = 4001 + ing.pid;
|
||||
ing.itemIDs = {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0,};
|
||||
ing.diplayName = ing.name;
|
||||
switch(i) {
|
||||
case 0:
|
||||
ing.itemIDs["strReq"] = powderIng["skpReq"];
|
||||
|
@ -213,24 +215,21 @@ function init_ing_maps() {
|
|||
ing.itemIDs["agiReq"] = powderIng["skpReq"];
|
||||
break;
|
||||
}
|
||||
ing.consumableIDs = {"dura": 0, "charges": 0};
|
||||
ing.posMods = {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0};
|
||||
ingMap.set(ing["displayName"],ing);
|
||||
ingList.push(ing["displayName"]);
|
||||
ingIDMap.set(ing["id"], ing["displayName"]);
|
||||
ingMap.set(ing.displayName, ing);
|
||||
ingList.push(ing.displayName);
|
||||
ingIDMap.set(ing.id, ing.displayName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const ing of ings) {
|
||||
ingMap.set(ing["displayName"], ing);
|
||||
ingList.push(ing["displayName"]);
|
||||
ingIDMap.set(ing["id"], ing["displayName"]);
|
||||
ingMap.set(ing.displayName, ing);
|
||||
ingList.push(ing.displayName);
|
||||
ingIDMap.set(ing.id, ing.displayName);
|
||||
}
|
||||
for (const recipe of recipes) {
|
||||
recipeMap.set(recipe["name"], recipe);
|
||||
recipeList.push(recipe["name"]);
|
||||
recipeIDMap.set(recipe["id"],recipe["name"]);
|
||||
recipeMap.set(recipe.name, recipe);
|
||||
recipeList.push(recipe.name);
|
||||
recipeIDMap.set(recipe.id, recipe.name);
|
||||
}
|
||||
console.log(ingMap);
|
||||
}
|
||||
|
|
|
@ -1,98 +1,116 @@
|
|||
/**
|
||||
* Apply skillpoint bonuses from an item.
|
||||
* Also applies set deltas.
|
||||
* Modifies the skillpoints array.
|
||||
*/
|
||||
function apply_skillpoints(skillpoints, item, activeSetCounts) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
skillpoints[i] += item.skillpoints[i];
|
||||
}
|
||||
|
||||
const setName = item.set;
|
||||
if (setName) { // undefined/null means no set.
|
||||
let setCount = activeSetCounts.get(setName);
|
||||
let old_bonus = {};
|
||||
if (setCount) {
|
||||
old_bonus = sets.get(setName).bonuses[setCount-1];
|
||||
activeSetCounts.set(setName, setCount + 1);
|
||||
}
|
||||
else {
|
||||
setCount = 0;
|
||||
activeSetCounts.set(setName, 1);
|
||||
}
|
||||
const new_bonus = sets.get(setName).bonuses[setCount];
|
||||
//let skp_order = ["str","dex","int","def","agi"];
|
||||
for (const i in skp_order) {
|
||||
const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
||||
skillpoints[i] += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply skillpoints until this item can be worn.
|
||||
* Also applies set deltas.
|
||||
* Confusingly, does not modify the skillpoints array.
|
||||
* Instead, return an array of deltas.
|
||||
*/
|
||||
function apply_to_fit(skillpoints, item, skillpoint_min, activeSetCounts) {
|
||||
let applied = [0, 0, 0, 0, 0];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (item.skillpoints[i] < 0 && skillpoint_min[i]) {
|
||||
const unadjusted = skillpoints[i] + item.skillpoints[i];
|
||||
const delta = skillpoint_min[i] - unadjusted;
|
||||
if (delta > 0) {
|
||||
applied[i] += delta;
|
||||
}
|
||||
}
|
||||
if (item.reqs[i] == 0) continue;
|
||||
skillpoint_min[i] = Math.max(skillpoint_min[i], item.reqs[i] + item.skillpoints[i]);
|
||||
const req = item.reqs[i];
|
||||
const cur = skillpoints[i];
|
||||
if (req > cur) {
|
||||
const diff = req - cur;
|
||||
applied[i] += diff;
|
||||
}
|
||||
}
|
||||
|
||||
const setName = item.set;
|
||||
if (setName) { // undefined/null means no set.
|
||||
const setCount = activeSetCounts.get(setName);
|
||||
if (setCount) {
|
||||
const old_bonus = sets.get(setName).bonuses[setCount-1];
|
||||
const new_bonus = sets.get(setName).bonuses[setCount];
|
||||
//let skp_order = ["str","dex","int","def","agi"];
|
||||
for (const i in skp_order) {
|
||||
const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
||||
if (set_delta < 0 && skillpoint_min[i]) {
|
||||
const unadjusted = skillpoints[i] + set_delta;
|
||||
const delta = skillpoint_min[i] - unadjusted;
|
||||
if (delta > 0) {
|
||||
applied[i] += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
|
||||
function calculate_skillpoints(equipment, weapon) {
|
||||
// const start = performance.now();
|
||||
// Calculate equipment equipping order and required skillpoints.
|
||||
// Return value: [equip_order, best_skillpoints, final_skillpoints, best_total];
|
||||
let fixed = [];
|
||||
let consider = [];
|
||||
let noboost = [];
|
||||
let crafted = [];
|
||||
weapon.skillpoints = weapon.get('skillpoints');
|
||||
weapon.reqs = weapon.get('reqs');
|
||||
weapon.set = weapon.get('set');
|
||||
for (const item of equipment) {
|
||||
item.skillpoints = item.get('skillpoints');
|
||||
item.reqs = item.get('reqs');
|
||||
item.set = item.get('set');
|
||||
if (item.get("crafted")) {
|
||||
crafted.push(item);
|
||||
}
|
||||
else if (item.get("reqs").every(x => x === 0) && item.get("skillpoints").every(x => x >= 0)) {
|
||||
// TODO hack: We will treat ALL set items as unsafe :(
|
||||
else if (item.set !== null) {
|
||||
consider.push(item);
|
||||
}
|
||||
else if (item.get("reqs").every(x => x === 0) && item.skillpoints.every(x => x >= 0)) {
|
||||
// All reqless item without -skillpoints.
|
||||
fixed.push(item);
|
||||
}
|
||||
// TODO hack: We will treat ALL set items as unsafe :(
|
||||
else if (item.get("skillpoints").every(x => x === 0) && item.get("set") === null) {
|
||||
else if (item.skillpoints.every(x => x <= 0)) {
|
||||
noboost.push(item);
|
||||
}
|
||||
else {
|
||||
consider.push(item);
|
||||
}
|
||||
}
|
||||
function apply_skillpoints(skillpoints, item, activeSetCounts) {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
skillpoints[i] += item.get("skillpoints")[i];
|
||||
}
|
||||
|
||||
const setName = item.get("set");
|
||||
if (setName) { // undefined/null means no set.
|
||||
let setCount = activeSetCounts.get(setName);
|
||||
let old_bonus = {};
|
||||
if (setCount) {
|
||||
old_bonus = sets.get(setName).bonuses[setCount-1];
|
||||
activeSetCounts.set(setName, setCount + 1);
|
||||
}
|
||||
else {
|
||||
setCount = 0;
|
||||
activeSetCounts.set(setName, 1);
|
||||
}
|
||||
const new_bonus = sets.get(setName).bonuses[setCount];
|
||||
//let skp_order = ["str","dex","int","def","agi"];
|
||||
for (const i in skp_order) {
|
||||
const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
||||
skillpoints[i] += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function apply_to_fit(skillpoints, item, skillpoint_min, activeSetCounts) {
|
||||
let applied = [0, 0, 0, 0, 0];
|
||||
let total = 0;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (item.get("skillpoints")[i] < 0 && skillpoint_min[i]) {
|
||||
const unadjusted = skillpoints[i] + item.get("skillpoints")[i];
|
||||
const delta = skillpoint_min[i] - unadjusted;
|
||||
if (delta > 0) {
|
||||
applied[i] += delta;
|
||||
total += delta;
|
||||
}
|
||||
}
|
||||
if (item.get("reqs")[i] == 0) continue;
|
||||
skillpoint_min[i] = Math.max(skillpoint_min[i], item.get("reqs")[i] + item.get("skillpoints")[i]);
|
||||
const req = item.get("reqs")[i];
|
||||
const cur = skillpoints[i];
|
||||
if (req > cur) {
|
||||
const diff = req - cur;
|
||||
applied[i] += diff;
|
||||
total += diff;
|
||||
}
|
||||
}
|
||||
|
||||
const setName = item.get("set");
|
||||
if (setName) { // undefined/null means no set.
|
||||
const setCount = activeSetCounts.get(setName);
|
||||
if (setCount) {
|
||||
const old_bonus = sets.get(setName).bonuses[setCount-1];
|
||||
const new_bonus = sets.get(setName).bonuses[setCount];
|
||||
//let skp_order = ["str","dex","int","def","agi"];
|
||||
for (const i in skp_order) {
|
||||
const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
||||
if (set_delta < 0 && skillpoint_min[i]) {
|
||||
const unadjusted = skillpoints[i] + set_delta;
|
||||
const delta = skillpoint_min[i] - unadjusted;
|
||||
if (delta > 0) {
|
||||
applied[i] += delta;
|
||||
total += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [applied, total];
|
||||
}
|
||||
|
||||
// Separate out the no req items and add them to the static skillpoint base.
|
||||
let static_skillpoints_base = [0, 0, 0, 0, 0]
|
||||
|
@ -101,7 +119,7 @@ function calculate_skillpoints(equipment, weapon) {
|
|||
apply_skillpoints(static_skillpoints_base, item, static_activeSetCounts);
|
||||
}
|
||||
|
||||
let best = consider.concat(noboost);
|
||||
let best = consider;
|
||||
let final_skillpoints = static_skillpoints_base.slice();
|
||||
let best_skillpoints = [0, 0, 0, 0, 0];
|
||||
let best_total = Infinity;
|
||||
|
@ -110,100 +128,175 @@ function calculate_skillpoints(equipment, weapon) {
|
|||
let allFalse = [0, 0, 0, 0, 0];
|
||||
if (consider.length > 0 || noboost.length > 0 || crafted.length > 0) {
|
||||
// Try every combination and pick the best one.
|
||||
for (let permutation of perm(consider)) {
|
||||
let activeSetCounts = new Map(static_activeSetCounts);
|
||||
const [root, terminal, sccs] = construct_scc_graph(consider);
|
||||
const end_checks = crafted.concat(noboost);
|
||||
end_checks.push(weapon);
|
||||
|
||||
let has_skillpoint = allFalse.slice();
|
||||
|
||||
permutation = permutation.concat(noboost);
|
||||
|
||||
let skillpoints_applied = [0, 0, 0, 0, 0];
|
||||
// Complete slice is a shallow copy.
|
||||
let skillpoints = static_skillpoints_base.slice();
|
||||
|
||||
let total_applied = 0;
|
||||
|
||||
let result;
|
||||
let needed_skillpoints;
|
||||
let total_diff;
|
||||
for (const item of permutation) {
|
||||
result = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts);
|
||||
needed_skillpoints = result[0];
|
||||
total_diff = result[1];
|
||||
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
skillpoints_applied[i] += needed_skillpoints[i];
|
||||
skillpoints[i] += needed_skillpoints[i];
|
||||
}
|
||||
apply_skillpoints(skillpoints, item, activeSetCounts);
|
||||
total_applied += total_diff;
|
||||
if (total_applied >= best_total) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function check_end(skillpoints_applied, skillpoints, activeSetCounts, total_applied) {
|
||||
// Crafted skillpoint does not count initially.
|
||||
for (const item of crafted) {
|
||||
//console.log(item)
|
||||
result = apply_to_fit(skillpoints, item, allFalse.slice(), activeSetCounts);
|
||||
//console.log(result)
|
||||
needed_skillpoints = result[0];
|
||||
total_diff = result[1];
|
||||
for (const item of end_checks) {
|
||||
const needed_skillpoints = apply_to_fit(skillpoints, item,
|
||||
[false, false, false, false, false], activeSetCounts);
|
||||
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
skillpoints_applied[i] += needed_skillpoints[i];
|
||||
skillpoints[i] += needed_skillpoints[i];
|
||||
const skp = needed_skillpoints[i]
|
||||
skillpoints_applied[i] += skp;
|
||||
skillpoints[i] += skp;
|
||||
total_applied += skp;
|
||||
}
|
||||
total_applied += total_diff;
|
||||
}
|
||||
|
||||
if (total_applied >= best_total) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pre = skillpoints.slice();
|
||||
result = apply_to_fit(skillpoints, weapon, allFalse.slice(), activeSetCounts);
|
||||
needed_skillpoints = result[0];
|
||||
total_diff = result[1];
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
skillpoints_applied[i] += needed_skillpoints[i];
|
||||
skillpoints[i] += needed_skillpoints[i];
|
||||
}
|
||||
|
||||
apply_skillpoints(skillpoints, weapon, activeSetCounts);
|
||||
total_applied += total_diff;
|
||||
|
||||
// Applying crafted item skill points last.
|
||||
for (const item of crafted) {
|
||||
apply_skillpoints(skillpoints, item, activeSetCounts);
|
||||
//total_applied += total_diff;
|
||||
}
|
||||
|
||||
if (total_applied < best_total) {
|
||||
best = permutation;
|
||||
final_skillpoints = skillpoints;
|
||||
best_skillpoints = skillpoints_applied;
|
||||
best_total = total_applied;
|
||||
best_activeSetCounts = activeSetCounts;
|
||||
if (best_total < total_applied) { return -1; }
|
||||
}
|
||||
return total_applied;
|
||||
}
|
||||
|
||||
function permute_check(idx, _applied, _skillpoints, _sets, _has, _total_applied, order) {
|
||||
const {nodes, children} = sccs[idx];
|
||||
if (nodes[0] === terminal) {
|
||||
const total = check_end(_applied, _skillpoints, _sets, _total_applied);
|
||||
if (total !== -1 && total < best_total) {
|
||||
final_skillpoints = _skillpoints;
|
||||
best_skillpoints = _applied;
|
||||
best_total = total;
|
||||
best_activeSetCounts = _sets;
|
||||
best = order;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (let permutation of perm(nodes)) {
|
||||
const skillpoints_applied = _applied.slice();
|
||||
const skillpoints = _skillpoints.slice();
|
||||
const activeSetCounts = new Map(_sets);
|
||||
const has_skillpoint = _has.slice();
|
||||
let total_applied = _total_applied;
|
||||
let short_circuit = false;
|
||||
for (const {item} of permutation) {
|
||||
needed_skillpoints = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts);
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
skp = needed_skillpoints[i];
|
||||
skillpoints_applied[i] += skp;
|
||||
skillpoints[i] += skp;
|
||||
total_applied += skp;
|
||||
}
|
||||
if (total_applied >= best_total) {
|
||||
short_circuit = true;
|
||||
break; // short circuit failure
|
||||
}
|
||||
apply_skillpoints(skillpoints, item, activeSetCounts);
|
||||
}
|
||||
if (short_circuit) { continue; }
|
||||
permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item)));
|
||||
}
|
||||
}
|
||||
// skip root.
|
||||
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []);
|
||||
|
||||
// add extra sp bonus
|
||||
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);
|
||||
// Applying crafted item skill points last.
|
||||
for (const item of crafted) {
|
||||
apply_skillpoints(final_skillpoints, item, best_activeSetCounts);
|
||||
}
|
||||
}
|
||||
else {
|
||||
best_total = 0;
|
||||
result = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts);
|
||||
needed_skillpoints = result[0];
|
||||
total_diff = result[1];
|
||||
needed_skillpoints = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts);
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
best_skillpoints[i] += needed_skillpoints[i];
|
||||
final_skillpoints[i] += needed_skillpoints[i];
|
||||
const skp = needed_skillpoints[i];
|
||||
best_skillpoints[i] += skp;
|
||||
final_skillpoints[i] += skp;
|
||||
best_total += skp;
|
||||
}
|
||||
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);
|
||||
best_total += total_diff;
|
||||
}
|
||||
let equip_order = fixed.concat(best).concat(crafted);
|
||||
let equip_order = fixed.concat(best).concat(noboost).concat(crafted);
|
||||
// best_skillpoints: manually assigned (before any gear)
|
||||
// final_skillpoints: final totals (5 individ)
|
||||
// best_total: total skillpoints assigned (number)
|
||||
// const end = performance.now();
|
||||
// const output_msg = `skillpoint calculation took ${(end-start)/ 1000} seconds.`;
|
||||
// console.log(output_msg);
|
||||
return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts];
|
||||
}
|
||||
|
||||
function construct_scc_graph(items_to_consider) {
|
||||
let nodes = [];
|
||||
let terminal_node = {
|
||||
item: null,
|
||||
children: [],
|
||||
parents: nodes,
|
||||
visited: false,
|
||||
assigned: false,
|
||||
scc: null
|
||||
};
|
||||
let root_node = {
|
||||
item: null,
|
||||
children: nodes,
|
||||
parents: [],
|
||||
visited: false,
|
||||
assigned: false,
|
||||
scc: null
|
||||
};
|
||||
for (const item of items_to_consider) {
|
||||
nodes.push({item: item, children: [terminal_node], parents: [root_node], visited: false, assigned: false, scc: null});
|
||||
}
|
||||
// Dependency graph construction.
|
||||
for (const node_a of nodes) {
|
||||
const {item: a, children: a_children} = node_a;
|
||||
for (const node_b of nodes) {
|
||||
const {item: b, parents: b_parents} = node_b;
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
if (a.skillpoints[i] > 0 && (a.reqs[i] < b.reqs[i] || b.skillpoints[i] < 0)) {
|
||||
a_children.push(node_b);
|
||||
b_parents.push(node_a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const res = []
|
||||
/*
|
||||
* SCC graph construction.
|
||||
* https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
|
||||
*/
|
||||
function visit(u, res) {
|
||||
if (u.visited) { return; }
|
||||
u.visited = true;
|
||||
for (const child of u.children) {
|
||||
if (!child.visited) { visit(child, res); }
|
||||
}
|
||||
res.push(u);
|
||||
}
|
||||
visit(root_node, res);
|
||||
res.reverse();
|
||||
const sccs = [];
|
||||
function assign(node, cur_scc) {
|
||||
if (node.assigned) { return; }
|
||||
cur_scc.nodes.push(node);
|
||||
node.scc = cur_scc;
|
||||
node.assigned = true;
|
||||
for (const parent of node.parents) {
|
||||
assign(parent, cur_scc);
|
||||
}
|
||||
}
|
||||
for (const node of res) {
|
||||
if (node.assigned) { continue; }
|
||||
const cur_scc = {
|
||||
nodes: [],
|
||||
children: new Set(),
|
||||
parents: new Set()
|
||||
};
|
||||
assign(node, cur_scc);
|
||||
sccs.push(cur_scc);
|
||||
}
|
||||
for (const scc of sccs) {
|
||||
for (const node of scc.nodes) {
|
||||
for (const child of node.children) {
|
||||
scc.children.add(child.scc);
|
||||
}
|
||||
for (const parent of node.parents) {
|
||||
scc.parents.add(parent.scc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [root_node, terminal_node, sccs];
|
||||
}
|
||||
|
|
39
js/utils.js
|
@ -619,8 +619,15 @@ function addClasses(elem, classes) {
|
|||
*/
|
||||
async function hardReload() {
|
||||
//https://gist.github.com/rmehner/b9a41d9f659c9b1c3340
|
||||
const dbs = await window.indexedDB.databases();
|
||||
await dbs.forEach(db => { window.indexedDB.deleteDatabase(db.name) });
|
||||
try {
|
||||
const dbs = await window.indexedDB.databases();
|
||||
await dbs.forEach(db => { window.indexedDB.deleteDatabase(db.name) });
|
||||
} catch (error) {
|
||||
// Hacky patch for firefox...
|
||||
console.log(error);
|
||||
const db_names = ['item_db', 'ing_db', 'map_db', 'tome_db'];
|
||||
await db_names.forEach(db => { window.indexedDB.deleteDatabase(db) });
|
||||
}
|
||||
|
||||
location.reload(true);
|
||||
}
|
||||
|
@ -743,13 +750,34 @@ function assert_error(func_binding, msg) {
|
|||
/**
|
||||
* Deep copy object/array of basic types.
|
||||
*/
|
||||
function deepcopy(obj) {
|
||||
function deepcopy(obj, refs=undefined) {
|
||||
if (refs === undefined) {
|
||||
refs = new Map();
|
||||
}
|
||||
if (typeof(obj) !== 'object' || obj === null) { // null or value type
|
||||
return obj;
|
||||
}
|
||||
let ret = Array.isArray(obj) ? [] : {};
|
||||
for (let key in obj) {
|
||||
ret[key] = deepcopy(obj[key]);
|
||||
let val;
|
||||
try {
|
||||
val = obj[key];
|
||||
} catch (exc) {
|
||||
console.trace();
|
||||
val = undefined;
|
||||
}
|
||||
if (typeof(obj) === 'object') {
|
||||
if (refs.has(val)) {
|
||||
ret[key] = refs.get(val);
|
||||
}
|
||||
else {
|
||||
refs.set(val, val);
|
||||
ret[key] = deepcopy(val, refs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret[key] = val;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -760,10 +788,9 @@ function deepcopy(obj) {
|
|||
*/
|
||||
function gen_slider_labeled({label_name, label_classlist = [], min = 0, max = 100, step = 1, default_val = min, id = undefined, color = "#FFFFFF", classlist = []}) {
|
||||
let slider_container = document.createElement("div");
|
||||
slider_container.classList.add("row");
|
||||
|
||||
let buf_col = document.createElement("div");
|
||||
buf_col.classList.add("col", "mx-1");
|
||||
buf_col.classList.add("col");
|
||||
|
||||
let label = document.createElement("div");
|
||||
label.classList.add("col");
|
||||
|
|
BIN
media/atree/connect_0011.png
Normal file
After Width: | Height: | Size: 182 B |
BIN
media/atree/connect_0011_1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
media/atree/connect_0101.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
media/atree/connect_0101_1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
media/atree/connect_0110.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
media/atree/connect_0110_1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
media/atree/connect_0111.png
Normal file
After Width: | Height: | Size: 202 B |
BIN
media/atree/connect_0111_0011.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_0111_0101.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_0111_0110.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_0111_0111.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1001.png
Normal file
After Width: | Height: | Size: 184 B |
BIN
media/atree/connect_1001_1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
media/atree/connect_1010.png
Normal file
After Width: | Height: | Size: 177 B |
BIN
media/atree/connect_1010_1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
media/atree/connect_1011.png
Normal file
After Width: | Height: | Size: 202 B |
BIN
media/atree/connect_1011_0011.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1011_1001.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1011_1010.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1011_1011.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1100.png
Normal file
After Width: | Height: | Size: 184 B |
BIN
media/atree/connect_1100_1.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1101.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
media/atree/connect_1101_0101.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1101_1001.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1101_1100.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1101_1101.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1110.png
Normal file
After Width: | Height: | Size: 203 B |
BIN
media/atree/connect_1110_0110.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1110_1010.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1110_1100.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1110_1110.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111.png
Normal file
After Width: | Height: | Size: 221 B |
BIN
media/atree/connect_1111_0011.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_0101.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_0110.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_0111.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1001.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1010.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1011.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1100.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1101.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1110.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
media/atree/connect_1111_1111.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 962 B |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 974 B |
Before Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 666 B |
Before Width: | Height: | Size: 654 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 231 B |
BIN
media/atree/node_0_selected.png
Normal file
After Width: | Height: | Size: 266 B |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 254 B |
BIN
media/atree/node_1_selected.png
Normal file
After Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 276 B |