Merge branch 'atree' of https://github.com/hppeng-wynn/hppeng-wynn.github.io into mobile_ui_tweaks

This commit is contained in:
reschan 2022-07-19 13:26:27 +07:00
commit 0e9cb28ae1
165 changed files with 2992 additions and 2860 deletions

View file

@ -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>

File diff suppressed because one or more lines are too long

1315
builder/index_full.html Normal file

File diff suppressed because it is too large Load diff

View file

@ -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 @@
]
}
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -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>

View file

@ -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)

View file

@ -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;
}

View file

@ -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>

View file

@ -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": {

File diff suppressed because one or more lines are too long

View file

@ -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>

View file

@ -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

View file

@ -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;
};

View file

@ -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 }
}
]

File diff suppressed because one or more lines are too long

View file

@ -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"));

View file

@ -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",

View file

@ -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);

View file

@ -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) {

View file

@ -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;
}

View file

@ -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);

View file

@ -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];

View file

@ -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

View file

@ -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();
})();

View file

@ -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;

View file

@ -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);
}

View file

@ -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];
}

View file

@ -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");

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 276 B

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