Merge
1
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
||||||
*.swp
|
*.swp
|
||||||
*.bat
|
*.bat
|
||||||
|
*.json
|
||||||
sets/
|
sets/
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
|
44
atlas.html
|
@ -1,44 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html scroll-behavior="smooth">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<!-- nunito font, copying wynndata -->
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
<link rel="icon" href="favicon.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<title>ATLAS???</title>
|
|
||||||
</head>
|
|
||||||
<body class="all">
|
|
||||||
<div class="center">
|
|
||||||
<header class = "header nomarginp">
|
|
||||||
<div class = "headerleft" id = "headerleft">
|
|
||||||
</div>
|
|
||||||
<div class = "headercenter" id = "headercenter">
|
|
||||||
<div >
|
|
||||||
<p class = "itemp" id = "header">Atlas???</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "headerright" id = "headerright">
|
|
||||||
<div>
|
|
||||||
<button class = "atlas" onclick = "atlasClick()" style = "padding:0">
|
|
||||||
<img src = "/favicon.png" style = "left:0;top:0;width:48px;height:48px"/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
<div class = "center" id = "flavortext">
|
|
||||||
...
|
|
||||||
</div>
|
|
||||||
<div class = "center bodydiv" id = "bodydiv">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="/js/loadheader.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/icons.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/atlas.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Before Width: | Height: | Size: 304 B After Width: | Height: | Size: 304 B |
71
atlas/index.html
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>ATLAS???</title>
|
||||||
|
|
||||||
|
<link rel="icon" href="favicon.png">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
</head>
|
||||||
|
<body class="text-light d-flex justify-content-center" id = "body">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow col">
|
||||||
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<a href = "/customizer.html"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
|
||||||
|
<a href = "/map.html"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
|
||||||
|
<a href = "/wynnfo/index.html"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</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 py-1 vh-100">
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col-4" id = "headerleft">
|
||||||
|
</div>
|
||||||
|
<div class = "col-4 d-flex justify-content-center" id = "headercenter" style = "color:#fbcd49; font-size: 200%;">
|
||||||
|
<div>
|
||||||
|
<p class = "itemp box-title" id = "header">Atlas???</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "col-4 d-flex justify-content-end" id = "headerright">
|
||||||
|
<div>
|
||||||
|
<button class = "rounded atlas" onclick = "atlasClick()" style = "padding:0; text-align: right">
|
||||||
|
<img src = "favicon.png" class = "rounded" style = "left:0;top:0;width:48px;height:48px"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row d-flex text-center justify-content-center scaled-font" id = "flavortext">
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
<div class = "col navbar navbar-fixed-bottom vh-75 min-vh-50 text-break ml-5" id = "bodydiv" style = "min-height: 75vh; display: flex; flex-direction: column;" >
|
||||||
|
</div>
|
||||||
|
<audio id="bruh_sound_effect" src="../media/audio/bruh_sound_effect.mp3" preload="auto"></audio>
|
||||||
|
</div>
|
||||||
|
<!-- sidebar -->
|
||||||
|
|
||||||
|
<!-- main body -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<script type="text/javascript" src="../js/icons.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/atlas.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1439
builder/doc.html
Normal file
1432
builder/index.html
Normal file
3107
clean.json
229
crafter.html
|
@ -1,229 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html scroll-behavior="smooth">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<!-- nunito font, copying wynndata -->
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
<link rel="stylesheet" media="screen and (min-width: 1100px)" href="/css/crafter-wide.css"/>
|
|
||||||
<link rel="stylesheet" media="screen and (max-width: 1099px)" href="/css/crafter-narrow.css"/>
|
|
||||||
<link rel="icon" href="./media/icons/new/crafter.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<title>WynnCrafter</title>
|
|
||||||
</head>
|
|
||||||
<body class="all">
|
|
||||||
<div class="center">
|
|
||||||
<header class = "header nomarginp">
|
|
||||||
<div class = "headerleft" id = "headerleft">
|
|
||||||
</div>
|
|
||||||
<div class = "headercenter" id = "headercenter">
|
|
||||||
<div >
|
|
||||||
<p class = "itemp" id = "header">WynnCrafter</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "headerright" id = "headerright">
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
<div class = "container" display = "grid">
|
|
||||||
<div class = "center" style = "grid-column:1;grid-row:1" >
|
|
||||||
<div class = "crafter">
|
|
||||||
<table class = "center">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<label for="recipe-choice">Recipe Type:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="recipeinput" list="recipe-choices" id="recipe-choice" name="recipe-choice" placeholder="Potion"/>
|
|
||||||
<datalist id="recipe-choices">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
<td >
|
|
||||||
<label for="level-choice">Recipe Level:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="levelinput" list="level-choices" id="level-choice" name="level-choice" placeholder="103-105" />
|
|
||||||
<datalist id="level-choices">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="center">
|
|
||||||
<label id = "mat-1" for="mat-1-tier-choice">Material 1 Tier:</label>
|
|
||||||
<br/>
|
|
||||||
<button class = "button Star" id = "mat-1-1" onclick = "toggleMaterial('mat-1-1')">1</button>
|
|
||||||
<button class = "button Star" id = "mat-1-2" onclick = "toggleMaterial('mat-1-2')">2</button>
|
|
||||||
<button class = "button Star" id = "mat-1-3" onclick = "toggleMaterial('mat-1-3')">3</button>
|
|
||||||
</td>
|
|
||||||
<td class="center">
|
|
||||||
<label id = "mat-2" for="mat-2-tier-choice">Material 2 Tier:</label>
|
|
||||||
<br/>
|
|
||||||
<button class = "button Star" id = "mat-2-1" onclick = "toggleMaterial('mat-2-1')">1</button>
|
|
||||||
<button class = "button Star" id = "mat-2-2" onclick = "toggleMaterial('mat-2-2')">2</button>
|
|
||||||
<button class = "button Star" id = "mat-2-3" onclick = "toggleMaterial('mat-2-3')">3</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="center">
|
|
||||||
<label for="ing-choice-1">Ingredient 1:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="inginput" list="ing-choices-1" id="ing-choice-1" name="ing-choice-1" placeholder="No Ingredient" />
|
|
||||||
<datalist id="ing-choices-1">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
<td class="center">
|
|
||||||
<label for="ing-choice-2">Ingredient 2:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="inginput" list="ing-choices-2" id="ing-choice-2" name="ing-choice-2" placeholder="No Ingredient" />
|
|
||||||
<datalist id="ing-choices-2">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="center">
|
|
||||||
<label for="ing-choice-3">Ingredient 3:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="inginput" list="ing-choices-3" id="ing-choice-3" name="ing-choice-3" placeholder="No Ingredient" />
|
|
||||||
<datalist id="ing-choices-3">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
<td class="center">
|
|
||||||
<label for="ing-choice-4">Ingredient 4:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="inginput" list="ing-choices-4" id="ing-choice-4" name="ing-choice-4" placeholder="No Ingredient" />
|
|
||||||
<datalist id="ing-choices-4">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="center">
|
|
||||||
<label for="ing-choice-5">Ingredient 5:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="inginput" list="ing-choices-5" id="ing-choice-5" name="ing-choice-5" placeholder="No Ingredient" />
|
|
||||||
<datalist id="ing-choices-5">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
<td class="center">
|
|
||||||
<label for="ing-choice-6">Ingredient 6:</label>
|
|
||||||
<br/>
|
|
||||||
<input class="inginput" list="ing-choices-6" id="ing-choice-6" name="ing-choice-6" placeholder="No Ingredient" />
|
|
||||||
<datalist id="ing-choices-6">
|
|
||||||
</datalist>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<label for = "atkSpdChoices"> Attack Speed (weapons only):</label>
|
|
||||||
<table class = "center" id = "atkSpdChoices">
|
|
||||||
<tr>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "slow-atk-button" onclick = "toggleAtkSpd('slow-atk-button')">
|
|
||||||
Slow
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "normal-atk-button" onclick = "toggleAtkSpd('normal-atk-button')">
|
|
||||||
Normal
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "fast-atk-button" onclick = "toggleAtkSpd('fast-atk-button')">
|
|
||||||
Fast
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<table class = "center">
|
|
||||||
<tr>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "craft-button" onclick = "calculateCraft()">
|
|
||||||
Craft Item
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "reset-button" onclick = "resetFields()">
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "copy-button" onclick = "copyRecipe()">
|
|
||||||
Copy Short
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
<td class = "center">
|
|
||||||
<button class = "button" id = "share-button" onclick = "shareRecipe()">
|
|
||||||
Copy Long
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "recipe center hide-container-block" style = "display:none">
|
|
||||||
<div class = "recipe-stats">
|
|
||||||
<div class = "center" id = "recipe-stats"></div>
|
|
||||||
</div>
|
|
||||||
<div class = "craft-warnings">
|
|
||||||
<div class = "center" id = "craft-warnings"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "crafted center hide-container-block" style = "display:none">
|
|
||||||
<div class = "craft-stats">
|
|
||||||
<div class = "center" id = "craft-stats"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--This is also incredibly janky.-->
|
|
||||||
<p></p>
|
|
||||||
<p></p>
|
|
||||||
<p></p>
|
|
||||||
<div class="ingredients-container hide-container-grid" id = "ingreds" style = "display:none">
|
|
||||||
<p class="center title hide-container-block" style = "display:block">
|
|
||||||
Ingredients
|
|
||||||
</p>
|
|
||||||
<div class = "ingredients hide-container-grid" id = "ingreds">
|
|
||||||
<div class="ing-stats" id = "ing-1" style = "grid-item-1">
|
|
||||||
<div class = "center" id = "ing-1-stats"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ing-stats" id = "ing-2" style = "grid-item-2">
|
|
||||||
<div class = "center" id = "ing-2-stats"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ing-stats" id = "ing-3" style = "grid-item-3">
|
|
||||||
<div class = "center" id = "ing-3-stats"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ing-stats" id = "ing-4" style = "grid-item-4">
|
|
||||||
<div class = "center" id = "ing-4-stats"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ing-stats" id = "ing-5" style = "grid-item-5">
|
|
||||||
<div class = "center" id = "ing-5-stats"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ing-stats" id = "ing-6" style = "grid-item-6">
|
|
||||||
<div class = "center" id = "ing-6-stats"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "center">
|
|
||||||
<footer>
|
|
||||||
<div class="center" id="header2">
|
|
||||||
<p>Made by <b class = "hppeng">hppeng</b> and <b class = "ferricles">ferricles</b> with <a href = "./atlas.html" target = "_blank" class = "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 class="center" id="credits">
|
|
||||||
<a href="https://hppeng-wynn.github.io/credits.txt" class="link">Additional credits</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript" src="/js/utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/build_utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/loadheader.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/icons.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/powders.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/skillpoints.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/damage_calc.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/display_constants.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/display.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/load_ing.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/craft.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/crafter.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
313
crafter/index.html
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>WynnCrafter</title>
|
||||||
|
<link rel="icon" href="../media/icons/new/crafter.png" type="image/icon type">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
|
</head>
|
||||||
|
<body id = "body" class = "text-light d-flex justify-content-center">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href = ""><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<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 = "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 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>
|
||||||
|
</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>
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
<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-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-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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
<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>
|
||||||
|
<div id="credits">
|
||||||
|
<a href="credits.txt" class="link">Additional credits</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="../js/query.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/query_2.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/build_utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/sq2icons.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/powders.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/skillpoints.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/damage_calc.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/display_constants.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/display.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/load_ing.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/load.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/craft.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/crafter.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> -->
|
||||||
|
</body>
|
||||||
|
</html>
|
10
credits.txt
|
@ -1,14 +1,20 @@
|
||||||
|
|
||||||
Theme, formatting, and overall inspiration: Wynndata (Dukio)
|
Theme, formatting, and overall inspiration: Wynndata (Dukio)
|
||||||
- https://wynndata.tk
|
- https://wynndata.tk
|
||||||
|
|
||||||
The game, of course
|
The game, of course
|
||||||
- wynncraft.com
|
- wynncraft.com
|
||||||
|
|
||||||
Additional Contributors:
|
Additional Contributors, in no particular order:
|
||||||
- Kiocifer (Icons!)
|
- Kiocifer (Icons!)
|
||||||
|
- IncinerateMe (helping transition to 1.20.3 / CI helper)
|
||||||
|
- puppy (wynn2 ability tree help)
|
||||||
|
- SockMower (ability tree encode/decode optimization)
|
||||||
|
- ITechnically (coding emotional support / misc)
|
||||||
|
- touhoku (best IM)
|
||||||
|
- HeyZeer0 (huge help in getting our damage formulas right)
|
||||||
- Lennon (Skill point formula reversing)
|
- Lennon (Skill point formula reversing)
|
||||||
- Phanta (WynnAtlas custom expression parser / item search)
|
- Phanta (WynnAtlas custom expression parser / item search)
|
||||||
- QuantumNep (Layout code/layout ideas)
|
|
||||||
- nbcss (Crafted Item mechanics reverse engineering)
|
- nbcss (Crafted Item mechanics reverse engineering)
|
||||||
- dr_carlos (Hiding UI elements properly, fade animations, proper error handling)
|
- dr_carlos (Hiding UI elements properly, fade animations, proper error handling)
|
||||||
- Atlas Inc discord (feedback, ideas, damage calc, etc)
|
- Atlas Inc discord (feedback, ideas, damage calc, etc)
|
||||||
|
|
|
@ -102,7 +102,7 @@ main .heart {
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-width > img {
|
.full-width > img {
|
||||||
width: 100%;
|
width: 80%;
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
filter: brightness(0.7) contrast(1.2) grayscale(0.5);
|
filter: brightness(0.7) contrast(1.2) grayscale(0.5);
|
||||||
}
|
}
|
||||||
|
|
85
css/dev.css
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* General Styling - used for all elements on /dev/ */
|
||||||
|
.row > * {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
padding-left: 0 !important; /*Override grid.scss (bs) */
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
border-left: 3px solid white;
|
||||||
|
border-radius: 0.1rem;
|
||||||
|
/* padding-right: 0rem !important;
|
||||||
|
padding-left: 0rem !important; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title:hover{
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin-bottom: 1.5rem;;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
/*Needed to override <ul> style in sq2bs.css*/
|
||||||
|
list-style-type: circle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indent {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Math */
|
||||||
|
math, number {
|
||||||
|
font-family: 'CMU Serif', 'Cambria Math', 'Times New Roman', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
number {
|
||||||
|
color: rgb(17, 234, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Variable Sizing (specific to dev page) */
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.section-title > * {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) and (max-width: 1400px) {
|
||||||
|
.section-title > * {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1400px) {
|
||||||
|
.section-title > *{
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 4.5rem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,12 +6,6 @@
|
||||||
.overall-container{
|
.overall-container{
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.container{
|
|
||||||
width: 95%;
|
|
||||||
border: 3px solid #BCBCBC;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 2% 4% 4%;
|
|
||||||
}
|
|
||||||
.coord-container {
|
.coord-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
|
|
@ -15,11 +15,6 @@
|
||||||
grid-column-gap: 5px;
|
grid-column-gap: 5px;
|
||||||
grid-auto-rows: minmax(32px, auto);
|
grid-auto-rows: minmax(32px, auto);
|
||||||
}
|
}
|
||||||
.container{
|
|
||||||
width: 95%;
|
|
||||||
border: 3px solid #BCBCBC;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
.leaflet-tooltip.labelp {
|
.leaflet-tooltip.labelp {
|
||||||
font-family: 'Nunito', sans-serif;
|
font-family: 'Nunito', sans-serif;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
|
|
48
css/sidebar.css
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
:root {
|
||||||
|
--sidebar-width: 3.5vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sidebar stuff */
|
||||||
|
.sidebar {
|
||||||
|
height: 100%; /* 100% Full-height */
|
||||||
|
width: var(--sidebar-width); /* 0 width - change this with JavaScript */
|
||||||
|
position: fixed; /* Stay in place */
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
overflow-x: hidden; /* Disable horizontal scroll */
|
||||||
|
transition: 0.5s; /* 0.5 second transition effect to slide in the sidebar */
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar:hover {
|
||||||
|
width: 11vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar a {
|
||||||
|
padding: .4vw .4vw .4vw .4vw;
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar a img {
|
||||||
|
margin-right: .6vw;
|
||||||
|
width: 2.5vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar a b {
|
||||||
|
font-size: .85vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar a:hover {
|
||||||
|
background-color: hsl(0, 0%, 8%);
|
||||||
|
color: rgb(210, 210, 210);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 992px) {
|
||||||
|
:root {
|
||||||
|
--sidebar-width: 0px;
|
||||||
|
}
|
||||||
|
.sidebar {display: none;}
|
||||||
|
}
|
507
css/sq2bs.css
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
* {
|
||||||
|
font-family: 'Nunito', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#body {
|
||||||
|
background-color: #121212;
|
||||||
|
}
|
||||||
|
/* builder containers */
|
||||||
|
|
||||||
|
|
||||||
|
.e_slider, .t_slider, .w_slider, .f_slider, .a_slider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background: #AAAAAA;
|
||||||
|
border-radius: 30px;
|
||||||
|
height: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Chrome, Safari, Opera, and Edge Chromium *****/
|
||||||
|
.e_slider::-webkit-slider-runnable-track, .t_slider::-webkit-slider-runnable-track, .w_slider::-webkit-slider-runnable-track, .f_slider::-webkit-slider-runnable-track, .a_slider::-webkit-slider-runnable-track {
|
||||||
|
-webkit-appeareance: none;
|
||||||
|
background:transparent;
|
||||||
|
height: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******** Firefox **** **/
|
||||||
|
.e_slider::-moz-range-track, .t_slider::-moz-range-track, .w_slider::-moz-range-track, .f_slider::-moz-range-track, .a_slider::-moz-range-track {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border-radius: 30px;
|
||||||
|
height: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.e_slider::-webkit-slider-thumb, .t_slider::-webkit-slider-thumb, .w_slider::-webkit-slider-thumb, .f_slider::-webkit-slider-thumb, .a_slider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
height: 0.75rem;
|
||||||
|
width: 0.75rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
margin-top: -0.125rem;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: solid 2px #82CFD0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range]:focus {
|
||||||
|
outline: none; /* Removes the border. */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* equipment field specifics */
|
||||||
|
/* inputs and dropdowns */
|
||||||
|
.form-control {
|
||||||
|
transition: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
width: 95% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.search-box {
|
||||||
|
position: absolute;
|
||||||
|
padding: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.search-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.search-item:hover {
|
||||||
|
background-color: hsl(0, 0%, 11%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* boosts styles */
|
||||||
|
.button-boost:hover {
|
||||||
|
background-color: rgba(255, 255, 255, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.toggleOn {
|
||||||
|
background-color: #0a0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* floating tooltip styles */
|
||||||
|
.float-tooltip {
|
||||||
|
background-color: hsl(0, 0%, 16%);
|
||||||
|
position: absolute;
|
||||||
|
transition: .3s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generic */
|
||||||
|
|
||||||
|
input {
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.equipment-input {
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: hsl(0, 0%, 21%) !important;
|
||||||
|
border-radius: 0.375rem !important;
|
||||||
|
border-color: rgba(33, 37, 41, 1) !important;
|
||||||
|
min-height: calc(1.2 * var(--scaled-fontsize) + 2px);
|
||||||
|
padding: 0rem 0.5rem;
|
||||||
|
font-size: var(--scaled-fontsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-container {
|
||||||
|
position: fixed; /* Stay in place */
|
||||||
|
left: var(--sidebar-width);
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell-display p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell-display b {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell-expand {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--scaled-fontsize: 2.5rem;
|
||||||
|
}
|
||||||
|
.scaled-font {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
text-align: center;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-title {
|
||||||
|
text-align: center;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
font-size: 4.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skp-tooltip {
|
||||||
|
font-size: 2.1875rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spellcost-tooltip b {
|
||||||
|
font-size: 2.1875rem !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: #ff8180;
|
||||||
|
font-size: 1.875rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-item-icon {
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-item-icon img {
|
||||||
|
width: 6.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-bckgrd {
|
||||||
|
width: 10rem;
|
||||||
|
height: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-bckgrd img {
|
||||||
|
width: 6.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1200px) and (max-width: 1400px) {
|
||||||
|
:root {
|
||||||
|
--scaled-fontsize: 1rem;
|
||||||
|
}
|
||||||
|
.scaled-font {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skp-tooltip {
|
||||||
|
font-size: .625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spellcost-tooltip b {
|
||||||
|
font-size: .625rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-item-icon {
|
||||||
|
width: 3.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-item-icon img {
|
||||||
|
width: 2.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-bckgrd {
|
||||||
|
width: 4rem;
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-bckgrd img {
|
||||||
|
width: 2.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
font-size: .7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell-display b {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1400px) {
|
||||||
|
:root {
|
||||||
|
--scaled-fontsize: 1rem;
|
||||||
|
}
|
||||||
|
.scaled-font {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skp-tooltip {
|
||||||
|
font-size: .78rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spellcost-tooltip b {
|
||||||
|
font-size: .78rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-item-icon {
|
||||||
|
width: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-item-icon img {
|
||||||
|
width: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-bckgrd {
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scaled-bckgrd img {
|
||||||
|
width: 3.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
font-size: .8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell-display b {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* WynnAtlas Mini */
|
||||||
|
.search-field {
|
||||||
|
background-color: hsl(0, 0%, 14%) !important;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
border-color: hsl(0, 0%, 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fake button for build stats */
|
||||||
|
.fake-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fake-button:hover, .selected-btn{
|
||||||
|
background-color: hsl(0, 0%, 14%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* material design dark mode */
|
||||||
|
.dark-1 {
|
||||||
|
background-color: hsl(0, 0%, 5%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-2 {
|
||||||
|
background-color: hsl(0, 0%, 7%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-3 {
|
||||||
|
background-color: hsl(0, 0%, 8%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-4 {
|
||||||
|
background-color: hsl(0, 0%, 9%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-5 {
|
||||||
|
background-color: hsl(0, 0%, 11%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-6 {
|
||||||
|
background-color: hsl(0, 0%, 12%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-7 {
|
||||||
|
background-color: hsl(0, 0%, 14%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-8 {
|
||||||
|
background-color: hsl(0, 0%, 15%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-9 {
|
||||||
|
background-color: hsl(0, 0%, 16%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-10 {
|
||||||
|
background-color: hsl(0, 0%, 20%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-1u {
|
||||||
|
background-color: hsl(0, 0%, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-2u {
|
||||||
|
background-color: hsl(0, 0%, 7%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-3u {
|
||||||
|
background-color: hsl(0, 0%, 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-4u {
|
||||||
|
background-color: hsl(0, 0%, 9%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-5u {
|
||||||
|
background-color: hsl(0, 0%, 11%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-6u {
|
||||||
|
background-color: hsl(0, 0%, 12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-7u {
|
||||||
|
background-color: hsl(0, 0%, 14%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-8u {
|
||||||
|
background-color: hsl(0, 0%, 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-9u {
|
||||||
|
background-color: hsl(0, 0%, 16%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-dark-7 {
|
||||||
|
border-color:hsl(0, 0%, 14%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BOX SHADOW STYLES */
|
||||||
|
.dark-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.5rem 0.075rem black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-shadow-sm {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.atlas {
|
||||||
|
color: #fbcd49;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #88FFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #ff88ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-semi-light {
|
||||||
|
border-color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* supposed to work in firefox but doesn't */
|
||||||
|
input[type=number]::-webkit-inner-spin-button,
|
||||||
|
input[type=number]::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make links bold when hovered over */
|
||||||
|
|
||||||
|
.clickable:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
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 {
|
||||||
|
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
|
.hide-scroll::-webkit-scrollbar {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
.ferricles{
|
||||||
|
color: #5be553;
|
||||||
|
}
|
|
@ -356,43 +356,6 @@ input {
|
||||||
::placeholder{
|
::placeholder{
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
/* Tier colors tier colors */
|
|
||||||
.none {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
.lore {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
.Normal{
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.Unique{
|
|
||||||
color:#ff5;
|
|
||||||
}
|
|
||||||
.Rare{
|
|
||||||
color:#f5f;
|
|
||||||
}
|
|
||||||
.Legendary{
|
|
||||||
color:#5ff;
|
|
||||||
}
|
|
||||||
.Fabled{
|
|
||||||
color:#f55;
|
|
||||||
}
|
|
||||||
.Mythic{
|
|
||||||
color:#a0a;
|
|
||||||
}
|
|
||||||
.Crafted {
|
|
||||||
color: #0aa;
|
|
||||||
}
|
|
||||||
.Custom {
|
|
||||||
color: #0aa;
|
|
||||||
}
|
|
||||||
.Set{
|
|
||||||
color:#5f5;
|
|
||||||
}
|
|
||||||
.restrict, .warning {
|
|
||||||
color: #ff8180;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.toggleOn{
|
button.toggleOn{
|
||||||
background-color:#0a0;
|
background-color:#0a0;
|
||||||
|
@ -411,40 +374,11 @@ button.toggleOn:hover {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Star {
|
|
||||||
color:rgb(255, 198, 85);
|
|
||||||
}
|
|
||||||
.Star:after {
|
|
||||||
content: "\272B";
|
|
||||||
}
|
|
||||||
.externalBuffs {
|
.externalBuffs {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.T0 {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
.T1 {
|
|
||||||
color: #ff5;
|
|
||||||
}
|
|
||||||
.T2 {
|
|
||||||
color: #f5f;
|
|
||||||
}
|
|
||||||
.T3 {
|
|
||||||
color: #5ff;
|
|
||||||
}
|
|
||||||
.T0-bracket {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
.T1-bracket {
|
|
||||||
color: #fa0;
|
|
||||||
}
|
|
||||||
.T2-bracket {
|
|
||||||
color: #a0a;
|
|
||||||
}
|
|
||||||
.T3-bracket {
|
|
||||||
color: #0aa;
|
|
||||||
}
|
|
||||||
.hide-container-block, .hide-container-grid, .set-info-div, .fade-in {
|
.hide-container-block, .hide-container-grid, .set-info-div, .fade-in {
|
||||||
animation-duration: 0.5s;
|
animation-duration: 0.5s;
|
||||||
animation-name: fadeInFromNone;
|
animation-name: fadeInFromNone;
|
||||||
|
@ -548,7 +482,3 @@ button.toggleOn:hover {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.atlas{
|
|
||||||
height: 48px;
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
|
|
184
css/wynnstyles.css
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
Wynn-Related CSS
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Misc/Util Colors */
|
||||||
|
.positive {
|
||||||
|
color: #5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.negative {
|
||||||
|
color: #f55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Health {
|
||||||
|
color: #AA0000
|
||||||
|
}
|
||||||
|
|
||||||
|
.Health:before {
|
||||||
|
content: "\2764" ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
.Mana {
|
||||||
|
color: #5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Mana:after {
|
||||||
|
content: "\273A"
|
||||||
|
}
|
||||||
|
|
||||||
|
.lvl:before {
|
||||||
|
content: 'Lv. '
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Tier Colors */
|
||||||
|
|
||||||
|
.Normal {
|
||||||
|
color: #FFFFFF !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Unique {
|
||||||
|
color: #FFFF55 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Rare {
|
||||||
|
color: #FF55FF !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Legendary {
|
||||||
|
color: #55FFFF !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Fabled {
|
||||||
|
color: #FF5555 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Mythic {
|
||||||
|
color: #AA00AA !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Set {
|
||||||
|
color: #5f5 !important;
|
||||||
|
}
|
||||||
|
.Crafted, .Custom {
|
||||||
|
color: #0aa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tier Shadows */
|
||||||
|
.Normal-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Unique-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #ff5;
|
||||||
|
}
|
||||||
|
.Rare-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #f5f;
|
||||||
|
}
|
||||||
|
.Legendary-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #5ff;
|
||||||
|
}
|
||||||
|
.Fabled-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #f55;
|
||||||
|
}
|
||||||
|
.Mythic-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #a0a;
|
||||||
|
}
|
||||||
|
.Crafted-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #0aa;
|
||||||
|
}
|
||||||
|
.Custom-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #0aa;
|
||||||
|
}
|
||||||
|
.Set-shadow {
|
||||||
|
box-shadow: 0rem 0rem 0.25rem 0.05rem #5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CR Styles */
|
||||||
|
|
||||||
|
.Star {
|
||||||
|
color:rgb(255, 198, 85);
|
||||||
|
}
|
||||||
|
.Star:after {
|
||||||
|
content: "\272B";
|
||||||
|
}
|
||||||
|
.T0 {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.T1 {
|
||||||
|
color: #ff5;
|
||||||
|
}
|
||||||
|
.T2 {
|
||||||
|
color: #f5f;
|
||||||
|
}
|
||||||
|
.T3 {
|
||||||
|
color: #5ff;
|
||||||
|
}
|
||||||
|
.T0-bracket {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.T1-bracket {
|
||||||
|
color: #fa0;
|
||||||
|
}
|
||||||
|
.T2-bracket {
|
||||||
|
color: #a0a;
|
||||||
|
}
|
||||||
|
.T3-bracket {
|
||||||
|
color: #0aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Damages */
|
||||||
|
|
||||||
|
.base_dps:before { /* Little Dagger icon */
|
||||||
|
content: "\1F5E1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.Damage {
|
||||||
|
color: rgb(255, 198, 85)
|
||||||
|
}
|
||||||
|
|
||||||
|
.nDam, .Neutral {
|
||||||
|
color: #FFAA00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nDam:before, .Neutral:before {
|
||||||
|
content: "\2724" ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
.eDam, .Earth, .Earth_powder {
|
||||||
|
color: #00AA00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eDam:before, .Earth:before, .Earth_powder:before { content: "\2724" ' '; }
|
||||||
|
|
||||||
|
.tDam, .Thunder, .Thunder_powder {
|
||||||
|
color: #FFFF55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tDam:before, .Thunder:before, .Thunder_powder:before { content: "\2726" ' '; }
|
||||||
|
|
||||||
|
.wDam, .Water, .Water_powder {
|
||||||
|
color: #55FFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
.wDam:before, .Water:before, .Water_powder:before { content: "\2749" ' '; }
|
||||||
|
|
||||||
|
.fDam, .Fire, .Fire_powder {
|
||||||
|
color: #FF5555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fDam:before, .Fire:before, .Fire_powder:before { content: "\2739" ' '; }
|
||||||
|
|
||||||
|
.aDam, .Air, .Air_powder {
|
||||||
|
color: #FFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
.aDam:before, .Air:before, .Air_powder:before { content: "\274b" ' '; }
|
||||||
|
|
||||||
|
.restrict {
|
||||||
|
color: #ff8180;
|
||||||
|
}
|
1914
custom/index.html
Normal file
1893
customizer.html
BIN
dev/builder_colorcode.png
Executable file
After Width: | Height: | Size: 178 KiB |
12
dev/compute_graph.svg
Executable file
After Width: | Height: | Size: 63 KiB |
965
dev/index.html
Normal file
|
@ -0,0 +1,965 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html scroll-behavior="smooth">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>WynnBuilder Dev</title>
|
||||||
|
<link rel="icon" href="../media/icons/new/atlas64.png">
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
|
<link rel="stylesheet" href="../css/dev.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="body" class="all" style="overflow-y: scroll">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
|
<a href="../builder/"><img src="../media/icons/new/builder.png" alt="WynnBuilder"
|
||||||
|
title="WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href="../crafter/"><img src="../media/icons/new/crafter.png" alt="WynnCrafter"
|
||||||
|
title="WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href="../items/"><img src="../media/icons/new/searcher.png" alt="WynnAtlas"
|
||||||
|
title="WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<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="WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href="" 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 text-light px-5 scaled-font">
|
||||||
|
<div class="row justify-content-center page-title">
|
||||||
|
Wynnbuilder Developer Page
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
Welcome to the Wynnbuilder page for developers! Here we provide documentation and specifications for our
|
||||||
|
website. Read through these sections to learn more about how WynnBuilder works!
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Decoding WynnBuilder links">
|
||||||
|
<p>
|
||||||
|
This section is about the encoding schemes Wynnbuilder uses for its various saveable items (builds,
|
||||||
|
crafted items, and custom items).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We use a Base 64 (B64) encode/decode system in most shareable links. It would be quite clunky to put a
|
||||||
|
bunch of numbers (the data we save and read) into one link. To save some space, we compress the
|
||||||
|
base 10 numerical alphabet into a custom B64 alphabet.
|
||||||
|
</p>
|
||||||
|
<div class="row section" title="WB Base 64 (B64)">
|
||||||
|
<p>
|
||||||
|
The Wynnbuilder B64 character table:
|
||||||
|
</p>
|
||||||
|
<pre class="full-width">
|
||||||
|
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-
|
||||||
|
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
|
||||||
|
| | | | | | | | | | | | |
|
||||||
|
0 5 10 15 20 25 30 35 40 45 50 55 60 </pre>
|
||||||
|
<p>
|
||||||
|
The B64 encoding of a number (in the 0 to 63 range) is equal to the character at the index
|
||||||
|
within the above string.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For example, if we have a set of items with id numbers in the range [<number>0</number>,
|
||||||
|
<number>10000</number>], we need at most 3 B64 characters to encode any of these items
|
||||||
|
in the link! The item of id <number>1337</number> corresponds to the B64 hash <code>0Kv</code>:
|
||||||
|
<math>1337 = 0 * 4096 + 20 * 64 + 57</math>, <number>0</number> maps to <code>0</code>,
|
||||||
|
<number>20</number> maps to <code>K</code>, and <number>57</number> maps to <code>v</code>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Decoding is a little different. We can either interpret the B64 string as a <b>signed</b> or <b>unsigned</b> number (signed: using 2s complement binary).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Things that should be interpreted as <b>signed</b> are:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Skill Points</li>
|
||||||
|
<li>Any numerical identification value for custom items</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Things that should be interpreted as <b>unsigned</b> are:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Item ID numbers</li>
|
||||||
|
<li>Tome ID numbers</li>
|
||||||
|
<li>Build Level</li>
|
||||||
|
<li>Ingredient ID numbers</li>
|
||||||
|
<li>Recipe ID numbers</li>
|
||||||
|
<li>Powder numbers</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Now that we understand the B64 system, we can move on to the way builds, crafted items, and custom items are stored in links.
|
||||||
|
</p>
|
||||||
|
<div class="row section" title="Builds">
|
||||||
|
<p>
|
||||||
|
First, what do we need in order to encode an entire build?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Wynnbuilder mainly runs calculations for damages and defense. Therefore, we need:
|
||||||
|
</p>
|
||||||
|
<ul class="indent">
|
||||||
|
<li>The build's items (equipment, tomes)</li>
|
||||||
|
<li>The skill points distributed by the user (and user level)</li>
|
||||||
|
<li>Item powderings</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Wynnbuilder assigns each item in the Wynncraft item pool to a unique ID number.
|
||||||
|
<!-- For example, the bracelet <b class="atlas">Atlas</b>
|
||||||
|
has an id number of <number>167</number>. We can then store all of a build's item pool items in a link with the items' id numbers. A
|
||||||
|
similar idea is used for skill points and powders. However, we know how many different skills there are already (5), so we can encode
|
||||||
|
the user's assignment of skill points in 5 numbers. With powders, it's a little different. There are 31 "states" of powder: 1 for no
|
||||||
|
powder and then 5 elements with 6 tiers of powder for each element. We will know how many available powder slots we have based on our
|
||||||
|
equipment. We can then put all of these numbers in a specific order (after running B64 encoding) to get our build link. -->
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "ID number specifics">
|
||||||
|
<p>
|
||||||
|
For items, you can download the item DB here: <a href = "../clean.json" target = "_blank">clean.json</a>. Each item has an id value that can be put in a map. The NoneItem ID numbers start at 10000 in the canonical order: [helmet, chestplate, leggings, boots, ring 1, ring 2, bracelet, necklace, weapon] (No Weapon has an id of 10008).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For tomes, you can download the tome DB here: <a href = "../tome_map.json" target = "_blank">tome_map.json</a>. The NoneTome ID numbers start at 61 in the order [no weapon tome, no armor tome, no guild tome] so that we can store tome IDs in 1 B64 character.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For powders: id numbers <number>1</number> through <number>30</number> map to Earth I, Earth II, ..., Earth VI,
|
||||||
|
etc. in the order Earth, Thunder, Water, Fire, Air. 0 is the id number for no powder.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
All build links will end in "#[version number]_[build hash]".
|
||||||
|
</p>
|
||||||
|
<div class="row section" title="Version 6">
|
||||||
|
<p>
|
||||||
|
Version 6 was made to account for the desire to save tomes in a build. As of the last version of this documentation, version 6 is used for encoding whenever there are tomes in the build.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example 1: With Tomes">
|
||||||
|
<code class="full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#6_06W2SH0D40Qq2SK2SL02d0og0Qi191V-E0i2C1g0000100nZ6ZU6FCDo
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> B64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>2SH</code>,
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>5</number> skill point totals (<number>2</number> B64 characters each):
|
||||||
|
<code>19</code>,
|
||||||
|
<code>1V</code>,
|
||||||
|
<code>-E</code>,
|
||||||
|
<code>0i</code>,
|
||||||
|
<code>2C</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>1</number> player level (<number>2</number> B64 characters):
|
||||||
|
<code>1g</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>7</number> tomes (<number>1</number> character each):
|
||||||
|
<code>ZU6FCDo</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The order of tomes listed is [2x weapon tome, 4x armor tome, 1x guild tome].</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Version 5">
|
||||||
|
<p>
|
||||||
|
Version 5 was made to allow for the ability to save custom items. To learn the specifics about custom item encoding, refer to the Custom Items section.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
As of the last version of this documentation, version 5 is only used for encoding when there are custom items (and no tomes) in the build.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example 1: With Custom Item">
|
||||||
|
<code class = "full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#5_06W00mCI-10000JCustom%20Chestplate0220510G020Fe0M0201a0D40Qq2SK2SL02d0og0Qi191V-E0i2C1g0000100nZ6zz++++-
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build Hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> B64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>00m</code> (with the custom item <code>CI-10000JCustom%20Chestplate0220510G020Fe0M0201a</code>),
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Starting in this version, to encode a custom item we substitute in the length of the full hash of a custom item ("CI-[gibberish]") in <number>3</number> B64 characters for the item ID, followed by the full hash.</li>
|
||||||
|
<li>When decoding build links of this version or higher, you must check whether or not the 3 characters after the current item are "CI-". If they are, the current 3 characters are the B64 representation of the unsigned length of the custom item hash (<math>n</math>), in characters. Then the next <math>n</math> characters make up the full custom item hash.</li>
|
||||||
|
<li>No existing item has an item ID of "CI-" in B64, so we can define a special case check for this "id number".</li>
|
||||||
|
<li>Further details on parsing and loading this custom item are in the Custom Item section.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>5</number> skill point totals (<number>2</number> B64 characters each):
|
||||||
|
<code>19</code>,
|
||||||
|
<code>1V</code>,
|
||||||
|
<code>-E</code>,
|
||||||
|
<code>0i</code>,
|
||||||
|
<code>2C</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>1</number> player level (<number>2</number> B64 characters):
|
||||||
|
<code>1g</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
It is possible that version 5 links will have an extra tome section at the end like above (see: Version 6 section). We ignore this in decoding.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Version 4">
|
||||||
|
<p>
|
||||||
|
Version 4 was made to allow for the ability to save crafted items. To learn the specifics about crafted item encoding, refer to the Crafted Items section.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
As of the last version of this documentation, version 4 is the default version and is used when there are no custom items or tomes in the build.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example 1: No Crafted Items">
|
||||||
|
<code class = "full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#4_06W2SH0D40Qq2SK2SL02d0og0Qi191V-E0i2C1g0000100nZ6zz++++-
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build Hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> N64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>2SH</code>,
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>5</number> skill point totals (<number>2</number> B64 characters each):
|
||||||
|
<code>19</code>,
|
||||||
|
<code>1V</code>,
|
||||||
|
<code>-E</code>,
|
||||||
|
<code>0i</code>,
|
||||||
|
<code>2C</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>1</number> player level (<number>2</number> B64 characters):
|
||||||
|
<code>1g</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class = "row section" title = "Example 2: With Crafted Items">
|
||||||
|
<code class = "full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#4_06WCR-1628i8v8v94948f210D40Qq2SK2SL02d0og0Qi1Q1V-E0l2C1g0000100nZ6zz++++-
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build Hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> B64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>CR-1628i8v8v94948f21</code>,
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Starting in this version, you can substitute in the full hash of a crafted item ("CR-[gibberish]") for the 3-character hash of an item pool item.</li>
|
||||||
|
<li>The way we can tell that an item is a crafted item is when the 3-character hash of the 'item' is "CR-". No existing item has an item ID of "CR-" in B64, so we can define a special case check for this "id number".</li>
|
||||||
|
<li>Further details on parsing and loading this custom item are in the Crafted Item section.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>5</number> skill point totals (<number>2</number> B64 characters each):
|
||||||
|
<code>19</code>,
|
||||||
|
<code>1V</code>,
|
||||||
|
<code>-E</code>,
|
||||||
|
<code>0i</code>,
|
||||||
|
<code>2C</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>1</number> player level (<number>2</number> B64 characters):
|
||||||
|
<code>1g</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
It is possible that version 4 links will have an extra tome string like above (see: Version 6 section) after the powders. You can ignore this in decoding.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Version 3">
|
||||||
|
<p>
|
||||||
|
Version 3 encoding added the ability to save build level.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example">
|
||||||
|
<code class="full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#3_06W2SH0D40Qq2SK2SL02d0og0Qi191V-E0i2C1g0000100nZ6
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> B64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>2SH</code>,
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>5</number> skill point totals (<number>2</number> B64 characters each):
|
||||||
|
<code>19</code>,
|
||||||
|
<code>1V</code>,
|
||||||
|
<code>-E</code>,
|
||||||
|
<code>0i</code>,
|
||||||
|
<code>2C</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>1</number> player level (<number>2</number> B64 characters):
|
||||||
|
<code>1g</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Version 2">
|
||||||
|
<p>
|
||||||
|
Version 2 encoding added the ability to save skill point info.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example">
|
||||||
|
<code class="full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#2_06W2SH0D40Qq2SK2SL02d0og0Qi191V-E0i2C0000100nZ6
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> B64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>2SH</code>,
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<number>5</number> skill point totals (<number>2</number> B64 characters each):
|
||||||
|
<code>19</code>,
|
||||||
|
<code>1V</code>,
|
||||||
|
<code>-E</code>,
|
||||||
|
<code>0i</code>,
|
||||||
|
<code>2C</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Version 1">
|
||||||
|
<p>
|
||||||
|
Version 1 is the very first encoding version by Wynnbuilder. It allows for saving all equipment (armors, accessories, weapon) and powders put on that equipment.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example">
|
||||||
|
<code class="full-width">
|
||||||
|
https://hppeng-wynn.github.io/builder/#1_06W2SH0D40Qq2SK2SL02d0og0Qi0000100nZ6
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Build hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>
|
||||||
|
<number>9</number> items from <code>idMap</code> (<number>3</number> B64 characters each):
|
||||||
|
<code>06W</code>,
|
||||||
|
<code>2SH</code>,
|
||||||
|
<code>0D4</code>,
|
||||||
|
<code>0Qq</code>,
|
||||||
|
<code>2SK</code>,
|
||||||
|
<code>2SL</code>,
|
||||||
|
<code>02d</code>,
|
||||||
|
<code>0og</code>,
|
||||||
|
<code>0Qi</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A <b>variable</b> number of powder "blocks" (<number>5</number> B64 characters which give us <number>6</number> powders per block).
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>For each of the 5 powderable equipment fields [helmet, chestplate, leggings, boots, weapon], we will have the following:</li>
|
||||||
|
<li><number>1</number> B64 character that says that we need <number>n</number> blocks for this item.</li>
|
||||||
|
<li><number>n</number> blocks of <number>5</number> B64 characters.</li>
|
||||||
|
<li>
|
||||||
|
Since there are 4 <code>0</code>s (B64 0 = 0 unsigned) in this example, we have no powders on any of the armor piece (no blocks).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Then, we have <code>1</code> (B64 1 = 1 unsigned). There is 1 block of powders to decode for the weapon item. That is <code>00nZ6</code>.
|
||||||
|
</li>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The unsigned equivalent of <code>00nZ6</code> in binary is 30 <b>binary</b> bits long (omitted). Each section of 5 bits directly corresponds to an powder ID.</li>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Crafted Items">
|
||||||
|
<p>
|
||||||
|
This section is about how to decode crafted items. To view an example of a crafted item in a build, check out <b>Builds > Version 4</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Crafted items always start with "CR-" so that they are, as an entire category, distinguishable from item pool items. The ingredients and materials that make up the crafted item are stored in the rest of the "hash".
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To encode all the info about a crafted item, we need:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Ingredient Data</li>
|
||||||
|
<li>Recipe Data</li>
|
||||||
|
<li>Crafting Material Tiers</li>
|
||||||
|
<li>Attack Speed (for weapons)</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Wynnbuilder assigns each ingredient and recipe to a unique ID number.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "ID number specifics">
|
||||||
|
<p>
|
||||||
|
For ingredients, you can download the ingredient id map here: <a href = "../ing_map.json" target = "_blank">ing_map.json</a>. The ID number for No Ingredient is 4000.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For recipes, you can download the recipe id map here: <a href = "../recipe_map.json" target = "_blank">recipe_map.json</a> or the recipe DB here: <a href = "recipes_clean.json" target = "_blank">recipes_clean.json</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class = "row section" title = "Version 1">
|
||||||
|
<p>
|
||||||
|
This is the first version of crafted item encoding. Crafted Items are always stored in a constant number of B64 characters.
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Example - Crafted Item">
|
||||||
|
<p>
|
||||||
|
This example shows how to parse a crafted item hash.
|
||||||
|
</p>
|
||||||
|
<code class = "full-width">
|
||||||
|
CR-1628i8v8v94948f21
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Crafted item hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li><number>3</number> characters to denote item type as crafted: <code>CR-</code> (always)</li>
|
||||||
|
<li><number>1</number> character for encoding version: <code>1</code> </li>
|
||||||
|
<li><number>6</number> ingredient IDs (<number>2</number> B64 characters each):
|
||||||
|
<code>62</code>,
|
||||||
|
<code>8i</code>,
|
||||||
|
<code>8v</code>,
|
||||||
|
<code>8v</code>,
|
||||||
|
<code>94</code>,
|
||||||
|
<code>94</code>
|
||||||
|
</li>
|
||||||
|
<li><number>2</number> B64 characters for recipe ID: <code>8f</code></li>
|
||||||
|
<li><number>1</number> character to encode material tiers: <code>2</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>There are 2 material tiers to decode. The ordering of materials is determined by their order within the corresponding recipe object held in the db.</li>
|
||||||
|
<li>The material tier character (from here on <math>t</math>) is in the range [<number>1</number>, <number>9</number>]. </li>
|
||||||
|
<li>Mat 1's tier is equal to <math>t % 3</math> except when this yields 0, in which case it becomes 3.</li>
|
||||||
|
<li>Mat 2's tier is equal to ceil(<math> (t - 0.5) / 3</math>).</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><number>1</number> character to encode attack speed: <code>1</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>The integer after doing unsigned decoding from the B64 character denotes the index within the following array: [SLOW, NORMAL, FAST]. B64 <code>1</code> maps to the unsigned integer <number>1</number>, meaning that the attack speed of this crafted item would be NORMAL if it were a weapon.</li>
|
||||||
|
<li>Note: although only weapons will have attack speed, we decided to include the character in all crafted item hashes to keep a constant hash length.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You may need to parse a crafted item from a wynnbuilder crafter link.
|
||||||
|
</p>
|
||||||
|
<code class = "full-width">
|
||||||
|
https://hppeng-wynn.github.io/crafter/#1628i8v8v94948f21
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
We can simply take the string after the octothorpe/hash tag (#), tack on "CR-" in front of this string, and arrive at the full hash for the crafted item in question. Decode using the same logic as the previous example.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Custom Items">
|
||||||
|
<p>
|
||||||
|
This section is about how to decode custom items. To view an example of a custom item in a build, check out <b>Builds > Version 5</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Custom items always start with "CI-" so that they are, as an entire category, distinguishable from item pool items. The stats and values that make up the custom item are stored in the rest of the "hash".
|
||||||
|
</p>
|
||||||
|
<div class = "row section" title = "Version 1">
|
||||||
|
<p>This is the first version of custom item encoding and decoding.</p>
|
||||||
|
<p>You will need the full array of item identification saving order and all non-rolled identifications (ex: name). View them below.</p>
|
||||||
|
<div class = "row section" title = "Important Arrays">
|
||||||
|
<p> ID saving order: <code>ci_save_order = ["name", "lore", "tier", "set", "slots", "type", "material", "drop", "quest", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq","str", "dex", "int", "agi", "def", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "majorIds", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd","durability","duration","charges"];</code> </p>
|
||||||
|
<p> Non-rolled string IDs: <code>nonRolled_strings = ["name", "lore", "tier", "set", "type", "material", "drop", "quest", "majorIds", "classReq", "atkSpd", "displayName", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "durability", "duration"];</code></p>
|
||||||
|
<p> Rolled IDs: <code>rolledIDs = ["hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd"];</code></p>
|
||||||
|
<p> Non-rolled IDs: <code>nonRolledIDs = ["name", "lore", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "str", "dex", "int", "agi", "def", "fixID", "category", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "majorIds"];</code></p>
|
||||||
|
<p> Tiers: <code>tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"]</code></p>
|
||||||
|
<p> Types: <code>types = [ "helmet", "chestplate", "leggings", "boots", "ring", "bracelet", "necklace", "wand", "spear", "bow", "dagger", "relik", "potion", "scroll", "food"];</code></p>
|
||||||
|
<p> Attack Speeds: <code>attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];</code></p>
|
||||||
|
<p> Class Requirements: <code>classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"]</code> </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class = "row section" title = "Example">
|
||||||
|
<p>
|
||||||
|
Here's an example of a custom item hash.
|
||||||
|
</p>
|
||||||
|
<code class = "full-width">
|
||||||
|
CI-10000HMeta%20Chestplate010Gbest%20in%20slot0240401030510G0302SG0H020Fe0I020Fe0J020Fe0K020Fe0L020Fe0M0201Y0i0200U220z0204iKK150200U22160200U22170200U22180200U22190200U22
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Given a custom item hash, we will in general continue to parse through many identifications and their values until we reach the end of the custom item hash.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Custom item hash format:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li><number>1</number> B64 character denoting encoding/decoding version number: <code>1</code></li>
|
||||||
|
<li><number>1</number> character denoting whether or not this item has fixed IDs: <code>0</code>. (0 for no fixed IDs, 1 for fixed IDs)</li>
|
||||||
|
<li>A series of encoded identifications, each taking a variable number of characters. For every ID, we have to save:
|
||||||
|
<ul class = "indent">
|
||||||
|
<li><number>2</number> B64 characters that represent the identification ID (its index in the CI save order array). </li>
|
||||||
|
<li><number>2</number> B64 characters that represent the length <math>len</math> of the value of the identification.</li>
|
||||||
|
<li>A variable number characters to encode the value of the identification.
|
||||||
|
<li>For string-valued identifications (in the non-rolled strings array), we do not use any encoding for the value. The next <number>len</number> characters of the custom item hash is the raw value of the identification (before substituting space for "%20").
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Exception: for the identifications <code>tier</code>, <code>type</code>, <code>atkSpd</code>, and <code>classReq</code>, there is no string used. They are encoded as a numerical value representing an index in a pre-defined array (check the Important Arrays section above). They also do not use the earlier-specified 2 characters to store length; instead, they each use only 1 B64 character to store their index in their corresponding arrays.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>For numerical-valued identifications, encoding depends on the fixed ID value from before.
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Rolled IDs (with non-fixed IDs):
|
||||||
|
<ul class = "indent">
|
||||||
|
<li><number>1</number> character to denote the sign of the min and max values: 0 for both positive, 1 for negative min and positive max, 2 for positive min and negative max, and 3 for both negative.</li>
|
||||||
|
<li><number>len</number> B64 characters that represent the unsigned <b>minimum</b> value of the identification.</li>
|
||||||
|
<li><number>len</number> B64 characters that represent the unsigned <b>maximum</b> value of the identification.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Rolled IDs (with fixed IDs) and Non-Rolled IDs:
|
||||||
|
<ul class = "indent">
|
||||||
|
<li><number>1</number> character (binary bit) to denote the sign of the value (0 positive, 1 negative).</li>
|
||||||
|
<li><number>len</number> B64 characters that represent the unsigned value of the identification.</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>To finish the example, we'll go through all the actual identifications of the provided custom item.
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>"CI-" constant portion</li>
|
||||||
|
<li>Encoding version number: <code>1</code></li>
|
||||||
|
<li>Fixed IDs: <code>0</code> (non-fixed IDs)</li>
|
||||||
|
<li><code>000HMeta%20Chestplate</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>00</code> ("name")</li>
|
||||||
|
<li>ID value length: <code>0H</code> (<number>17</number>)</li>
|
||||||
|
<li>ID value: <code>Meta%20Chestplate</code> ("Meta Chestplate")</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>010Gbest%20in%20slot</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>01</code> ("lore")</li>
|
||||||
|
<li>ID value length: <code>0G</code> (<number>16</number>)</li>
|
||||||
|
<li>ID value: <code>best%20in%20slot</code> ("best in slot")</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>024</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>02</code> ("tier")</li>
|
||||||
|
<li>ID value length: None (exception) </li>
|
||||||
|
<li>ID value: <code>4</code> (tiers[4] = "Fabled")</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>040103</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>04</code> ("slots")</li>
|
||||||
|
<li>ID value length: <code>01</code> (<number>1</number>)</li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>3</code> (<number>3</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>051</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>05</code> ("type")</li>
|
||||||
|
<li>ID value length: None (exception)</li>
|
||||||
|
<li>ID value: <code>1</code> (types[1] = "chestplate")</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0G0302SG</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0G</code> ("hp")</li>
|
||||||
|
<li>ID value length: <code>03</code> (<number>3</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>2SG</code> (<number>10000</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0H020Fe</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0H</code> ("fDef")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>Fe</code> (<number>1000</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0I020Fe</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0I</code> ("wDef")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>Fe</code> (<number>1000</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0J020Fe</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0J</code> ("aDef")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>Fe</code> (<number>1000</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0K020Fe</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0K</code> ("tDef")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>Fe</code> (<number>1000</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0L020Fe</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0L</code> ("eDef")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>Fe</code> (<number>1000</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0M0201Y</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0M</code> ("lvl")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (positive)</li>
|
||||||
|
<li>ID value: <code>1Y</code> (<number>98</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0i0200U22</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0i</code> ("hprPct")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>0U</code> (<number>30</number>)</li>
|
||||||
|
<li>ID value maximum: <code>22</code> (<number>130</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0z0204iKK</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0z</code> ("hprRaw")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>4i</code> (<number>300</number>)</li>
|
||||||
|
<li>ID value maximum: <code>KK</code> (<number>1300</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>0z0204iKK</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>0z</code> ("hprRaw")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>4i</code> (<number>300</number>)</li>
|
||||||
|
<li>ID value maximum: <code>KK</code> (<number>1300</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>150200U22</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>15</code> ("fDefPct")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>0U</code> (<number>30</number>)</li>
|
||||||
|
<li>ID value maximum: <code>22</code> (<number>130</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>160200U22</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>16</code> ("wDefPct")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>0U</code> (<number>30</number>)</li>
|
||||||
|
<li>ID value maximum: <code>22</code> (<number>130</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>170200U22</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>17</code> ("aDefPct")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>0U</code> (<number>30</number>)</li>
|
||||||
|
<li>ID value maximum: <code>22</code> (<number>130</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>180200U22</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>18</code> ("tDefPct")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>0U</code> (<number>30</number>)</li>
|
||||||
|
<li>ID value maximum: <code>22</code> (<number>130</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><code>190200U22</code>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>ID name: <code>19</code> ("eDefPct")</li>
|
||||||
|
<li>ID value length: <code>02</code> (<number>2</number>) </li>
|
||||||
|
<li>ID sign: <code>0</code> (both positive)</li>
|
||||||
|
<li>ID value minimum: <code>0U</code> (<number>30</number>)</li>
|
||||||
|
<li>ID value maximum: <code>22</code> (<number>130</number>)</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<!-- TODO -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You may need to parse a custom item from a Wynnbuilder customizer link.
|
||||||
|
</p>
|
||||||
|
<code class = "full-width">
|
||||||
|
hppeng-wynn.github.io/custom/#10000HMeta%20Chestplate010Gbest%20in%20slot0240401030510G0302SG0H020Fe0I020Fe0J020Fe0K020Fe0L020Fe0M0201Y0i0200U220z0204iKK150200U22160200U22170200U22180200U22190200U22
|
||||||
|
</code>
|
||||||
|
<p>
|
||||||
|
Similar to crafted items, the part of the link after the "#" is the rest of the custom item after the "CI-" constant portion. You may need to convert all "%20" to spaces manually.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Details on reading custom items in build links are provided in the Decoding WB links > Builds section.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Last updated: 30 May 2022
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="Wynnbuilder Internals (compute graph)">
|
||||||
|
<p>
|
||||||
|
This section is about how Wynnbuilder's main builder page processes user input and calculates results.
|
||||||
|
Might be useful if you want to script wynnbuilder or extend it! Or for wynnbuilder developers (internal docs).
|
||||||
|
</p>
|
||||||
|
<div class="row section" title="Why?">
|
||||||
|
<p>
|
||||||
|
Modeling wynnbuilder's internal computations as a directed graph has a few advantages:
|
||||||
|
</p>
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Each compute "node" is small(er); easier to debug.</li>
|
||||||
|
<li>Information flow is specified explicitly (easier to debug).</li>
|
||||||
|
<li>Easy to build caching for arbitrary computations (only calculate what u need)</li>
|
||||||
|
<li>Stateless builder! Abstract the entire builder as a chain of function calls</li>
|
||||||
|
<li>Makes for pretty pictures</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="row section" title="TODO ComputeNode details">
|
||||||
|
TODO
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
An overview of wynnbuilder's internal structure can be seen <a href = "./compute_graph.svg" target = "_blank">here</a>. Arrows indicate flow of information.
|
||||||
|
Colors correspond roughly as follows:
|
||||||
|
</p>
|
||||||
|
<img src="./builder_colorcode.png"/>
|
||||||
|
<p>
|
||||||
|
The overall logic flow is as follows:
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Item and Powder inputs are parsed. Powders are applied to items.</li>
|
||||||
|
<li>Items and level information are combined to make a build.</li>
|
||||||
|
<li>Information from input fields for skill points and edit IDs is collected into an ID bonuses table.</li>
|
||||||
|
<li>Information about active powder specials, strength boosts, etc. are collected into their own ID tables.</li>
|
||||||
|
<li>All of the above tables are merged with the build's stats table to produce the "Final" ID bonus table.</li>
|
||||||
|
<li>Which spell variant (think: major id) to use for each of the 4 spells is computed based on the build.</li>
|
||||||
|
<li>Spell damage is calculated, using the merged stat table, spell info, and weapon info.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Outputs are computed as follows:
|
||||||
|
<ul class = "indent">
|
||||||
|
<li>Input box highlights are computed from the items produced by item input box nodes.</li>
|
||||||
|
<li>Item display is computed from item input boxes.</li>
|
||||||
|
<li>Build hash/URL is computed from the build, and skillpoint assignment.</li>
|
||||||
|
<li>Spell damage is displayed based on calculated spell damage results.</li>
|
||||||
|
<li>Build stats are displayed by builder-stats-display (this same node also displays a bunch of stuff at the bottom of the screen...)</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<div class="row section" title="Gotchas">
|
||||||
|
<p>
|
||||||
|
The build sets default skillpoints and edited IDs automatically, whenever a build item/level is updated.
|
||||||
|
This is done using "soft links" by two nodes shown in red (builder-skillpoint-setter and builder-id-setter).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A soft link is where something goes and manually marks nodes dirty and calls their update methods.
|
||||||
|
This is useful for these cases because the skillpoints and editable ID fields usually take their value from
|
||||||
|
user input, but in some cases we want to programatically set them.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For example another soft link (not shown) is used to implement the reset button.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="row section" title="Test Section">
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="../js/dev.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/sq2icons.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
1044
index.html
71
item.html
|
@ -1,71 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html scroll-behavior="smooth">
|
|
||||||
<head>
|
|
||||||
<meta name="HandheldFriendly" content="true" />
|
|
||||||
<meta name="MobileOptimized" content="320" />
|
|
||||||
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
|
|
||||||
<!-- nunito font, copying wynndata -->
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
<link rel="stylesheet" href="/css/items.css">
|
|
||||||
<link rel="stylesheet" media="screen and (min-width: 800px)" href="/css/item-wide.css"/>
|
|
||||||
<link rel="stylesheet" media="screen and (max-width: 799px)" href="/css/item-narrow.css"/>
|
|
||||||
<link rel="icon" href="./favicon.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<title>Wynn Clientside</title>
|
|
||||||
</head>
|
|
||||||
<body class="all">
|
|
||||||
<div class="center">
|
|
||||||
<header class = "header nomarginp">
|
|
||||||
<div class = "headerleft" id = "headerleft">
|
|
||||||
</div>
|
|
||||||
<div class = "headercenter" id = "headercenter">
|
|
||||||
<div >
|
|
||||||
<p class = "itemp" id = "header">Item Info</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "headerright" id = "headerright">
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class = "overall center">
|
|
||||||
<div class = "overall-container" style = "display:grid">
|
|
||||||
<div class = "item-view-container hide-container-grid" display = "grid-item-1">
|
|
||||||
<p class = "title"></p>
|
|
||||||
<div class = "item-view container" id = "item-view">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "info" display = "grid-item-2">
|
|
||||||
<p class = "title" >Additional Info</p>
|
|
||||||
<div class = "additional-info" id = "additional-info"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "container set-bonus-info bigcontainer" id = "set-bonus-info" style = "display: none">
|
|
||||||
</div>
|
|
||||||
<div class = "container identification-costs bigcontainer" id = "identification-costs" style = "display: none">
|
|
||||||
</div>
|
|
||||||
<div class = "container identification-probabilities bigcontainer" id = "identification-probabilities" style = "display: none">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript" src="/js/utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/build_utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/loadheader.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/load.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/load_ing.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/crafter.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/craft.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/custom.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/customizer.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/item.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
68
item/index.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html scroll-behavior="smooth">
|
||||||
|
<head>
|
||||||
|
<title>WB Item Viewer</title>
|
||||||
|
<link rel="icon" href="../media/icons/new/searcher.png" type="image/icon type">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
|
</head>
|
||||||
|
<body class = "text-light d-flex justify-content-center" id = "body">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<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 = "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 py-5 vh-100 mx-0 mx-lg-auto scaled-font">
|
||||||
|
<div class="col">
|
||||||
|
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-0 mb-3">
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "rounded col g-0 scaled-font border border-3 border-dark dark-shadow p-3 dark-7" id = "item-view">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-6 col-sm-12">
|
||||||
|
<div class = "rounded col scaled-font border border-3 border-dark dark-shadow p-3" id = "additional-info"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row mb-3 rounded border border-semi-dark border-3 p-3" id = "set-bonus-info" style = "display: none">
|
||||||
|
</div>
|
||||||
|
<div class = "row mb-3 rounded border border-semi-dark border-3 p-3" id = "identification-costs" style = "display: none">
|
||||||
|
</div>
|
||||||
|
<div class = "row mb-3 rounded border border-semi-dark border-3 p-3" id = "identification-probabilities" style = "display: none">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="/js/utils.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/build_utils.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/icons.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/damage_calc.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>
|
||||||
|
<script type="text/javascript" src="/js/display_constants.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/display.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/item.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
137
items.html
|
@ -1,137 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html scroll-behavior="smooth">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="HandheldFriendly" content="true" />
|
|
||||||
<meta name="MobileOptimized" content="320" />
|
|
||||||
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
|
|
||||||
<!-- nunito font, copying wynndata -->
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
<link rel="stylesheet" href="/css/items.css">
|
|
||||||
<link rel="stylesheet" media="screen and (min-width: 1100px)" href="/css/items-wide.css"/>
|
|
||||||
<link rel="stylesheet" media="screen and (max-width: 1099px)" href="/css/items-narrow.css"/>
|
|
||||||
<link rel="icon" href="./media/icons/new/searcher.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<title>Wynn Clientside</title>
|
|
||||||
</head>
|
|
||||||
<body class="all">
|
|
||||||
<div class="center">
|
|
||||||
<header class = "header nomarginp">
|
|
||||||
<div class = "headerleft" id = "headerleft">
|
|
||||||
</div>
|
|
||||||
<div class = "headercenter" id = "headercenter">
|
|
||||||
<div >
|
|
||||||
<p class = "itemp" id = "header">WynnAtlas</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "headerright" id = "headerright">
|
|
||||||
<div class="center" id="advanced">
|
|
||||||
<a href="./items_2.html" class = "link">Advanced Search</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<div class="itemsearch">
|
|
||||||
<div class="searchbox">
|
|
||||||
<div class="left">
|
|
||||||
<label for="name-choice">Name:</label><br>
|
|
||||||
<input class="searchinput" type="text" id="name-choice" name="name-choice" placeholder="Item name (case insensitive)" tabindex="1" autofocus/>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
<label for="category-choice">Category:</label><br>
|
|
||||||
<input class="searchinput" list="category-items" id="category-choice" name="category-choice" placeholder="ALL" tabindex="2"/>
|
|
||||||
<datalist id="category-items">
|
|
||||||
<option value="ALL">
|
|
||||||
<option value="armor">
|
|
||||||
<option value="helmet">
|
|
||||||
<option value="chestplate">
|
|
||||||
<option value="leggings">
|
|
||||||
<option value="boots">
|
|
||||||
<option value="accessory">
|
|
||||||
<option value="ring">
|
|
||||||
<option value="bracelet">
|
|
||||||
<option value="necklace">
|
|
||||||
<option value="weapon">
|
|
||||||
<option value="wand">
|
|
||||||
<option value="spear">
|
|
||||||
<option value="bow">
|
|
||||||
<option value="dagger">
|
|
||||||
<option value="relik">
|
|
||||||
</datalist>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
<label for="rarity-choice">Rarity:</label><br>
|
|
||||||
<input class="searchinput" list="rarity-items" id="rarity-choice" name="rarity-choice" placeholder="ANY" tabindex="3"/>
|
|
||||||
<datalist id="rarity-items">
|
|
||||||
<option value="ANY">
|
|
||||||
<option value="Normal">
|
|
||||||
<option value="Unique">
|
|
||||||
<option value="Set">
|
|
||||||
<option value="Rare">
|
|
||||||
<option value="Legendary">
|
|
||||||
<option value="Fabled">
|
|
||||||
<option value="Mythic">
|
|
||||||
<option value="Sane">
|
|
||||||
</datalist>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
<label for="level-choice">Level:</label><br>
|
|
||||||
<input class="searchinput" type="text" id="level-choice" name="level-choice" value="1-106" tabindex="4"/>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<datalist id="filter-items">
|
|
||||||
</datalist>
|
|
||||||
<div class="left">
|
|
||||||
<label for="filter1-choice">Filter 1:</label><br>
|
|
||||||
<input class="searchinput" list="filter-items" id="filter1-choice" name="filter1-choice" placeholder="ANY" tabindex="5"/>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
<label for="filter2-choice">Filter 2:</label><br>
|
|
||||||
<input class="searchinput" list="filter-items" id="filter2-choice" name="filter2-choice" placeholder="ANY" tabindex="6"/>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
<label for="filter3-choice">Filter 3:</label><br>
|
|
||||||
<input class="searchinput" list="filter-items" id="filter3-choice" name="filter3-choice" placeholder="ANY" tabindex="7"/>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="left">
|
|
||||||
<label for="filter4-choice">Filter 4:</label><br>
|
|
||||||
<input class="searchinput" list="filter-items" id="filter4-choice" name="filter4-choice" placeholder="ANY" tabindex="8"/>
|
|
||||||
<p class="error"></p>
|
|
||||||
</div>
|
|
||||||
<div class="right" style="grid-column:1/span 2">
|
|
||||||
<button class = "button" id = "search-button" onclick = "doItemSearch()" tabindex="9">
|
|
||||||
Search!
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="summary" class="left" style="grid-column:3/span 2">
|
|
||||||
Hello!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="center items" id="main">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript" src="/js/utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/build_utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/loadheader.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/display_constants.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/display.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/query_2.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/expr_parser.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/load.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/items.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
152
items/index.html
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html scroll-behavior="smooth">
|
||||||
|
<head>
|
||||||
|
<title>WynnAtlas</title>
|
||||||
|
<link rel="icon" href="../media/icons/new/searcher.png" type="image/icon type">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
|
</head>
|
||||||
|
<body class = "text-light d-flex justify-content-center" id = "body">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href = ""><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<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 = "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 py-5 vh-100 mx-0 mx-lg-auto scaled-font">
|
||||||
|
<div class = "col">
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col text-end">
|
||||||
|
<a href = "../items_adv/">Advanced Item Search</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Name:</div>
|
||||||
|
<input class = "col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" type="text" id="item-name-choice" name="item-name-choice" placeholder="Item name (case insensitive)"/>
|
||||||
|
<p class="error col"></p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Category:</div>
|
||||||
|
<input class="col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="item-category-choice" name="item-category-choice" placeholder="ALL"/>
|
||||||
|
<datalist id="category-items">
|
||||||
|
<option value="ALL">
|
||||||
|
<option value="armor">
|
||||||
|
<option value="helmet">
|
||||||
|
<option value="chestplate">
|
||||||
|
<option value="leggings">
|
||||||
|
<option value="boots">
|
||||||
|
<option value="accessory">
|
||||||
|
<option value="ring">
|
||||||
|
<option value="bracelet">
|
||||||
|
<option value="necklace">
|
||||||
|
<option value="weapon">
|
||||||
|
<option value="wand">
|
||||||
|
<option value="spear">
|
||||||
|
<option value="bow">
|
||||||
|
<option value="dagger">
|
||||||
|
<option value="relik">
|
||||||
|
</datalist>
|
||||||
|
<p class="error"></p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Rarity:</div>
|
||||||
|
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="item-rarity-choice" name="item-rarity-choice" placeholder="ANY"/>
|
||||||
|
<datalist id = "rarity-items">
|
||||||
|
<option value="ANY">
|
||||||
|
<option value="Normal">
|
||||||
|
<option value="Unique">
|
||||||
|
<option value="Set">
|
||||||
|
<option value="Rare">
|
||||||
|
<option value="Legendary">
|
||||||
|
<option value="Fabled">
|
||||||
|
<option value="Mythic">
|
||||||
|
<option value="Sane">
|
||||||
|
</datalist>
|
||||||
|
<p class="error col-auto"></p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Level Range:</div>
|
||||||
|
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" type="text" id="item-level-choice" name="item-level-choice" placeholder = "1-106"/>
|
||||||
|
<p class="error col-auto"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Filter 1:</div>
|
||||||
|
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter1-choice" name="filter1-choice" placeholder="ANY"/>
|
||||||
|
<p class="error col-auto"></p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Filter 2:</div>
|
||||||
|
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter2-choice" name="filter2-choice" placeholder="ANY"/>
|
||||||
|
<p class="error col-auto"></p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Filter 3:</div>
|
||||||
|
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter3-choice" name="filter3-choice" placeholder="ANY"/>
|
||||||
|
<p class="error col-auto"></p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-lg-3 col-sm-12">
|
||||||
|
<div class = "col fw-bold">Filter 4:</div>
|
||||||
|
<input class = "border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="filter4-choice" name="filter4-choice" placeholder="ANY"/>
|
||||||
|
<p class="error col-auto"></p>
|
||||||
|
</div>
|
||||||
|
<datalist id = "filter-items"></datalist>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col-auto">
|
||||||
|
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "search-button" onclick = "doItemSearch()">
|
||||||
|
Search!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class = "col-auto">
|
||||||
|
<button class = "button rounded scaled-font fw-bold text-light dark-5" id = "reset-button" onclick = "resetItemSearch()">
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row box-title justify-content-center" id = "summary">
|
||||||
|
</div>
|
||||||
|
<div class = "row" id = "search-results">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/autoComplete.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/build_utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/icons.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/damage_calc.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/display_constants.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/display.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/sq2display_constants.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/sq2display.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/query.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/query_2.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/expr_parser.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/load.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/items.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/sq2items.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
74
items_2.html
|
@ -1,74 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html scroll-behavior="smooth">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="HandheldFriendly" content="true" />
|
|
||||||
<meta name="MobileOptimized" content="320" />
|
|
||||||
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
|
|
||||||
<!-- nunito font, copying wynndata -->
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
<link rel="stylesheet" href="/css/items_2.css">
|
|
||||||
<link rel="icon" href="./media/icons/new/searcher.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
<title>WynnAtlas</title>
|
|
||||||
</head>
|
|
||||||
<body class="all">
|
|
||||||
<header class="header nomarginp">
|
|
||||||
<div class="headerleft" id = "headerleft">
|
|
||||||
</div>
|
|
||||||
<div class="headercenter" id = "headercenter">
|
|
||||||
<div>
|
|
||||||
<p class="itemp" id="header">WynnAtlas</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="headerright" id = "headerright">
|
|
||||||
<div class="center" id="basic">
|
|
||||||
<a href="./items.html" class="link">Basic Search</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div class="center" id="credits">
|
|
||||||
<a href="https://hppeng-wynn.github.io/credits.txt" class="link">Additional credits</a>
|
|
||||||
</div>
|
|
||||||
<div class="center" id="help">
|
|
||||||
<a href="items_2_help.html" class="link" target="_blank">Search Guide</a>
|
|
||||||
</div>
|
|
||||||
<div class="center" id="main">
|
|
||||||
<div id="search-container">
|
|
||||||
<div class="search-field-container left" id="search-filter">
|
|
||||||
<label class="search-field-label" for="search-filter-field">Filter By:</label>
|
|
||||||
<input class="search-field" id="search-filter-field" type="text" autofocus="true"
|
|
||||||
placeholder="name ?= "blue" & str >= 15 & dex >= 10">
|
|
||||||
<div class="search-field-error" id="search-filter-error"></div>
|
|
||||||
<div class="search-field-compl" id="search-filter-compl"></div>
|
|
||||||
</div>
|
|
||||||
<div class="search-field-container left" id="search-sort">
|
|
||||||
<label class="search-field-label" for="search-sort-field">Sort By:</label>
|
|
||||||
<input class="search-field" id="search-sort-field" type="text"
|
|
||||||
placeholder="str + dex; meleerawdmg + spellrawdmg">
|
|
||||||
<div class="search-field-error" id="search-sort-error"></div>
|
|
||||||
<div class="search-field-compl" id="search-sort-compl"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="item-list-container">
|
|
||||||
<div class="left" id="item-list"></div>
|
|
||||||
<div class="center" id="item-list-footer"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="scroll-up">↑</div>
|
|
||||||
<script type="text/javascript" src="/js/utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/build_utils.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/loadheader.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/display_constants.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/display.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/query_2.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/expr_parser.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/load.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/items_2.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
14
items_adv/credits.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Theme, formatting, and overall inspiration: Wynndata (Dukio)
|
||||||
|
- https://wynndata.tk
|
||||||
|
|
||||||
|
The game, of course
|
||||||
|
- wynncraft.com
|
||||||
|
|
||||||
|
Additional Contributors:
|
||||||
|
- Kiocifer (Icons!)
|
||||||
|
- Lennon (Skill point formula reversing)
|
||||||
|
- Phanta (WynnAtlas custom expression parser / item search)
|
||||||
|
- QuantumNep (Layout code/layout ideas)
|
||||||
|
- nbcss (Crafted Item mechanics reverse engineering)
|
||||||
|
- dr_carlos (Hiding UI elements properly, fade animations, proper error handling)
|
||||||
|
- Atlas Inc discord (feedback, ideas, damage calc, etc)
|
BIN
items_adv/help_photo.png
Normal file
After Width: | Height: | Size: 65 KiB |
88
items_adv/index.html
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html scroll-behavior="smooth">
|
||||||
|
<head>
|
||||||
|
<title>WynnAtlas</title>
|
||||||
|
<link rel="icon" href="../media/icons/new/searcher.png" type="image/icon type">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
<link rel="stylesheet" href="../css/items_2.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
|
</head>
|
||||||
|
<body class = "text-light d-flex justify-content-center" id = "body">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<a href = "/customizer.html"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
|
||||||
|
<a href = "/map.html"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
|
||||||
|
<a href = "/wynnfo/index.html"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</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 py-5 vh-100 mx-0 mx-lg-auto scaled-font">
|
||||||
|
<div class = "col">
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col text-start" id = "credits">
|
||||||
|
<a href="credits.txt" class="link">Additional credits</a>
|
||||||
|
</div>
|
||||||
|
<div class = "col text-center" id = "help">
|
||||||
|
<a href="items_2_help.html" class="link" target="_blank">Search Guide</a>
|
||||||
|
</div>
|
||||||
|
<div class = "col text-end">
|
||||||
|
<a href = "../items/">Basic Item Search</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class="col" id="main">
|
||||||
|
<div class = "row" id="search-container">
|
||||||
|
<div class="col search-field-container" id="search-filter">
|
||||||
|
<div class="col fw-bold">Filter By:</div>
|
||||||
|
<input class="col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="search-filter-field" type="text" autofocus="true"
|
||||||
|
placeholder="name ?= "blue" & str >= 15 & dex >= 10">
|
||||||
|
<div class="search-field-error" id="search-filter-error"></div>
|
||||||
|
<div class="search-field-compl" id="search-filter-compl"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col search-field-container" id="search-sort">
|
||||||
|
<div class="col fw-bold">Sort By:</div>
|
||||||
|
<input class="col border-dark text-light dark-5 rounded scaled-font form-control form-control-sm" id="search-sort-field" type="text"
|
||||||
|
placeholder="str + dex; meleerawdmg + spellrawdmg">
|
||||||
|
<div class="search-field-error" id="search-sort-error"></div>
|
||||||
|
<div class="search-field-compl" id="search-sort-compl"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row" id="item-list-container">
|
||||||
|
<div class="row" id="item-list"></div>
|
||||||
|
<div class="row" id="item-list-footer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row" id="scroll-up">↑</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/js/utils.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/build_utils.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/icons.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/damage_calc.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/display_constants.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/display.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/query_2.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/expr_parser.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/load.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/items_2.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,32 +1,45 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html scroll-behavior="smooth">
|
<html scroll-behavior="smooth">
|
||||||
<head>
|
<head>
|
||||||
<!-- nunito font, copying wynndata -->
|
<title>WynnAtlas Help</title>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
<link rel="icon" href="../media/icons/new/searcher.png">
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
|
||||||
|
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="/css/article.css">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
<link rel="icon" href="./media/icons/new/searcher.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
<title>WynnAtlas</title>
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="../css/article.css">
|
||||||
|
<link rel="stylesheet" href="../css/styles.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="all" style="overflow-y: scroll">
|
<body class="all" style="overflow-y: scroll">
|
||||||
<header class="header nomarginp">
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
<div class="headerleft" id = "headerleft">
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
</div>
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
<div class="headercenter" id = "headercenter">
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
<div>
|
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
|
||||||
<p class="itemp" id="header">WynnAtlas</p>
|
<a href = "../map/"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
|
||||||
</div>
|
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
</div>
|
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
|
||||||
<div class="headerright" id = "headerright">
|
<hr/>
|
||||||
</div>
|
<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>
|
||||||
</header>
|
</div>
|
||||||
<main>
|
<main>
|
||||||
<div class="full-width">
|
<div class="full-width text-center">
|
||||||
<img src="https://i.imgur.com/Ap6Zd3Q.png"/>
|
<img src="./help_photo.png"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>What the heck is “Advanced Item Search”?</h2>
|
<h2>What the heck is “Advanced Item Search”?</h2>
|
||||||
<p>The WynnBuilder team has been hard at work giving you the latest and greatest tools for optimizing your most complex Wynncraft builds. Now, we're introducing <strong class="rb-text">WynnAtlas</strong>, the new, bigger, better, smarter, powerful-er item guide! Featuring an extremely flexible expression language for filtering and sorting items, WynnAtlas' advanced item search system gives build engineers the power to select items with a high degree of granularity. Picking components for your brand-new Divzer WFA build has never been easier!</p>
|
<p>The WynnBuilder team has been hard at work giving you the latest and greatest tools for optimizing your most complex Wynncraft builds. Now, we're introducing <strong class="rb-text">WynnAtlas</strong>, the new, bigger, better, smarter, powerful-er item guide! Featuring an extremely flexible expression language for filtering and sorting items, WynnAtlas' advanced item search system gives build engineers the power to select items with a high degree of granularity. Picking components for your brand-new Divzer WFA build has never been easier!</p>
|
||||||
|
@ -208,7 +221,6 @@
|
||||||
docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2]));
|
docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2]));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="/js/loadheader.js"></script>
|
<script type="text/javascript" src="../js/sq2icons.js"></script>
|
||||||
<script type="text/javascript" src="/js/icons.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
5
js/README_atree_constants.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
How to convert:
|
||||||
|
|
||||||
|
1. edit `atree_constants.js`
|
||||||
|
2. run `python3 ../py_script/atree-generateID.py
|
||||||
|
3. check that the site still works
|
61
js/atlas.js
|
@ -6,25 +6,33 @@ function setTitle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle();
|
setTitle();
|
||||||
const flavortexts = ["JALA?? \n ATLAS?? \n ANYONE??",
|
const flavortexts = [
|
||||||
"this really do be a bruh moment.",
|
"JALA?? \n ATLAS?? \n ANYONE??",
|
||||||
"OH, LOOK AT YOU. YOU FOUND THE FUNNY BUILDER GUILDER MEME PAGE. AREN'T YOU PROUD OF YOURSELF?",
|
"this really do be a bruh moment.",
|
||||||
"Downloading Atlas Inc Virus 2.0...",
|
"OH, LOOK AT YOU. YOU FOUND THE FUNNY BUILDER GUILDER MEME PAGE. AREN'T YOU PROUD OF YOURSELF?",
|
||||||
"Any WynnBuilders in the chat?",
|
"Downloading Atlas Inc Virus 2.0...",
|
||||||
":sunglaso:",
|
"Any WynnBuilders in the chat?",
|
||||||
"This says a lot about our society.",
|
":sunglaso:",
|
||||||
"WynnCraft is overrated. Stay on this page forever!",
|
"This says a lot about our society.",
|
||||||
"Now trading Smash invite letters for Atlas Inc invites!",
|
"WynnCraft is overrated. Stay on this page forever!",
|
||||||
"You have reached the customer support page of Wynnbuilder. Please call [REDACTED] to get your problems solved!",
|
"Now trading Smash invite letters for Atlas Inc invites!",
|
||||||
"",
|
"You have reached the customer support page of Wynnbuilder. Please call [REDACTED] to get your problems solved!",
|
||||||
"Isn't this like that one game Amogus?",
|
"Mom, what does 'hppeng' mean?",
|
||||||
"Mom, what does 'hppeng' mean?",
|
"hpgbegg",
|
||||||
"hpgbegg",
|
"| |I || |_",
|
||||||
"| |I || |_",
|
"Wynn was so good they made Wynn 2",
|
||||||
"",
|
"Join Monumenta today!",
|
||||||
];
|
"do NOT look up the 25th largest island of Greece",
|
||||||
|
"whatever you do, don't search for lego piece 26047.",
|
||||||
|
"guys what does perbromic acid look like",
|
||||||
|
"Hello Chat",
|
||||||
|
"Goodbye Chat",
|
||||||
|
"Look up. Now look down. Now look up again. Spin your head 3 times clockwise. You look real silly.",
|
||||||
|
"There\'s \'guillble\' written on the ceiling",
|
||||||
|
"when the pretender is conspicuous...",
|
||||||
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"];
|
||||||
const dt = 10; //millis
|
const dt = 10; //millis
|
||||||
const PIX_PER_SEC = 1000;
|
const PIX_PER_SEC = 1337;
|
||||||
const EPSILON = 1E-7 * dt;
|
const EPSILON = 1E-7 * dt;
|
||||||
let atli = [];
|
let atli = [];
|
||||||
|
|
||||||
|
@ -32,11 +40,13 @@ let atli = [];
|
||||||
function atlasClick() {
|
function atlasClick() {
|
||||||
let atlas = document.createElement("div");
|
let atlas = document.createElement("div");
|
||||||
let atlas_img = document.createElement("img");
|
let atlas_img = document.createElement("img");
|
||||||
atlas_img.src = "favicon.png";
|
atlas.style.maxWidth = "64px";
|
||||||
|
atlas.style.maxHeight = "64px";
|
||||||
|
atlas_img.src = "../atlas/favicon.png";
|
||||||
atlas_img.style.width = "100%";
|
atlas_img.style.width = "100%";
|
||||||
atlas_img.style.height = "100%";
|
atlas_img.style.height = "100%";
|
||||||
atlas_img.style.maxWidth = "48px";
|
atlas_img.style.maxWidth = "64px";
|
||||||
atlas_img.style.maxHeight = "48px";
|
atlas_img.style.maxHeight = "64px";
|
||||||
atlas_img.style.zIndex = 1;
|
atlas_img.style.zIndex = 1;
|
||||||
atlas.classList.add("atlas");
|
atlas.classList.add("atlas");
|
||||||
let roll = Math.random();
|
let roll = Math.random();
|
||||||
|
@ -44,7 +54,7 @@ function atlasClick() {
|
||||||
let rollList = ["lmoa","doom","agony","enraged","sunglaso","thonk","unglaso"];
|
let rollList = ["lmoa","doom","agony","enraged","sunglaso","thonk","unglaso"];
|
||||||
for (let i = rollList.length-1; i > -1; i--) {
|
for (let i = rollList.length-1; i > -1; i--) {
|
||||||
if (roll < (i+1) * rollchance) {
|
if (roll < (i+1) * rollchance) {
|
||||||
atlas_img.src = "./media/memes/" + rollList[i] + ".png";
|
atlas_img.src = "../media/memes/" + rollList[i] + ".png";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atlas.appendChild(atlas_img);
|
atlas.appendChild(atlas_img);
|
||||||
|
@ -52,7 +62,8 @@ function atlasClick() {
|
||||||
|
|
||||||
atlas.style.position = "absolute";
|
atlas.style.position = "absolute";
|
||||||
rect = document.getElementById("bodydiv").getBoundingClientRect(); //use rect.top, rect.left, rect.bottom, and rect.top
|
rect = document.getElementById("bodydiv").getBoundingClientRect(); //use rect.top, rect.left, rect.bottom, and rect.top
|
||||||
|
console.log(rect)
|
||||||
|
|
||||||
atlasrect = atlas.getBoundingClientRect();
|
atlasrect = atlas.getBoundingClientRect();
|
||||||
atlas.style.left = Math.floor((rect.right - rect.left - 2*(atlasrect.right - atlasrect.left) ) * Math.random() + rect.left + (atlasrect.right - atlasrect.left) )+ "px";
|
atlas.style.left = Math.floor((rect.right - rect.left - 2*(atlasrect.right - atlasrect.left) ) * Math.random() + rect.left + (atlasrect.right - atlasrect.left) )+ "px";
|
||||||
atlas.style.top = Math.floor((rect.bottom - rect.top - 2*(atlasrect.bottom - atlasrect.top) ) * Math.random() + rect.top + (atlasrect.bottom - atlasrect.top) ) + "px";
|
atlas.style.top = Math.floor((rect.bottom - rect.top - 2*(atlasrect.bottom - atlasrect.top) ) * Math.random() + rect.top + (atlasrect.bottom - atlasrect.top) ) + "px";
|
||||||
|
@ -108,6 +119,10 @@ function runAtlas() {
|
||||||
let center = [(at1[0]+at2[0])/2, (at1[1]+at2[1])/2 ];
|
let center = [(at1[0]+at2[0])/2, (at1[1]+at2[1])/2 ];
|
||||||
|
|
||||||
if (Math.sqrt(((at2[1]+atlas2.vy) - (at1[1]+atlas1.vy))**2 + ((at2[0]+atlas2.vx) - (at1[0]+atlas1.vx))**2) < 2*r) {
|
if (Math.sqrt(((at2[1]+atlas2.vy) - (at1[1]+atlas1.vy))**2 + ((at2[0]+atlas2.vx) - (at1[0]+atlas1.vx))**2) < 2*r) {
|
||||||
|
//Play bruh sound effect
|
||||||
|
document.getElementById('bruh_sound_effect').play();
|
||||||
|
document.getElementById('bruh_sound_effect').currentTime = 0;
|
||||||
|
|
||||||
if(Math.sqrt( (at2[1]-at1[1])**2 + (at2[0]-at1[0])**2 ) < 2*r ) {//check for collision
|
if(Math.sqrt( (at2[1]-at1[1])**2 + (at2[0]-at1[0])**2 ) < 2*r ) {//check for collision
|
||||||
//Move both away slightly - correct alg this time :)
|
//Move both away slightly - correct alg this time :)
|
||||||
atlas1.style.left = parseFloat(atlas1.style.left.replace("px","")) + (at1[0]-center[0]) * 2 * r / Math.sqrt(dx**2 + dy**2) + "px";
|
atlas1.style.left = parseFloat(atlas1.style.left.replace("px","")) + (at1[0]-center[0]) * 2 * r / Math.sqrt(dx**2 + dy**2) + "px";
|
||||||
|
|
1082
js/atree.js
Normal file
3978
js/atree_constants.js
Normal file
1
js/atree_constants_min.js
Normal file
146
js/atree_ids.json
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
{
|
||||||
|
"Archer": {
|
||||||
|
"Arrow Shield": 0,
|
||||||
|
"Escape": 1,
|
||||||
|
"Arrow Bomb": 2,
|
||||||
|
"Heart Shatter": 3,
|
||||||
|
"Fire Creep": 4,
|
||||||
|
"Bryophyte Roots": 5,
|
||||||
|
"Nimble String": 6,
|
||||||
|
"Arrow Storm": 7,
|
||||||
|
"Guardian Angels": 8,
|
||||||
|
"Windy Feet": 9,
|
||||||
|
"Basaltic Trap": 10,
|
||||||
|
"Windstorm": 11,
|
||||||
|
"Grappling Hook": 12,
|
||||||
|
"Implosion": 13,
|
||||||
|
"Twain's Arc": 14,
|
||||||
|
"Fierce Stomp": 15,
|
||||||
|
"Scorched Earth": 16,
|
||||||
|
"Leap": 17,
|
||||||
|
"Shocking Bomb": 18,
|
||||||
|
"Mana Trap": 19,
|
||||||
|
"Escape Artist": 20,
|
||||||
|
"Initiator": 21,
|
||||||
|
"Call of the Hound": 22,
|
||||||
|
"Arrow Hurricane": 23,
|
||||||
|
"Geyser Stomp": 24,
|
||||||
|
"Crepuscular Ray": 25,
|
||||||
|
"Grape Bomb": 26,
|
||||||
|
"Tangled Traps": 27,
|
||||||
|
"Snow Storm": 28,
|
||||||
|
"All-Seeing Panoptes": 29,
|
||||||
|
"Minefield": 30,
|
||||||
|
"Bow Proficiency I": 31,
|
||||||
|
"Cheaper Arrow Bomb": 32,
|
||||||
|
"Cheaper Arrow Storm": 33,
|
||||||
|
"Cheaper Escape": 34,
|
||||||
|
"Earth Mastery": 35,
|
||||||
|
"Thunder Mastery": 36,
|
||||||
|
"Water Mastery": 37,
|
||||||
|
"Air Mastery": 38,
|
||||||
|
"Fire Mastery": 39,
|
||||||
|
"More Shields": 40,
|
||||||
|
"Stormy Feet": 41,
|
||||||
|
"Refined Gunpowder": 42,
|
||||||
|
"More Traps": 43,
|
||||||
|
"Better Arrow Shield": 44,
|
||||||
|
"Better Leap": 45,
|
||||||
|
"Better Guardian Angels": 46,
|
||||||
|
"Cheaper Arrow Storm (2)": 47,
|
||||||
|
"Precise Shot": 48,
|
||||||
|
"Cheaper Arrow Shield": 49,
|
||||||
|
"Rocket Jump": 50,
|
||||||
|
"Cheaper Escape (2)": 51,
|
||||||
|
"Stronger Hook": 52,
|
||||||
|
"Cheaper Arrow Bomb (2)": 53,
|
||||||
|
"Bouncing Bomb": 54,
|
||||||
|
"Homing Shots": 55,
|
||||||
|
"Shrapnel Bomb": 56,
|
||||||
|
"Elusive": 57,
|
||||||
|
"Double Shots": 58,
|
||||||
|
"Triple Shots": 59,
|
||||||
|
"Power Shots": 60,
|
||||||
|
"Focus": 61,
|
||||||
|
"More Focus": 62,
|
||||||
|
"More Focus (2)": 63,
|
||||||
|
"Traveler": 64,
|
||||||
|
"Patient Hunter": 65,
|
||||||
|
"Stronger Patient Hunter": 66,
|
||||||
|
"Frenzy": 67,
|
||||||
|
"Phantom Ray": 68,
|
||||||
|
"Arrow Rain": 69,
|
||||||
|
"Decimator": 70
|
||||||
|
},
|
||||||
|
"Warrior": {
|
||||||
|
"Bash": 0,
|
||||||
|
"Spear Proficiency 1": 1,
|
||||||
|
"Cheaper Bash": 2,
|
||||||
|
"Double Bash": 3,
|
||||||
|
"Charge": 4,
|
||||||
|
"Heavy Impact": 5,
|
||||||
|
"Vehement": 6,
|
||||||
|
"Tougher Skin": 7,
|
||||||
|
"Uppercut": 8,
|
||||||
|
"Cheaper Charge": 9,
|
||||||
|
"War Scream": 10,
|
||||||
|
"Earth Mastery": 11,
|
||||||
|
"Thunder Mastery": 12,
|
||||||
|
"Water Mastery": 13,
|
||||||
|
"Air Mastery": 14,
|
||||||
|
"Fire Mastery": 15,
|
||||||
|
"Quadruple Bash": 16,
|
||||||
|
"Fireworks": 17,
|
||||||
|
"Half-Moon Swipe": 18,
|
||||||
|
"Flyby Jab": 19,
|
||||||
|
"Flaming Uppercut": 20,
|
||||||
|
"Iron Lungs": 21,
|
||||||
|
"Generalist": 22,
|
||||||
|
"Counter": 23,
|
||||||
|
"Mantle of the Bovemists": 24,
|
||||||
|
"Bak'al's Grasp": 25,
|
||||||
|
"Spear Proficiency 2": 26,
|
||||||
|
"Cheaper Uppercut": 27,
|
||||||
|
"Aerodynamics": 28,
|
||||||
|
"Provoke": 29,
|
||||||
|
"Precise Strikes": 30,
|
||||||
|
"Air Shout": 31,
|
||||||
|
"Enraged Blow": 32,
|
||||||
|
"Flying Kick": 33,
|
||||||
|
"Stronger Mantle": 34,
|
||||||
|
"Manachism": 35,
|
||||||
|
"Boiling Blood": 36,
|
||||||
|
"Ragnarokkr": 37,
|
||||||
|
"Ambidextrous": 38,
|
||||||
|
"Burning Heart": 39,
|
||||||
|
"Stronger Bash": 40,
|
||||||
|
"Intoxicating Blood": 41,
|
||||||
|
"Comet": 42,
|
||||||
|
"Collide": 43,
|
||||||
|
"Rejuvenating Skin": 44,
|
||||||
|
"Uncontainable Corruption": 45,
|
||||||
|
"Radiant Devotee": 46,
|
||||||
|
"Whirlwind Strike": 47,
|
||||||
|
"Mythril Skin": 48,
|
||||||
|
"Armour Breaker": 49,
|
||||||
|
"Shield Strike": 50,
|
||||||
|
"Sparkling Hope": 51,
|
||||||
|
"Massive Bash": 52,
|
||||||
|
"Tempest": 53,
|
||||||
|
"Spirit of the Rabbit": 54,
|
||||||
|
"Massacre": 55,
|
||||||
|
"Axe Kick": 56,
|
||||||
|
"Radiance": 57,
|
||||||
|
"Cheaper Bash 2": 58,
|
||||||
|
"Cheaper War Scream": 59,
|
||||||
|
"Discombobulate": 60,
|
||||||
|
"Thunderclap": 61,
|
||||||
|
"Cyclone": 62,
|
||||||
|
"Second Chance": 63,
|
||||||
|
"Blood Pact": 64,
|
||||||
|
"Haemorrhage": 65,
|
||||||
|
"Brink of Madness": 66,
|
||||||
|
"Cheaper Uppercut 2": 67,
|
||||||
|
"Martyr": 68
|
||||||
|
}
|
||||||
|
}
|
405
js/build.js
|
@ -100,238 +100,12 @@ class Build{
|
||||||
* @param {Number[]} powders : Powder application. List of lists of integers (powder IDs).
|
* @param {Number[]} powders : Powder application. List of lists of integers (powder IDs).
|
||||||
* In order: boots, Chestplate, Leggings, Boots, Weapon.
|
* In order: boots, Chestplate, Leggings, Boots, Weapon.
|
||||||
* @param {Object[]} inputerrors : List of instances of error-like classes.
|
* @param {Object[]} inputerrors : List of instances of error-like classes.
|
||||||
|
*
|
||||||
|
* @param {Object[]} tomes: List of tomes.
|
||||||
|
* In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome.
|
||||||
|
* 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building)
|
||||||
*/
|
*/
|
||||||
constructor(level,equipment, powders, externalStats, inputerrors=[]){
|
constructor(level, items, weapon){
|
||||||
|
|
||||||
let errors = inputerrors;
|
|
||||||
//this contains the Craft objects, if there are any crafted items. this.boots, etc. will contain the statMap of the Craft (which is built to be an expandedItem).
|
|
||||||
this.craftedItems = [];
|
|
||||||
this.customItems = [];
|
|
||||||
// NOTE: powders is just an array of arrays of powder IDs. Not powder objects.
|
|
||||||
this.powders = powders;
|
|
||||||
if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") {
|
|
||||||
const helmet = itemMap.get(equipment[0]);
|
|
||||||
this.powders[0] = this.powders[0].slice(0,helmet.slots);
|
|
||||||
this.helmet = expandItem(helmet, this.powders[0]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let helmet = getCustomFromHash(equipment[0]) ? getCustomFromHash(equipment[0]) : (getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : undefined);
|
|
||||||
if (helmet.statMap.get("type") !== "helmet") {
|
|
||||||
throw new Error("Not a helmet");
|
|
||||||
}
|
|
||||||
this.powders[0] = this.powders[0].slice(0,helmet.statMap.get("slots"));
|
|
||||||
helmet.statMap.set("powders",this.powders[0].slice());
|
|
||||||
this.helmet = helmet.statMap;
|
|
||||||
applyArmorPowders(this.helmet, this.powders[0]);
|
|
||||||
if (this.helmet.get("custom")) {
|
|
||||||
this.customItems.push(helmet);
|
|
||||||
} else if (this.helmet.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(helmet);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Error) {
|
|
||||||
const helmet = itemMap.get("No Helmet");
|
|
||||||
this.powders[0] = this.powders[0].slice(0,helmet.slots);
|
|
||||||
this.helmet = expandItem(helmet, this.powders[0]);
|
|
||||||
errors.push(new ItemNotFound(equipment[0], "helmet", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") {
|
|
||||||
const chestplate = itemMap.get(equipment[1]);
|
|
||||||
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
|
|
||||||
this.chestplate = expandItem(chestplate, this.powders[1]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let chestplate = getCustomFromHash(equipment[1]) ? getCustomFromHash(equipment[1]) : (getCraftFromHash(equipment[1]) ? getCraftFromHash(equipment[1]) : undefined);
|
|
||||||
if (chestplate.statMap.get("type") !== "chestplate") {
|
|
||||||
throw new Error("Not a chestplate");
|
|
||||||
}
|
|
||||||
this.powders[1] = this.powders[1].slice(0,chestplate.statMap.get("slots"));
|
|
||||||
chestplate.statMap.set("powders",this.powders[1].slice());
|
|
||||||
this.chestplate = chestplate.statMap;
|
|
||||||
applyArmorPowders(this.chestplate, this.powders[1]);
|
|
||||||
if (this.chestplate.get("custom")) {
|
|
||||||
this.customItems.push(chestplate);
|
|
||||||
} else if (this.chestplate.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(chestplate);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
console.log(Error);
|
|
||||||
const chestplate = itemMap.get("No Chestplate");
|
|
||||||
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
|
|
||||||
this.chestplate = expandItem(chestplate, this.powders[1]);
|
|
||||||
errors.push(new ItemNotFound(equipment[1], "chestplate", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") {
|
|
||||||
const leggings = itemMap.get(equipment[2]);
|
|
||||||
this.powders[2] = this.powders[2].slice(0,leggings.slots);
|
|
||||||
this.leggings = expandItem(leggings, this.powders[2]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let leggings = getCustomFromHash(equipment[2]) ? getCustomFromHash(equipment[2]) : (getCraftFromHash(equipment[2]) ? getCraftFromHash(equipment[2]) : undefined);
|
|
||||||
if (leggings.statMap.get("type") !== "leggings") {
|
|
||||||
throw new Error("Not a leggings");
|
|
||||||
}
|
|
||||||
this.powders[2] = this.powders[2].slice(0,leggings.statMap.get("slots"));
|
|
||||||
leggings.statMap.set("powders",this.powders[2].slice());
|
|
||||||
this.leggings = leggings.statMap;
|
|
||||||
applyArmorPowders(this.leggings, this.powders[2]);
|
|
||||||
if (this.leggings.get("custom")) {
|
|
||||||
this.customItems.push(leggings);
|
|
||||||
} else if (this.leggings.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(leggings);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const leggings = itemMap.get("No Leggings");
|
|
||||||
this.powders[2] = this.powders[2].slice(0,leggings.slots);
|
|
||||||
this.leggings = expandItem(leggings, this.powders[2]);
|
|
||||||
errors.push(new ItemNotFound(equipment[2], "leggings", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") {
|
|
||||||
const boots = itemMap.get(equipment[3]);
|
|
||||||
this.powders[3] = this.powders[3].slice(0,boots.slots);
|
|
||||||
this.boots = expandItem(boots, this.powders[3]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let boots = getCustomFromHash(equipment[3]) ? getCustomFromHash(equipment[3]) : (getCraftFromHash(equipment[3]) ? getCraftFromHash(equipment[3]) : undefined);
|
|
||||||
if (boots.statMap.get("type") !== "boots") {
|
|
||||||
throw new Error("Not a boots");
|
|
||||||
}
|
|
||||||
this.powders[3] = this.powders[3].slice(0,boots.statMap.get("slots"));
|
|
||||||
boots.statMap.set("powders",this.powders[3].slice());
|
|
||||||
this.boots = boots.statMap;
|
|
||||||
applyArmorPowders(this.boots, this.powders[3]);
|
|
||||||
if (this.boots.get("custom")) {
|
|
||||||
this.customItems.push(boots);
|
|
||||||
} else if (this.boots.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(boots);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const boots = itemMap.get("No Boots");
|
|
||||||
this.powders[3] = this.powders[3].slice(0,boots.slots);
|
|
||||||
this.boots = expandItem(boots, this.powders[3]);
|
|
||||||
errors.push(new ItemNotFound(equipment[3], "boots", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") {
|
|
||||||
const ring = itemMap.get(equipment[4]);
|
|
||||||
this.ring1 = expandItem(ring, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let ring = getCustomFromHash(equipment[4]) ? getCustomFromHash(equipment[4]) : (getCraftFromHash(equipment[4]) ? getCraftFromHash(equipment[4]) : undefined);
|
|
||||||
if (ring.statMap.get("type") !== "ring") {
|
|
||||||
throw new Error("Not a ring");
|
|
||||||
}
|
|
||||||
this.ring1 = ring.statMap;
|
|
||||||
if (this.ring1.get("custom")) {
|
|
||||||
this.customItems.push(ring);
|
|
||||||
} else if (this.ring1.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(ring);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const ring = itemMap.get("No Ring 1");
|
|
||||||
this.ring1 = expandItem(ring, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[4], "ring1", true, "ring"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") {
|
|
||||||
const ring = itemMap.get(equipment[5]);
|
|
||||||
this.ring2 = expandItem(ring, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let ring = getCustomFromHash(equipment[5]) ? getCustomFromHash(equipment[5]) : (getCraftFromHash(equipment[5]) ? getCraftFromHash(equipment[5]) : undefined);
|
|
||||||
if (ring.statMap.get("type") !== "ring") {
|
|
||||||
throw new Error("Not a ring");
|
|
||||||
}
|
|
||||||
this.ring2 = ring.statMap;
|
|
||||||
if (this.ring2.get("custom")) {
|
|
||||||
this.customItems.push(ring);
|
|
||||||
} else if (this.ring2.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(ring);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const ring = itemMap.get("No Ring 2");
|
|
||||||
this.ring2 = expandItem(ring, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[5], "ring2", true, "ring"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") {
|
|
||||||
const bracelet = itemMap.get(equipment[6]);
|
|
||||||
this.bracelet = expandItem(bracelet, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let bracelet = getCustomFromHash(equipment[6]) ? getCustomFromHash(equipment[6]) : (getCraftFromHash(equipment[6]) ? getCraftFromHash(equipment[6]) : undefined);
|
|
||||||
if (bracelet.statMap.get("type") !== "bracelet") {
|
|
||||||
throw new Error("Not a bracelet");
|
|
||||||
}
|
|
||||||
this.bracelet = bracelet.statMap;
|
|
||||||
if (this.bracelet.get("custom")) {
|
|
||||||
this.customItems.push(bracelet);
|
|
||||||
} else if (this.bracelet.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(bracelet);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const bracelet = itemMap.get("No Bracelet");
|
|
||||||
this.bracelet = expandItem(bracelet, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[6], "bracelet", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") {
|
|
||||||
const necklace = itemMap.get(equipment[7]);
|
|
||||||
this.necklace = expandItem(necklace, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let necklace = getCustomFromHash(equipment[7]) ? getCustomFromHash(equipment[7]) : (getCraftFromHash(equipment[7]) ? getCraftFromHash(equipment[7]) : undefined);
|
|
||||||
if (necklace.statMap.get("type") !== "necklace") {
|
|
||||||
throw new Error("Not a necklace");
|
|
||||||
}
|
|
||||||
this.necklace = necklace.statMap;
|
|
||||||
if (this.necklace.get("custom")) {
|
|
||||||
this.customItems.push(necklace);
|
|
||||||
} else if (this.necklace.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(necklace);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const necklace = itemMap.get("No Necklace");
|
|
||||||
this.necklace = expandItem(necklace, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[7], "necklace", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") {
|
|
||||||
const weapon = itemMap.get(equipment[8]);
|
|
||||||
this.powders[4] = this.powders[4].slice(0,weapon.slots);
|
|
||||||
this.weapon = expandItem(weapon, this.powders[4]);
|
|
||||||
if (equipment[8] !== "No Weapon") {
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "grid";
|
|
||||||
} else {
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "none";
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let weapon = getCustomFromHash(equipment[8]) ? getCustomFromHash(equipment[8]) : (getCraftFromHash(equipment[8]) ? getCraftFromHash(equipment[8]) : undefined);
|
|
||||||
if (weapon.statMap.get("category") !== "weapon") {
|
|
||||||
throw new Error("Not a weapon");
|
|
||||||
}
|
|
||||||
this.weapon = weapon.statMap;
|
|
||||||
if (this.weapon.get("custom")) {
|
|
||||||
this.customItems.push(weapon);
|
|
||||||
} else if (this.weapon.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(weapon);
|
|
||||||
}
|
|
||||||
this.powders[4] = this.powders[4].slice(0,this.weapon.get("slots"));
|
|
||||||
this.weapon.set("powders",this.powders[4].slice());
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "grid";
|
|
||||||
} catch (Error) {
|
|
||||||
const weapon = itemMap.get("No Weapon");
|
|
||||||
this.powders[4] = this.powders[4].slice(0,weapon.slots);
|
|
||||||
this.weapon = expandItem(weapon, this.powders[4]);
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "none";
|
|
||||||
errors.push(new ItemNotFound(equipment[8], "weapon", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log(this.craftedItems)
|
|
||||||
|
|
||||||
if (level < 1) { //Should these be constants?
|
if (level < 1) { //Should these be constants?
|
||||||
this.level = 1;
|
this.level = 1;
|
||||||
|
@ -348,11 +122,13 @@ class Build{
|
||||||
document.getElementById("level-choice").value = this.level;
|
document.getElementById("level-choice").value = this.level;
|
||||||
|
|
||||||
this.availableSkillpoints = levelToSkillPoints(this.level);
|
this.availableSkillpoints = levelToSkillPoints(this.level);
|
||||||
this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ];
|
this.equipment = items;
|
||||||
|
this.weapon = weapon;
|
||||||
this.items = this.equipment.concat([this.weapon]);
|
this.items = this.equipment.concat([this.weapon]);
|
||||||
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
|
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
|
||||||
let result = calculate_skillpoints(this.equipment, this.weapon);
|
|
||||||
console.log(result);
|
// calc skillpoints requires statmaps only
|
||||||
|
let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap);
|
||||||
this.equip_order = result[0];
|
this.equip_order = result[0];
|
||||||
// How many skillpoints the player had to assign (5 number)
|
// How many skillpoints the player had to assign (5 number)
|
||||||
this.base_skillpoints = result[1];
|
this.base_skillpoints = result[1];
|
||||||
|
@ -361,22 +137,8 @@ class Build{
|
||||||
// How many skillpoints assigned (1 number, sum of base_skillpoints)
|
// How many skillpoints assigned (1 number, sum of base_skillpoints)
|
||||||
this.assigned_skillpoints = result[3];
|
this.assigned_skillpoints = result[3];
|
||||||
this.activeSetCounts = result[4];
|
this.activeSetCounts = result[4];
|
||||||
|
|
||||||
// For strength boosts like warscream, vanish, etc.
|
|
||||||
this.damageMultiplier = 1.0;
|
|
||||||
this.defenseMultiplier = 1.0;
|
|
||||||
|
|
||||||
// For other external boosts ;-;
|
|
||||||
this.externalStats = externalStats;
|
|
||||||
|
|
||||||
this.initBuildStats();
|
this.initBuildStats();
|
||||||
|
|
||||||
// Remove every error before adding specific ones
|
|
||||||
for (let i of document.getElementsByClassName("error")) {
|
|
||||||
i.textContent = "";
|
|
||||||
}
|
|
||||||
this.errors = errors;
|
|
||||||
if (errors.length > 0) this.errored = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Returns build in string format
|
/*Returns build in string format
|
||||||
|
@ -385,138 +147,62 @@ class Build{
|
||||||
return [this.equipment,this.weapon].flat();
|
return [this.equipment,this.weapon].flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getters */
|
|
||||||
|
|
||||||
getSpellCost(spellIdx, cost) {
|
|
||||||
return Math.max(1, this.getBaseSpellCost(spellIdx, cost));
|
|
||||||
}
|
|
||||||
|
|
||||||
getBaseSpellCost(spellIdx, cost) {
|
|
||||||
cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2])));
|
|
||||||
cost += this.statMap.get("spRaw"+spellIdx);
|
|
||||||
return Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get melee stats for build.
|
|
||||||
Returns an array in the order:
|
|
||||||
*/
|
|
||||||
getMeleeStats(){
|
|
||||||
const stats = this.statMap;
|
|
||||||
if (this.weapon.get("tier") === "Crafted") {
|
|
||||||
stats.set("damageBases", [this.weapon.get("nDamBaseHigh"),this.weapon.get("eDamBaseHigh"),this.weapon.get("tDamBaseHigh"),this.weapon.get("wDamBaseHigh"),this.weapon.get("fDamBaseHigh"),this.weapon.get("aDamBaseHigh")]);
|
|
||||||
}
|
|
||||||
let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier");
|
|
||||||
if(adjAtkSpd > 6){
|
|
||||||
adjAtkSpd = 6;
|
|
||||||
}else if(adjAtkSpd < 0){
|
|
||||||
adjAtkSpd = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let damage_mult = 1;
|
|
||||||
if (this.weapon.get("type") === "relik") {
|
|
||||||
damage_mult = 0.99; // CURSE YOU WYNNCRAFT
|
|
||||||
//One day we will create WynnWynn and no longer have shaman 99% melee injustice.
|
|
||||||
//In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams.
|
|
||||||
}
|
|
||||||
// 0spellmult for melee damage.
|
|
||||||
let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct") + this.externalStats.get("mdPct"), 0, this.weapon, this.total_skillpoints, damage_mult * this.damageMultiplier, this.externalStats);
|
|
||||||
|
|
||||||
let dex = this.total_skillpoints[1];
|
|
||||||
|
|
||||||
let totalDamNorm = results[0];
|
|
||||||
let totalDamCrit = results[1];
|
|
||||||
totalDamNorm.push(1-skillPointsToPercentage(dex));
|
|
||||||
totalDamCrit.push(skillPointsToPercentage(dex));
|
|
||||||
let damages_results = results[2];
|
|
||||||
|
|
||||||
let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2])
|
|
||||||
+(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2;
|
|
||||||
|
|
||||||
//Now do math
|
|
||||||
let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd];
|
|
||||||
let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd];
|
|
||||||
let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex)));
|
|
||||||
//[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit]
|
|
||||||
return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get all defensive stats for this build.
|
|
||||||
*/
|
|
||||||
getDefenseStats(){
|
|
||||||
const stats = this.statMap;
|
|
||||||
let defenseStats = [];
|
|
||||||
let def_pct = skillPointsToPercentage(this.total_skillpoints[3]);
|
|
||||||
let agi_pct = skillPointsToPercentage(this.total_skillpoints[4]);
|
|
||||||
//total hp
|
|
||||||
let totalHp = stats.get("hp") + stats.get("hpBonus");
|
|
||||||
if (totalHp < 5) totalHp = 5;
|
|
||||||
defenseStats.push(totalHp);
|
|
||||||
//EHP
|
|
||||||
let ehp = [totalHp, totalHp];
|
|
||||||
let defMult = classDefenseMultipliers.get(this.weapon.get("type"));
|
|
||||||
ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
ehp[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
defenseStats.push(ehp);
|
|
||||||
//HPR
|
|
||||||
let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.);
|
|
||||||
defenseStats.push(totalHpr);
|
|
||||||
//EHPR
|
|
||||||
let ehpr = [totalHpr, totalHpr];
|
|
||||||
ehpr[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
ehpr[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
defenseStats.push(ehpr);
|
|
||||||
//skp stats
|
|
||||||
defenseStats.push([ (1 - ((1-def_pct) * (2 - this.defenseMultiplier)))*100, agi_pct*100]);
|
|
||||||
//eledefs - TODO POWDERS
|
|
||||||
let eledefs = [0, 0, 0, 0, 0];
|
|
||||||
for(const i in skp_elements){ //kinda jank but ok
|
|
||||||
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.);
|
|
||||||
}
|
|
||||||
defenseStats.push(eledefs);
|
|
||||||
|
|
||||||
//[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]]
|
|
||||||
return defenseStats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get all stats for this build. Stores in this.statMap.
|
/* Get all stats for this build. Stores in this.statMap.
|
||||||
@pre The build itself should be valid. No checking of validity of pieces is done here.
|
@pre The build itself should be valid. No checking of validity of pieces is done here.
|
||||||
*/
|
*/
|
||||||
initBuildStats(){
|
initBuildStats(){
|
||||||
|
|
||||||
let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "def", "agi"];
|
let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "def", "agi", "damMobs", "defMobs"];
|
||||||
|
|
||||||
|
let must_ids = [
|
||||||
|
"eMdPct","eMdRaw","eSdPct","eSdRaw","eDamPct","eDamRaw","eDamAddMin","eDamAddMax",
|
||||||
|
"tMdPct","tMdRaw","tSdPct","tSdRaw","tDamPct","tDamRaw","tDamAddMin","tDamAddMax",
|
||||||
|
"wMdPct","wMdRaw","wSdPct","wSdRaw","wDamPct","wDamRaw","wDamAddMin","wDamAddMax",
|
||||||
|
"fMdPct","fMdRaw","fSdPct","fSdRaw","fDamPct","fDamRaw","fDamAddMin","fDamAddMax",
|
||||||
|
"aMdPct","aMdRaw","aSdPct","aSdRaw","aDamPct","aDamRaw","aDamAddMin","aDamAddMax",
|
||||||
|
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
|
||||||
|
"mdPct","mdRaw","sdPct","sdRaw","damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
|
||||||
|
"rMdPct","rMdRaw","rSdPct","rSdRaw","rDamPct","rDamRaw","rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
|
||||||
|
]
|
||||||
|
|
||||||
//Create a map of this build's stats
|
//Create a map of this build's stats
|
||||||
let statMap = new Map();
|
let statMap = new Map();
|
||||||
|
statMap.set("defMultiplier", 1);
|
||||||
|
|
||||||
for (const staticID of staticIDs) {
|
for (const staticID of staticIDs) {
|
||||||
statMap.set(staticID, 0);
|
statMap.set(staticID, 0);
|
||||||
}
|
}
|
||||||
|
for (const staticID of must_ids) {
|
||||||
|
statMap.set(staticID, 0);
|
||||||
|
}
|
||||||
statMap.set("hp", levelToHPBase(this.level));
|
statMap.set("hp", levelToHPBase(this.level));
|
||||||
|
|
||||||
let major_ids = new Set();
|
let major_ids = new Set();
|
||||||
for (const item of this.items){
|
for (const item of this.items){
|
||||||
for (let [id, value] of item.get("maxRolls")) {
|
const item_stats = item.statMap;
|
||||||
|
for (let [id, value] of item_stats.get("maxRolls")) {
|
||||||
if (staticIDs.includes(id)) {
|
if (staticIDs.includes(id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
statMap.set(id,(statMap.get(id) || 0)+value);
|
statMap.set(id,(statMap.get(id) || 0)+value);
|
||||||
}
|
}
|
||||||
for (const staticID of staticIDs) {
|
for (const staticID of staticIDs) {
|
||||||
if (item.get(staticID)) {
|
if (item_stats.get(staticID)) {
|
||||||
statMap.set(staticID, statMap.get(staticID) + item.get(staticID));
|
statMap.set(staticID, statMap.get(staticID) + item_stats.get(staticID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.get("majorIds")) {
|
if (item_stats.get("majorIds")) {
|
||||||
for (const major_id of item.get("majorIds")) {
|
for (const major_id of item_stats.get("majorIds")) {
|
||||||
major_ids.add(major_id);
|
major_ids.add(major_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
statMap.set('damageMultiplier', 1 + (statMap.get('damMobs') / 100));
|
||||||
|
statMap.set('defMultiplier', 1 - (statMap.get('defMobs') / 100));
|
||||||
statMap.set("activeMajorIDs", major_ids);
|
statMap.set("activeMajorIDs", major_ids);
|
||||||
for (const [setName, count] of this.activeSetCounts) {
|
for (const [setName, count] of this.activeSetCounts) {
|
||||||
const bonus = sets[setName].bonuses[count-1];
|
const bonus = sets.get(setName).bonuses[count-1];
|
||||||
for (const id in bonus) {
|
for (const id in bonus) {
|
||||||
if (skp_order.includes(id)) {
|
if (skp_order.includes(id)) {
|
||||||
// pass. Don't include skillpoints in ids
|
// pass. Don't include skillpoints in ids
|
||||||
|
@ -529,27 +215,8 @@ class Build{
|
||||||
statMap.set("poisonPct", 100);
|
statMap.set("poisonPct", 100);
|
||||||
|
|
||||||
// The stuff relevant for damage calculation!!! @ferricles
|
// The stuff relevant for damage calculation!!! @ferricles
|
||||||
statMap.set("atkSpd", this.weapon.get("atkSpd"));
|
statMap.set("atkSpd", this.weapon.statMap.get("atkSpd"));
|
||||||
|
|
||||||
for (const x of skp_elements) {
|
|
||||||
this.externalStats.set(x + "DamPct", 0);
|
|
||||||
}
|
|
||||||
this.externalStats.set("mdPct", 0);
|
|
||||||
this.externalStats.set("sdPct", 0);
|
|
||||||
this.externalStats.set("damageBonus", [0, 0, 0, 0, 0]);
|
|
||||||
this.externalStats.set("defBonus",[0, 0, 0, 0, 0]);
|
|
||||||
this.externalStats.set("poisonPct", 0);
|
|
||||||
this.statMap = statMap;
|
this.statMap = statMap;
|
||||||
|
|
||||||
this.aggregateStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
aggregateStats() {
|
|
||||||
let statMap = this.statMap;
|
|
||||||
statMap.set("damageRaw", [this.weapon.get("nDam"), this.weapon.get("eDam"), this.weapon.get("tDam"), this.weapon.get("wDam"), this.weapon.get("fDam"), this.weapon.get("aDam")]);
|
|
||||||
statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]);
|
|
||||||
statMap.set("defRaw", [statMap.get("eDef"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]);
|
|
||||||
statMap.set("defBonus", [statMap.get("eDefPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]);
|
|
||||||
statMap.set("defMult", classDefenseMultipliers.get(this.weapon.get("type")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
550
js/build2.js
|
@ -1,550 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20] ]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Error to catch items that don't exist.
|
|
||||||
* @module ItemNotFound
|
|
||||||
*/
|
|
||||||
class ItemNotFound {
|
|
||||||
/**
|
|
||||||
* @class
|
|
||||||
* @param {String} item the item name entered
|
|
||||||
* @param {String} type the type of item
|
|
||||||
* @param {Boolean} genElement whether to generate an element from inputs
|
|
||||||
* @param {String} override override for item type
|
|
||||||
*/
|
|
||||||
constructor(item, type, genElement, override) {
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
this.message = `Cannot find ${override||type} named ${item}`;
|
|
||||||
if (genElement)
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
* @type {Element}
|
|
||||||
*/
|
|
||||||
this.element = document.getElementById(`${type}-choice`).parentElement.querySelectorAll("p.error")[0];
|
|
||||||
else
|
|
||||||
this.element = document.createElement("div");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Error to catch incorrect input.
|
|
||||||
* @module IncorrectInput
|
|
||||||
*/
|
|
||||||
class IncorrectInput {
|
|
||||||
/**
|
|
||||||
* @class
|
|
||||||
* @param {String} input the inputted text
|
|
||||||
* @param {String} format the correct format
|
|
||||||
* @param {String} sibling the id of the error node's sibling
|
|
||||||
*/
|
|
||||||
constructor(input, format, sibling) {
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
this.message = `${input} is incorrect. Example: ${format}`;
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
this.id = sibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Error that inputs an array of items to generate errors of.
|
|
||||||
* @module ListError
|
|
||||||
* @extends Error
|
|
||||||
*/
|
|
||||||
class ListError extends Error {
|
|
||||||
/**
|
|
||||||
* @class
|
|
||||||
* @param {Array} errors array of errors
|
|
||||||
*/
|
|
||||||
constructor(errors) {
|
|
||||||
let ret = [];
|
|
||||||
if (typeof errors[0] == "string") {
|
|
||||||
super(errors[0]);
|
|
||||||
} else {
|
|
||||||
super(errors[0].message);
|
|
||||||
}
|
|
||||||
for (let i of errors) {
|
|
||||||
if (typeof i == "string") {
|
|
||||||
ret.push(new Error(i));
|
|
||||||
} else {
|
|
||||||
ret.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
* @type {Object[]}
|
|
||||||
*/
|
|
||||||
this.errors = ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Class that represents a wynn player's build.
|
|
||||||
*/
|
|
||||||
class Build{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Construct a build.
|
|
||||||
* @param {Number} level : Level of the player.
|
|
||||||
* @param {String[]} equipment : List of equipment names that make up the build.
|
|
||||||
* In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon.
|
|
||||||
* @param {Number[]} powders : Powder application. List of lists of integers (powder IDs).
|
|
||||||
* In order: boots, Chestplate, Leggings, Boots, Weapon.
|
|
||||||
* @param {Object[]} inputerrors : List of instances of error-like classes.
|
|
||||||
*/
|
|
||||||
constructor(level,equipment, powders, externalStats, inputerrors=[]){
|
|
||||||
|
|
||||||
let errors = inputerrors;
|
|
||||||
//this contains the Craft objects, if there are any crafted items. this.boots, etc. will contain the statMap of the Craft (which is built to be an expandedItem).
|
|
||||||
this.craftedItems = [];
|
|
||||||
this.customItems = [];
|
|
||||||
// NOTE: powders is just an array of arrays of powder IDs. Not powder objects.
|
|
||||||
this.powders = powders;
|
|
||||||
if(itemMap.get(equipment[0]) && itemMap.get(equipment[0]).type === "helmet") {
|
|
||||||
const helmet = itemMap.get(equipment[0]);
|
|
||||||
this.powders[0] = this.powders[0].slice(0,helmet.slots);
|
|
||||||
this.helmet = expandItem(helmet, this.powders[0]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
//let boots = getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : (getCustomFromHash(equipment[0])? getCustomFromHash(equipment[0]) : undefined);
|
|
||||||
let helmet = getCustomFromHash(equipment[0]) ? getCustomFromHash(equipment[0]) : (getCraftFromHash(equipment[0]) ? getCraftFromHash(equipment[0]) : undefined);
|
|
||||||
if (helmet.statMap.get("type") !== "helmet") {
|
|
||||||
throw new Error("Not a helmet");
|
|
||||||
}
|
|
||||||
this.powders[0] = this.powders[0].slice(0,helmet.statMap.get("slots"));
|
|
||||||
helmet.statMap.set("powders",this.powders[0].slice());
|
|
||||||
helmet.applyPowders();
|
|
||||||
this.helmet = helmet.statMap;
|
|
||||||
if (this.helmet.get("custom")) {
|
|
||||||
this.customItems.push(helmet);
|
|
||||||
} else if (this.helmet.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(helmet);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Error) {
|
|
||||||
//console.log(Error); //fix
|
|
||||||
const helmet = itemMap.get("No Helmet");
|
|
||||||
this.powders[0] = this.powders[0].slice(0,helmet.slots);
|
|
||||||
this.helmet = expandItem(helmet, this.powders[0]);
|
|
||||||
errors.push(new ItemNotFound(equipment[0], "helmet", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[1]) && itemMap.get(equipment[1]).type === "chestplate") {
|
|
||||||
const chestplate = itemMap.get(equipment[1]);
|
|
||||||
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
|
|
||||||
this.chestplate = expandItem(chestplate, this.powders[1]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let chestplate = getCustomFromHash(equipment[1]) ? getCustomFromHash(equipment[1]) : (getCraftFromHash(equipment[1]) ? getCraftFromHash(equipment[1]) : undefined);
|
|
||||||
if (chestplate.statMap.get("type") !== "chestplate") {
|
|
||||||
throw new Error("Not a chestplate");
|
|
||||||
}
|
|
||||||
this.powders[1] = this.powders[1].slice(0,chestplate.statMap.get("slots"));
|
|
||||||
chestplate.statMap.set("powders",this.powders[1].slice());
|
|
||||||
chestplate.applyPowders();
|
|
||||||
this.chestplate = chestplate.statMap;
|
|
||||||
if (this.chestplate.get("custom")) {
|
|
||||||
this.customItems.push(chestplate);
|
|
||||||
} else if (this.chestplate.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(chestplate);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const chestplate = itemMap.get("No Chestplate");
|
|
||||||
this.powders[1] = this.powders[1].slice(0,chestplate.slots);
|
|
||||||
this.chestplate = expandItem(chestplate, this.powders[1]);
|
|
||||||
errors.push(new ItemNotFound(equipment[1], "chestplate", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (itemMap.get(equipment[2]) && itemMap.get(equipment[2]).type === "leggings") {
|
|
||||||
const leggings = itemMap.get(equipment[2]);
|
|
||||||
this.powders[2] = this.powders[2].slice(0,leggings.slots);
|
|
||||||
this.leggings = expandItem(leggings, this.powders[2]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let leggings = getCustomFromHash(equipment[2]) ? getCustomFromHash(equipment[2]) : (getCraftFromHash(equipment[2]) ? getCraftFromHash(equipment[2]) : undefined);
|
|
||||||
if (leggings.statMap.get("type") !== "leggings") {
|
|
||||||
throw new Error("Not a leggings");
|
|
||||||
}
|
|
||||||
this.powders[2] = this.powders[2].slice(0,leggings.statMap.get("slots"));
|
|
||||||
leggings.statMap.set("powders",this.powders[2].slice());
|
|
||||||
leggings.applyPowders();
|
|
||||||
this.leggings = leggings.statMap;
|
|
||||||
if (this.leggings.get("custom")) {
|
|
||||||
this.customItems.push(leggings);
|
|
||||||
} else if (this.leggings.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(leggings);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const leggings = itemMap.get("No Leggings");
|
|
||||||
this.powders[2] = this.powders[2].slice(0,leggings.slots);
|
|
||||||
this.leggings = expandItem(leggings, this.powders[2]);
|
|
||||||
errors.push(new ItemNotFound(equipment[2], "leggings", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (itemMap.get(equipment[3]) && itemMap.get(equipment[3]).type === "boots") {
|
|
||||||
const boots = itemMap.get(equipment[3]);
|
|
||||||
this.powders[3] = this.powders[3].slice(0,boots.slots);
|
|
||||||
this.boots = expandItem(boots, this.powders[3]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let boots = getCustomFromHash(equipment[3]) ? getCustomFromHash(equipment[3]) : (getCraftFromHash(equipment[3]) ? getCraftFromHash(equipment[3]) : undefined);
|
|
||||||
if (boots.statMap.get("type") !== "boots") {
|
|
||||||
throw new Error("Not a boots");
|
|
||||||
}
|
|
||||||
this.powders[3] = this.powders[3].slice(0,boots.statMap.get("slots"));
|
|
||||||
boots.statMap.set("powders",this.powders[3].slice());
|
|
||||||
boots.applyPowders();
|
|
||||||
this.boots = boots.statMap;
|
|
||||||
console.log(boots);
|
|
||||||
if (this.boots.get("custom")) {
|
|
||||||
this.customItems.push(boots);
|
|
||||||
} else if (this.boots.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(boots);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const boots = itemMap.get("No Boots");
|
|
||||||
this.powders[3] = this.powders[3].slice(0,boots.slots);
|
|
||||||
this.boots = expandItem(boots, this.powders[3]);
|
|
||||||
errors.push(new ItemNotFound(equipment[3], "boots", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[4]) && itemMap.get(equipment[4]).type === "ring") {
|
|
||||||
const ring = itemMap.get(equipment[4]);
|
|
||||||
this.ring1 = expandItem(ring, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let ring = getCustomFromHash(equipment[4]) ? getCustomFromHash(equipment[4]) : (getCraftFromHash(equipment[4]) ? getCraftFromHash(equipment[4]) : undefined);
|
|
||||||
if (ring.statMap.get("type") !== "ring") {
|
|
||||||
throw new Error("Not a ring");
|
|
||||||
}
|
|
||||||
this.ring1 = ring.statMap;
|
|
||||||
if (this.ring1.get("custom")) {
|
|
||||||
this.customItems.push(ring);
|
|
||||||
} else if (this.ring1.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(ring);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const ring = itemMap.get("No Ring 1");
|
|
||||||
this.ring1 = expandItem(ring, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[4], "ring1", true, "ring"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[5]) && itemMap.get(equipment[5]).type === "ring") {
|
|
||||||
const ring = itemMap.get(equipment[5]);
|
|
||||||
this.ring2 = expandItem(ring, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let ring = getCustomFromHash(equipment[5]) ? getCustomFromHash(equipment[5]) : (getCraftFromHash(equipment[5]) ? getCraftFromHash(equipment[5]) : undefined);
|
|
||||||
if (ring.statMap.get("type") !== "ring") {
|
|
||||||
throw new Error("Not a ring");
|
|
||||||
}
|
|
||||||
this.ring2 = ring.statMap;
|
|
||||||
if (this.ring2.get("custom")) {
|
|
||||||
this.customItems.push(ring);
|
|
||||||
} else if (this.ring2.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(ring);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const ring = itemMap.get("No Ring 2");
|
|
||||||
this.ring2 = expandItem(ring, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[5], "ring2", true, "ring"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[6]) && itemMap.get(equipment[6]).type === "bracelet") {
|
|
||||||
const bracelet = itemMap.get(equipment[6]);
|
|
||||||
this.bracelet = expandItem(bracelet, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let bracelet = getCustomFromHash(equipment[6]) ? getCustomFromHash(equipment[6]) : (getCraftFromHash(equipment[6]) ? getCraftFromHash(equipment[6]) : undefined);
|
|
||||||
if (bracelet.statMap.get("type") !== "bracelet") {
|
|
||||||
throw new Error("Not a bracelet");
|
|
||||||
}
|
|
||||||
this.bracelet = bracelet.statMap;
|
|
||||||
if (this.bracelet.get("custom")) {
|
|
||||||
this.customItems.push(bracelet);
|
|
||||||
} else if (this.bracelet.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(bracelet);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const bracelet = itemMap.get("No Bracelet");
|
|
||||||
this.bracelet = expandItem(bracelet, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[6], "bracelet", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[7]) && itemMap.get(equipment[7]).type === "necklace") {
|
|
||||||
const necklace = itemMap.get(equipment[7]);
|
|
||||||
this.necklace = expandItem(necklace, []);
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let necklace = getCustomFromHash(equipment[7]) ? getCustomFromHash(equipment[7]) : (getCraftFromHash(equipment[7]) ? getCraftFromHash(equipment[7]) : undefined);
|
|
||||||
if (necklace.statMap.get("type") !== "necklace") {
|
|
||||||
throw new Error("Not a necklace");
|
|
||||||
}
|
|
||||||
this.necklace = necklace.statMap;
|
|
||||||
if (this.necklace.get("custom")) {
|
|
||||||
this.customItems.push(necklace);
|
|
||||||
} else if (this.necklace.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(necklace);
|
|
||||||
}
|
|
||||||
} catch (Error) {
|
|
||||||
const necklace = itemMap.get("No Necklace");
|
|
||||||
this.necklace = expandItem(necklace, []);
|
|
||||||
errors.push(new ItemNotFound(equipment[7], "necklace", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(itemMap.get(equipment[8]) && itemMap.get(equipment[8]).category === "weapon") {
|
|
||||||
const weapon = itemMap.get(equipment[8]);
|
|
||||||
this.powders[4] = this.powders[4].slice(0,weapon.slots);
|
|
||||||
this.weapon = expandItem(weapon, this.powders[4]);
|
|
||||||
if (equipment[8] !== "No Weapon") {
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "grid";
|
|
||||||
} else {
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "none";
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
try {
|
|
||||||
let weapon = getCustomFromHash(equipment[8]) ? getCustomFromHash(equipment[8]) : (getCraftFromHash(equipment[8]) ? getCraftFromHash(equipment[8]) : undefined);
|
|
||||||
if (weapon.statMap.get("category") !== "weapon") {
|
|
||||||
throw new Error("Not a weapon");
|
|
||||||
}
|
|
||||||
this.weapon = weapon.statMap;
|
|
||||||
if (this.weapon.get("custom")) {
|
|
||||||
this.customItems.push(weapon);
|
|
||||||
} else if (this.weapon.get("crafted")) { //customs can also be crafted, but custom takes priority.
|
|
||||||
this.craftedItems.push(weapon);
|
|
||||||
}
|
|
||||||
this.powders[4] = this.powders[4].slice(0,this.weapon.get("slots"));
|
|
||||||
this.weapon.set("powders",this.powders[4].slice());
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "grid";
|
|
||||||
} catch (Error) {
|
|
||||||
const weapon = itemMap.get("No Weapon");
|
|
||||||
this.powders[4] = this.powders[4].slice(0,weapon.slots);
|
|
||||||
this.weapon = expandItem(weapon, this.powders[4]);
|
|
||||||
document.getElementsByClassName("powder-specials")[0].style.display = "none";
|
|
||||||
errors.push(new ItemNotFound(equipment[8], "weapon", true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log(this.craftedItems)
|
|
||||||
|
|
||||||
if (level < 1) { //Should these be constants?
|
|
||||||
this.level = 1;
|
|
||||||
} else if (level > 106) {
|
|
||||||
this.level = 106;
|
|
||||||
} else if (level <= 106 && level >= 1) {
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
document.getElementById("level-choice").value = this.level;
|
|
||||||
|
|
||||||
this.availableSkillpoints = levelToSkillPoints(this.level);
|
|
||||||
this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ];
|
|
||||||
this.items = this.equipment.concat([this.weapon]);
|
|
||||||
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
|
|
||||||
let result = calculate_skillpoints(this.equipment, this.weapon);
|
|
||||||
console.log(result);
|
|
||||||
this.equip_order = result[0];
|
|
||||||
this.base_skillpoints = result[1];
|
|
||||||
this.total_skillpoints = result[2];
|
|
||||||
this.assigned_skillpoints = result[3];
|
|
||||||
this.activeSetCounts = result[4];
|
|
||||||
|
|
||||||
// For strength boosts like warscream, vanish, etc.
|
|
||||||
this.damageMultiplier = 1.0;
|
|
||||||
this.defenseMultiplier = 1.0;
|
|
||||||
|
|
||||||
// For other external boosts ;-;
|
|
||||||
this.externalStats = externalStats;
|
|
||||||
|
|
||||||
this.initBuildStats();
|
|
||||||
|
|
||||||
// Remove every error before adding specific ones
|
|
||||||
for (let i of document.getElementsByClassName("error")) {
|
|
||||||
i.textContent = "";
|
|
||||||
}
|
|
||||||
this.errors = errors;
|
|
||||||
if (errors.length > 0) this.errored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Returns build in string format
|
|
||||||
*/
|
|
||||||
toString(){
|
|
||||||
return [this.equipment,this.weapon].flat();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getters */
|
|
||||||
|
|
||||||
/* Get total health for build.
|
|
||||||
*/
|
|
||||||
|
|
||||||
getSpellCost(spellIdx, cost) {
|
|
||||||
cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2])));
|
|
||||||
cost = Math.max(0, Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100)));
|
|
||||||
return Math.max(1, cost + this.statMap.get("spRaw"+spellIdx));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get melee stats for build.
|
|
||||||
Returns an array in the order:
|
|
||||||
*/
|
|
||||||
getMeleeStats(){
|
|
||||||
const stats = this.statMap;
|
|
||||||
if (this.weapon.get("tier") === "Crafted") {
|
|
||||||
stats.set("damageBases", [this.weapon.get("nDamBaseHigh"),this.weapon.get("eDamBaseHigh"),this.weapon.get("tDamBaseHigh"),this.weapon.get("wDamBaseHigh"),this.weapon.get("fDamBaseHigh"),this.weapon.get("aDamBaseHigh")]);
|
|
||||||
}
|
|
||||||
let adjAtkSpd = attackSpeeds.indexOf(stats.get("atkSpd")) + stats.get("atkTier");
|
|
||||||
if(adjAtkSpd > 6){
|
|
||||||
adjAtkSpd = 6;
|
|
||||||
}else if(adjAtkSpd < 0){
|
|
||||||
adjAtkSpd = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let damage_mult = 1;
|
|
||||||
if (this.weapon.get("type") === "relik") {
|
|
||||||
damage_mult = 0.99; // CURSE YOU WYNNCRAFT
|
|
||||||
//One day we will create WynnWynn and no longer have shaman 99% melee injustice.
|
|
||||||
//In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams.
|
|
||||||
}
|
|
||||||
// 0spellmult for melee damage.
|
|
||||||
let results = calculateSpellDamage(stats, [100, 0, 0, 0, 0, 0], stats.get("mdRaw"), stats.get("mdPct") + this.externalStats.get("mdPct"), 0, this.weapon, this.total_skillpoints, damage_mult * this.damageMultiplier, this.externalStats);
|
|
||||||
|
|
||||||
let dex = this.total_skillpoints[1];
|
|
||||||
|
|
||||||
let totalDamNorm = results[0];
|
|
||||||
let totalDamCrit = results[1];
|
|
||||||
totalDamNorm.push(1-skillPointsToPercentage(dex));
|
|
||||||
totalDamCrit.push(skillPointsToPercentage(dex));
|
|
||||||
let damages_results = results[2];
|
|
||||||
|
|
||||||
let singleHitTotal = ((totalDamNorm[0]+totalDamNorm[1])*(totalDamNorm[2])
|
|
||||||
+(totalDamCrit[0]+totalDamCrit[1])*(totalDamCrit[2]))/2;
|
|
||||||
|
|
||||||
//Now do math
|
|
||||||
let normDPS = (totalDamNorm[0]+totalDamNorm[1])/2 * baseDamageMultiplier[adjAtkSpd];
|
|
||||||
let critDPS = (totalDamCrit[0]+totalDamCrit[1])/2 * baseDamageMultiplier[adjAtkSpd];
|
|
||||||
let avgDPS = (normDPS * (1 - skillPointsToPercentage(dex))) + (critDPS * (skillPointsToPercentage(dex)));
|
|
||||||
//[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit]
|
|
||||||
return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get all defensive stats for this build.
|
|
||||||
*/
|
|
||||||
getDefenseStats(){
|
|
||||||
const stats = this.statMap;
|
|
||||||
let defenseStats = [];
|
|
||||||
let def_pct = skillPointsToPercentage(this.total_skillpoints[3]);
|
|
||||||
let agi_pct = skillPointsToPercentage(this.total_skillpoints[4]);
|
|
||||||
//total hp
|
|
||||||
let totalHp = stats.get("hp") + stats.get("hpBonus");
|
|
||||||
if (totalHp < 5) totalHp = 5;
|
|
||||||
defenseStats.push(totalHp);
|
|
||||||
//EHP
|
|
||||||
let ehp = [totalHp, totalHp];
|
|
||||||
let defMult = classDefenseMultipliers.get(this.weapon.get("type"));
|
|
||||||
ehp[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
ehp[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
defenseStats.push(ehp);
|
|
||||||
//HPR
|
|
||||||
let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.);
|
|
||||||
defenseStats.push(totalHpr);
|
|
||||||
//EHPR
|
|
||||||
let ehpr = [totalHpr, totalHpr];
|
|
||||||
ehpr[0] /= ((1-def_pct)*(1-agi_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
ehpr[1] /= ((1-def_pct)*(2-defMult)*(2-this.defenseMultiplier));
|
|
||||||
defenseStats.push(ehpr);
|
|
||||||
//skp stats
|
|
||||||
defenseStats.push([ (1 - ((1-def_pct) * (2 - this.defenseMultiplier)))*100, agi_pct*100]);
|
|
||||||
//eledefs - TODO POWDERS
|
|
||||||
let eledefs = [0, 0, 0, 0, 0];
|
|
||||||
for(const i in skp_elements){ //kinda jank but ok
|
|
||||||
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.);
|
|
||||||
}
|
|
||||||
defenseStats.push(eledefs);
|
|
||||||
|
|
||||||
//[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]]
|
|
||||||
return defenseStats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get all stats for this build. Stores in this.statMap.
|
|
||||||
@pre The build itself should be valid. No checking of validity of pieces is done here.
|
|
||||||
*/
|
|
||||||
initBuildStats(){
|
|
||||||
|
|
||||||
let staticIDs = ["hp", "eDef", "tDef", "wDef", "fDef", "aDef"];
|
|
||||||
|
|
||||||
//Create a map of this build's stats
|
|
||||||
let statMap = new Map();
|
|
||||||
|
|
||||||
for (const staticID of staticIDs) {
|
|
||||||
statMap.set(staticID, 0);
|
|
||||||
}
|
|
||||||
statMap.set("hp", levelToHPBase(this.level));
|
|
||||||
|
|
||||||
let major_ids = new Set();
|
|
||||||
for (const item of this.items){
|
|
||||||
for (let [id, value] of item.get("maxRolls")) {
|
|
||||||
statMap.set(id,(statMap.get(id) || 0)+value);
|
|
||||||
}
|
|
||||||
for (const staticID of staticIDs) {
|
|
||||||
if (item.get(staticID)) {
|
|
||||||
statMap.set(staticID, statMap.get(staticID) + item.get(staticID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.get("majorIds")) {
|
|
||||||
for (const majorID of item.get("majorIds")) {
|
|
||||||
major_ids.add(majorID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statMap.set("activeMajorIDs", major_ids);
|
|
||||||
for (const [setName, count] of this.activeSetCounts) {
|
|
||||||
const bonus = sets[setName].bonuses[count-1];
|
|
||||||
for (const id in bonus) {
|
|
||||||
if (skp_order.includes(id)) {
|
|
||||||
// pass. Don't include skillpoints in ids
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
statMap.set(id,(statMap.get(id) || 0)+bonus[id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statMap.set("poisonPct", 100);
|
|
||||||
|
|
||||||
// The stuff relevant for damage calculation!!! @ferricles
|
|
||||||
statMap.set("atkSpd", this.weapon.get("atkSpd"));
|
|
||||||
|
|
||||||
for (const x of skp_elements) {
|
|
||||||
this.externalStats.set(x + "DamPct", 0);
|
|
||||||
}
|
|
||||||
this.externalStats.set("mdPct", 0);
|
|
||||||
this.externalStats.set("sdPct", 0);
|
|
||||||
this.externalStats.set("damageBonus", [0, 0, 0, 0, 0]);
|
|
||||||
this.externalStats.set("defBonus",[0, 0, 0, 0, 0]);
|
|
||||||
this.externalStats.set("poisonPct", 0);
|
|
||||||
this.statMap = statMap;
|
|
||||||
|
|
||||||
this.aggregateStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
aggregateStats() {
|
|
||||||
let statMap = this.statMap;
|
|
||||||
statMap.set("damageRaw", [this.weapon.get("nDam"), this.weapon.get("eDam"), this.weapon.get("tDam"), this.weapon.get("wDam"), this.weapon.get("fDam"), this.weapon.get("aDam")]);
|
|
||||||
statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]);
|
|
||||||
statMap.set("defRaw", [statMap.get("eDef"), statMap.get("tDef"), statMap.get("wDef"), statMap.get("fDef"), statMap.get("aDef")]);
|
|
||||||
statMap.set("defBonus", [statMap.get("eDefPct"), statMap.get("tDefPct"), statMap.get("wDefPct"), statMap.get("fDefPct"), statMap.get("aDefPct")]);
|
|
||||||
statMap.set("defMult", classDefenseMultipliers.get(this.weapon.get("type")));
|
|
||||||
}
|
|
||||||
}
|
|
107
js/build_constants.js
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/**
|
||||||
|
* I kinda lied. Theres some listener stuff in here
|
||||||
|
* but its mostly constants for builder page specifically.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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",
|
||||||
|
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
|
||||||
|
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
|
||||||
|
"hprRaw", "hprPct", "hpBonus", "atkTier",
|
||||||
|
"spPct1", "spRaw1", "spPct2", "spRaw2",
|
||||||
|
"spPct3", "spRaw3", "spPct4", "spRaw4" ];
|
||||||
|
|
||||||
|
let editable_elems = [];
|
||||||
|
|
||||||
|
for (let i of editable_item_fields) {
|
||||||
|
let elem = document.getElementById(i);
|
||||||
|
elem.addEventListener("change", (event) => {
|
||||||
|
elem.classList.add("highlight");
|
||||||
|
});
|
||||||
|
editable_elems.push(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i of skp_order) {
|
||||||
|
let elem = document.getElementById(i+"-skp");
|
||||||
|
elem.addEventListener("change", (event) => {
|
||||||
|
elem.classList.add("highlight");
|
||||||
|
});
|
||||||
|
editable_elems.push(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_highlights() {
|
||||||
|
for (let i of editable_elems) {
|
||||||
|
i.classList.remove("highlight");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let equipment_fields = [
|
||||||
|
"helmet",
|
||||||
|
"chestplate",
|
||||||
|
"leggings",
|
||||||
|
"boots",
|
||||||
|
"ring1",
|
||||||
|
"ring2",
|
||||||
|
"bracelet",
|
||||||
|
"necklace",
|
||||||
|
"weapon"
|
||||||
|
];
|
||||||
|
let tome_fields = [
|
||||||
|
"weaponTome1",
|
||||||
|
"weaponTome2",
|
||||||
|
"armorTome1",
|
||||||
|
"armorTome2",
|
||||||
|
"armorTome3",
|
||||||
|
"armorTome4",
|
||||||
|
"guildTome1",
|
||||||
|
]
|
||||||
|
let equipment_names = [
|
||||||
|
"Helmet",
|
||||||
|
"Chestplate",
|
||||||
|
"Leggings",
|
||||||
|
"Boots",
|
||||||
|
"Ring 1",
|
||||||
|
"Ring 2",
|
||||||
|
"Bracelet",
|
||||||
|
"Necklace",
|
||||||
|
"Weapon"
|
||||||
|
];
|
||||||
|
|
||||||
|
let tome_names = [
|
||||||
|
"Weapon Tome",
|
||||||
|
"Weapon Tome",
|
||||||
|
"Armor Tome",
|
||||||
|
"Armor Tome",
|
||||||
|
"Armor Tome",
|
||||||
|
"Armor Tome",
|
||||||
|
"Guild Tome",
|
||||||
|
]
|
||||||
|
let equipment_inputs = equipment_fields.map(x => x + "-choice");
|
||||||
|
let build_fields = equipment_fields.map(x => x+"-tooltip");
|
||||||
|
let tomeInputs = tome_fields.map(x => x + "-choice");
|
||||||
|
|
||||||
|
let powder_inputs = [
|
||||||
|
"helmet-powder",
|
||||||
|
"chestplate-powder",
|
||||||
|
"leggings-powder",
|
||||||
|
"boots-powder",
|
||||||
|
"weapon-powder",
|
||||||
|
];
|
||||||
|
|
||||||
|
let weapon_keys = ['dagger', 'wand', 'bow', 'relik', 'spear'];
|
||||||
|
let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots'];
|
||||||
|
let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace'];
|
||||||
|
let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon'];
|
||||||
|
let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'];
|
||||||
|
let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1'];
|
||||||
|
|
||||||
|
let spell_disp = ['build-melee-stats', 'spell0-info', 'spell1-info', 'spell2-info', 'spell3-info'];
|
||||||
|
let other_disp = ['build-order', 'set-info', 'int-info'];
|
290
js/build_encode_decode.js
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
function parsePowdering(powder_info) {
|
||||||
|
// TODO: Make this run in linear instead of quadratic time... ew
|
||||||
|
let powdering = [];
|
||||||
|
for (let i = 0; i < 5; ++i) {
|
||||||
|
let powders = "";
|
||||||
|
let n_blocks = Base64.toInt(powder_info.charAt(0));
|
||||||
|
// console.log(n_blocks + " blocks");
|
||||||
|
powder_info = powder_info.slice(1);
|
||||||
|
for (let j = 0; j < n_blocks; ++j) {
|
||||||
|
let block = powder_info.slice(0,5);
|
||||||
|
let six_powders = Base64.toInt(block);
|
||||||
|
for (let k = 0; k < 6 && six_powders != 0; ++k) {
|
||||||
|
powders += powderNames.get((six_powders & 0x1f) - 1);
|
||||||
|
six_powders >>>= 5;
|
||||||
|
}
|
||||||
|
powder_info = powder_info.slice(5);
|
||||||
|
}
|
||||||
|
powdering[i] = powders;
|
||||||
|
}
|
||||||
|
return [powdering, powder_info];
|
||||||
|
}
|
||||||
|
|
||||||
|
let atree_data = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate fields based on url, and calculate build.
|
||||||
|
*/
|
||||||
|
function decodeBuild(url_tag) {
|
||||||
|
if (url_tag) {
|
||||||
|
//default values
|
||||||
|
let equipment = [null, null, null, null, null, null, null, null, null];
|
||||||
|
let tomes = [null, null, null, null, null, null, null];
|
||||||
|
let powdering = ["", "", "", "", ""];
|
||||||
|
let info = url_tag.split("_");
|
||||||
|
let version = info[0];
|
||||||
|
let save_skp = false;
|
||||||
|
let skillpoints = [0, 0, 0, 0, 0];
|
||||||
|
let level = 106;
|
||||||
|
|
||||||
|
let version_number = parseInt(version)
|
||||||
|
//equipment (items)
|
||||||
|
// TODO: use filters
|
||||||
|
if (version_number < 4) {
|
||||||
|
let equipments = info[1];
|
||||||
|
for (let i = 0; i < 9; ++i ) {
|
||||||
|
let equipment_str = equipments.slice(i*3,i*3+3);
|
||||||
|
equipment[i] = getItemNameFromID(Base64.toInt(equipment_str));
|
||||||
|
}
|
||||||
|
info[1] = equipments.slice(27);
|
||||||
|
}
|
||||||
|
else if (version_number == 4) {
|
||||||
|
let info_str = info[1];
|
||||||
|
let start_idx = 0;
|
||||||
|
for (let i = 0; i < 9; ++i ) {
|
||||||
|
if (info_str.charAt(start_idx) === "-") {
|
||||||
|
equipment[i] = "CR-"+info_str.slice(start_idx+1, start_idx+18);
|
||||||
|
start_idx += 18;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let equipment_str = info_str.slice(start_idx, start_idx+3);
|
||||||
|
equipment[i] = getItemNameFromID(Base64.toInt(equipment_str));
|
||||||
|
start_idx += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info[1] = info_str.slice(start_idx);
|
||||||
|
}
|
||||||
|
else if (version_number <= 7) {
|
||||||
|
let info_str = info[1];
|
||||||
|
let start_idx = 0;
|
||||||
|
for (let i = 0; i < 9; ++i ) {
|
||||||
|
if (info_str.slice(start_idx,start_idx+3) === "CR-") {
|
||||||
|
equipment[i] = info_str.slice(start_idx, start_idx+20);
|
||||||
|
start_idx += 20;
|
||||||
|
} else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") {
|
||||||
|
let len = Base64.toInt(info_str.slice(start_idx,start_idx+3));
|
||||||
|
equipment[i] = info_str.slice(start_idx+3,start_idx+3+len);
|
||||||
|
start_idx += (3+len);
|
||||||
|
} else {
|
||||||
|
let equipment_str = info_str.slice(start_idx, start_idx+3);
|
||||||
|
equipment[i] = getItemNameFromID(Base64.toInt(equipment_str));
|
||||||
|
start_idx += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info[1] = info_str.slice(start_idx);
|
||||||
|
}
|
||||||
|
//constant in all versions
|
||||||
|
for (let i in equipment) {
|
||||||
|
setValue(equipment_inputs[i], equipment[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//level, skill point assignments, and powdering
|
||||||
|
if (version_number == 1) {
|
||||||
|
let powder_info = info[1];
|
||||||
|
let res = parsePowdering(powder_info);
|
||||||
|
powdering = res[0];
|
||||||
|
} else if (version_number == 2) {
|
||||||
|
save_skp = true;
|
||||||
|
let skillpoint_info = info[1].slice(0, 10);
|
||||||
|
for (let i = 0; i < 5; ++i ) {
|
||||||
|
skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2));
|
||||||
|
}
|
||||||
|
|
||||||
|
let powder_info = info[1].slice(10);
|
||||||
|
let res = parsePowdering(powder_info);
|
||||||
|
powdering = res[0];
|
||||||
|
} else if (version_number <= 7){
|
||||||
|
level = Base64.toInt(info[1].slice(10,12));
|
||||||
|
setValue("level-choice",level);
|
||||||
|
save_skp = true;
|
||||||
|
let skillpoint_info = info[1].slice(0, 10);
|
||||||
|
for (let i = 0; i < 5; ++i ) {
|
||||||
|
skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2));
|
||||||
|
}
|
||||||
|
|
||||||
|
let powder_info = info[1].slice(12);
|
||||||
|
|
||||||
|
let res = parsePowdering(powder_info);
|
||||||
|
powdering = res[0];
|
||||||
|
info[1] = res[1];
|
||||||
|
}
|
||||||
|
// Tomes.
|
||||||
|
if (version >= 6) {
|
||||||
|
//tome values do not appear in anything before v6.
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version >= 7) {
|
||||||
|
// ugly af. only works since its the last thing. will be fixed with binary decode
|
||||||
|
atree_data = new BitVector(info[1]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
atree_data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i in powder_inputs) {
|
||||||
|
setValue(powder_inputs[i], powdering[i]);
|
||||||
|
}
|
||||||
|
for (let i in skillpoints) {
|
||||||
|
setValue(skp_order[i] + "-skp", skillpoints[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stores the entire build in a string using B64 encoding and adds it to the URL.
|
||||||
|
*/
|
||||||
|
function encodeBuild(build, powders, skillpoints, atree, atree_state) {
|
||||||
|
|
||||||
|
if (build) {
|
||||||
|
let build_string;
|
||||||
|
|
||||||
|
//V6 encoding - Tomes
|
||||||
|
//V7 encoding - ATree
|
||||||
|
build_version = 5;
|
||||||
|
build_string = "";
|
||||||
|
tome_string = "";
|
||||||
|
|
||||||
|
for (const item of build.items) {
|
||||||
|
if (item.statMap.get("custom")) {
|
||||||
|
let custom = "CI-"+encodeCustom(item, true);
|
||||||
|
build_string += Base64.fromIntN(custom.length, 3) + custom;
|
||||||
|
build_version = Math.max(build_version, 5);
|
||||||
|
} else if (item.statMap.get("crafted")) {
|
||||||
|
build_string += "CR-"+encodeCraft(item);
|
||||||
|
} else if (item.statMap.get("category") === "tome") {
|
||||||
|
let tome_id = item.statMap.get("id");
|
||||||
|
if (tome_id <= 60) {
|
||||||
|
// valid normal tome. ID 61-63 is for NONE tomes.
|
||||||
|
build_version = Math.max(build_version, 6);
|
||||||
|
}
|
||||||
|
tome_string += Base64.fromIntN(tome_id, 1);
|
||||||
|
} else {
|
||||||
|
build_string += Base64.fromIntN(item.statMap.get("id"), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const skp of skillpoints) {
|
||||||
|
build_string += Base64.fromIntN(skp, 2); // Maximum skillpoints: 2048
|
||||||
|
}
|
||||||
|
build_string += Base64.fromIntN(build.level, 2);
|
||||||
|
for (const _powderset of powders) {
|
||||||
|
let n_bits = Math.ceil(_powderset.length / 6);
|
||||||
|
build_string += Base64.fromIntN(n_bits, 1); // Hard cap of 378 powders.
|
||||||
|
// Slice copy.
|
||||||
|
let powderset = _powderset.slice();
|
||||||
|
while (powderset.length != 0) {
|
||||||
|
let firstSix = powderset.slice(0,6).reverse();
|
||||||
|
let powder_hash = 0;
|
||||||
|
for (const powder of firstSix) {
|
||||||
|
powder_hash = (powder_hash << 5) + 1 + powder; // LSB will be extracted first.
|
||||||
|
}
|
||||||
|
build_string += Base64.fromIntN(powder_hash, 5);
|
||||||
|
powderset = powderset.slice(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
build_string += tome_string;
|
||||||
|
|
||||||
|
if (atree.length > 0 && atree_state.get(atree[0].ability.id).active) {
|
||||||
|
build_version = Math.max(build_version, 7);
|
||||||
|
const bitvec = encode_atree(atree, atree_state);
|
||||||
|
build_string += bitvec.toB64();
|
||||||
|
}
|
||||||
|
|
||||||
|
return build_version.toString() + "_" + build_string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyBuild() {
|
||||||
|
copyTextToClipboard(url_base+location.hash);
|
||||||
|
document.getElementById("copy-button").textContent = "Copied!";
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareBuild(build) {
|
||||||
|
if (build) {
|
||||||
|
let text = url_base+location.hash+"\n"+
|
||||||
|
"WynnBuilder build:\n"+
|
||||||
|
"> "+build.items[0].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[1].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[2].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[3].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[4].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[5].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[6].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[7].statMap.get("displayName")+"\n"+
|
||||||
|
"> "+build.items[15].statMap.get("displayName")+" ["+build_powders[4].map(x => powderNames.get(x)).join("")+"]";
|
||||||
|
copyTextToClipboard(text);
|
||||||
|
document.getElementById("share-button").textContent = "Copied!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability tree encode and decode functions
|
||||||
|
*
|
||||||
|
* Based on a traversal, basically only uses bits to represent the nodes that are on (and "dark" outgoing edges).
|
||||||
|
* credit: SockMower
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return: BitVector
|
||||||
|
*/
|
||||||
|
function encode_atree(atree, atree_state) {
|
||||||
|
let ret_vec = new BitVector(0, 0);
|
||||||
|
|
||||||
|
function traverse(head, atree_state, visited, ret) {
|
||||||
|
for (const child of head.children) {
|
||||||
|
if (visited.has(child.ability.id)) { continue; }
|
||||||
|
visited.set(child.ability.id, true);
|
||||||
|
if (atree_state.get(child.ability.id).active) {
|
||||||
|
ret.append(1, 1);
|
||||||
|
traverse(child, atree_state, visited, ret);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret.append(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(atree[0], atree_state, new Map(), ret_vec);
|
||||||
|
return ret_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return: List of active nodes
|
||||||
|
*/
|
||||||
|
function decode_atree(atree, bits) {
|
||||||
|
let i = 0;
|
||||||
|
let ret = [];
|
||||||
|
ret.push(atree[0]);
|
||||||
|
function traverse(head, visited, ret) {
|
||||||
|
for (const child of head.children) {
|
||||||
|
if (visited.has(child.ability.id)) { continue; }
|
||||||
|
visited.set(child.ability.id, true);
|
||||||
|
if (bits.read_bit(i)) {
|
||||||
|
i += 1;
|
||||||
|
ret.push(child);
|
||||||
|
traverse(child, visited, ret);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(atree[0], new Map(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -15,6 +15,11 @@ function skillPointsToPercentage(skp){
|
||||||
//return clamp((-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771), 0.00, 0.808);
|
//return clamp((-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771), 0.00, 0.808);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WYNN2: Skillpoint max scaling. Intel is cost reduction
|
||||||
|
const skillpoint_final_mult = [1, 1, 0.5, 0.867, 0.951];
|
||||||
|
// intel damage and water%
|
||||||
|
const skillpoint_damage_mult = [1, 1, 1, 0.867, 0.951];
|
||||||
|
|
||||||
/*Turns the input amount of levels into skillpoints available.
|
/*Turns the input amount of levels into skillpoints available.
|
||||||
*
|
*
|
||||||
* @param level - the integer level count to be converted
|
* @param level - the integer level count to be converted
|
||||||
|
@ -51,29 +56,247 @@ const armorTypes = [ "helmet", "chestplate", "leggings", "boots" ];
|
||||||
const accessoryTypes = [ "ring", "bracelet", "necklace" ];
|
const accessoryTypes = [ "ring", "bracelet", "necklace" ];
|
||||||
const weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
|
const weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
|
||||||
const consumableTypes = [ "potion", "scroll", "food"];
|
const consumableTypes = [ "potion", "scroll", "food"];
|
||||||
const tomeTypes = ["armorTome", "weaponTome", "guildTome", "dungeonTome", "gatheringTome", "slayingTome"]
|
const tome_types = ['weaponTome', 'armorTome', 'guildTome'];
|
||||||
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
|
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
|
||||||
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
|
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
|
||||||
//0.51, 0.82, 1.50, 2.05, 2.50, 3.11, 4.27
|
//0.51, 0.82, 1.50, 2.05, 2.50, 3.11, 4.27
|
||||||
const classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"];
|
const classes = ["Warrior", "Assassin", "Mage", "Archer", "Shaman"];
|
||||||
|
const wep_to_class = new Map([["dagger", "Assassin"], ["spear", "Warrior"], ["wand", "Mage"], ["bow", "Archer"], ["relik", "Shaman"]])
|
||||||
const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"] //I'm not sure why you would make a custom crafted but if you do you should be able to use it w/ the correct powder formula
|
const tiers = ["Normal", "Unique", "Rare", "Legendary", "Fabled", "Mythic", "Set", "Crafted"] //I'm not sure why you would make a custom crafted but if you do you should be able to use it w/ the correct powder formula
|
||||||
const types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tomeTypes).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
|
const all_types = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(consumableTypes).concat(tome_types).map(x => x.substring(0,1).toUpperCase() + x.substring(1));
|
||||||
//weaponTypes.push("sword");
|
//weaponTypes.push("sword");
|
||||||
//console.log(types)
|
//console.log(types)
|
||||||
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tomeTypes);
|
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes).concat(tome_types);
|
||||||
|
|
||||||
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
|
let elementIcons = ["\u2724","\u2726", "\u2749", "\u2739", "\u274b" ];
|
||||||
let skpReqs = skp_order.map(x => x + "Req");
|
let skpReqs = skp_order.map(x => x + "Req");
|
||||||
|
|
||||||
let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id", "majorIds", "dmgMobs", "defMobs"];
|
let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rSdRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id", "majorIds", "damMobs", "defMobs",
|
||||||
|
|
||||||
|
// wynn2 damages.
|
||||||
|
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct"*/,"eDamRaw","eDamAddMin","eDamAddMax",
|
||||||
|
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct"*/,"tDamRaw","tDamAddMin","tDamAddMax",
|
||||||
|
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct"*/,"wDamRaw","wDamAddMin","wDamAddMax",
|
||||||
|
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct"*/,"fDamRaw","fDamAddMin","fDamAddMax",
|
||||||
|
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct"*/,"aDamRaw","aDamAddMin","aDamAddMax",
|
||||||
|
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
|
||||||
|
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
|
||||||
|
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
|
||||||
|
"critDamPct"
|
||||||
|
];
|
||||||
|
// Extra fake IDs (reserved for use in spell damage calculation) : damageMultiplier, defMultiplier, poisonPct, activeMajorIDs
|
||||||
let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ]
|
let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ]
|
||||||
|
|
||||||
//File reading for ID translations for JSON purposes
|
//File reading for ID translations for JSON purposes
|
||||||
let reversetranslations = new Map();
|
let reversetranslations = new Map();
|
||||||
let translations = new Map([["name", "name"], ["displayName", "displayName"], ["tier", "tier"], ["set", "set"], ["sockets", "slots"], ["type", "type"], ["dropType", "drop"], ["quest", "quest"], ["restrictions", "restrict"], ["damage", "nDam"], ["fireDamage", "fDam"], ["waterDamage", "wDam"], ["airDamage", "aDam"], ["thunderDamage", "tDam"], ["earthDamage", "eDam"], ["attackSpeed", "atkSpd"], ["health", "hp"], ["fireDefense", "fDef"], ["waterDefense", "wDef"], ["airDefense", "aDef"], ["thunderDefense", "tDef"], ["earthDefense", "eDef"], ["level", "lvl"], ["classRequirement", "classReq"], ["strength", "strReq"], ["dexterity", "dexReq"], ["intelligence", "intReq"], ["agility", "agiReq"], ["defense", "defReq"], ["healthRegen", "hprPct"], ["manaRegen", "mr"], ["spellDamage", "sdPct"], ["damageBonus", "mdPct"], ["lifeSteal", "ls"], ["manaSteal", "ms"], ["xpBonus", "xpb"], ["lootBonus", "lb"], ["reflection", "ref"], ["strengthPoints", "str"], ["dexterityPoints", "dex"], ["intelligencePoints", "int"], ["agilityPoints", "agi"], ["defensePoints", "def"], ["thorns", "thorns"], ["exploding", "expd"], ["speed", "spd"], ["attackSpeedBonus", "atkTier"], ["poison", "poison"], ["healthBonus", "hpBonus"], ["soulPoints", "spRegen"], ["emeraldStealing", "eSteal"], ["healthRegenRaw", "hprRaw"], ["spellDamageRaw", "sdRaw"], ["damageBonusRaw", "mdRaw"], ["bonusFireDamage", "fDamPct"], ["bonusWaterDamage", "wDamPct"], ["bonusAirDamage", "aDamPct"], ["bonusThunderDamage", "tDamPct"], ["bonusEarthDamage", "eDamPct"], ["bonusFireDefense", "fDefPct"], ["bonusWaterDefense", "wDefPct"], ["bonusAirDefense", "aDefPct"], ["bonusThunderDefense", "tDefPct"], ["bonusEarthDefense", "eDefPct"], ["type", "type"], ["identified", "fixID"], ["skin", "skin"], ["category", "category"], ["spellCostPct1", "spPct1"], ["spellCostRaw1", "spRaw1"], ["spellCostPct2", "spPct2"], ["spellCostRaw2", "spRaw2"], ["spellCostPct3", "spPct3"], ["spellCostRaw3", "spRaw3"], ["spellCostPct4", "spPct4"], ["spellCostRaw4", "spRaw4"], ["rainbowSpellDamageRaw", "rainbowRaw"], ["sprint", "sprint"], ["sprintRegen", "sprintReg"], ["jumpHeight", "jh"], ["lootQuality", "lq"], ["gatherXpBonus", "gXp"], ["gatherSpeed", "gSpd"]]);
|
let translations = new Map([["name", "name"], ["displayName", "displayName"], ["tier", "tier"], ["set", "set"], ["sockets", "slots"], ["type", "type"], ["dropType", "drop"], ["quest", "quest"], ["restrictions", "restrict"], ["damage", "nDam"], ["fireDamage", "fDam"], ["waterDamage", "wDam"], ["airDamage", "aDam"], ["thunderDamage", "tDam"], ["earthDamage", "eDam"], ["attackSpeed", "atkSpd"], ["health", "hp"], ["fireDefense", "fDef"], ["waterDefense", "wDef"], ["airDefense", "aDef"], ["thunderDefense", "tDef"], ["earthDefense", "eDef"], ["level", "lvl"], ["classRequirement", "classReq"], ["strength", "strReq"], ["dexterity", "dexReq"], ["intelligence", "intReq"], ["agility", "agiReq"], ["defense", "defReq"], ["healthRegen", "hprPct"], ["manaRegen", "mr"], ["spellDamage", "sdPct"], ["damageBonus", "mdPct"], ["lifeSteal", "ls"], ["manaSteal", "ms"], ["xpBonus", "xpb"], ["lootBonus", "lb"], ["reflection", "ref"], ["strengthPoints", "str"], ["dexterityPoints", "dex"], ["intelligencePoints", "int"], ["agilityPoints", "agi"], ["defensePoints", "def"], ["thorns", "thorns"], ["exploding", "expd"], ["speed", "spd"], ["attackSpeedBonus", "atkTier"], ["poison", "poison"], ["healthBonus", "hpBonus"], ["soulPoints", "spRegen"], ["emeraldStealing", "eSteal"], ["healthRegenRaw", "hprRaw"], ["spellDamageRaw", "sdRaw"], ["damageBonusRaw", "mdRaw"], ["bonusFireDamage", "fDamPct"], ["bonusWaterDamage", "wDamPct"], ["bonusAirDamage", "aDamPct"], ["bonusThunderDamage", "tDamPct"], ["bonusEarthDamage", "eDamPct"], ["bonusFireDefense", "fDefPct"], ["bonusWaterDefense", "wDefPct"], ["bonusAirDefense", "aDefPct"], ["bonusThunderDefense", "tDefPct"], ["bonusEarthDefense", "eDefPct"], ["type", "type"], ["identified", "fixID"], ["skin", "skin"], ["category", "category"], ["spellCostPct1", "spPct1"], ["spellCostRaw1", "spRaw1"], ["spellCostPct2", "spPct2"], ["spellCostRaw2", "spRaw2"], ["spellCostPct3", "spPct3"], ["spellCostRaw3", "spRaw3"], ["spellCostPct4", "spPct4"], ["spellCostRaw4", "spRaw4"], ["rainbowSpellDamageRaw", "rSdRaw"], ["sprint", "sprint"], ["sprintRegen", "sprintReg"], ["jumpHeight", "jh"], ["lootQuality", "lq"], ["gatherXpBonus", "gXp"], ["gatherSpeed", "gSpd"]]);
|
||||||
//does not include dmgMobs (wep tomes) and defMobs (armor tomes)
|
//does not include damMobs (wep tomes) and defMobs (armor tomes)
|
||||||
for (const [k, v] of translations) {
|
for (const [k, v] of translations) {
|
||||||
reversetranslations.set(v, k);
|
reversetranslations.set(v, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(translations);
|
let nonRolledIDs = [
|
||||||
|
"name",
|
||||||
|
"lore",
|
||||||
|
"displayName",
|
||||||
|
"tier",
|
||||||
|
"set",
|
||||||
|
"slots",
|
||||||
|
"type",
|
||||||
|
"material",
|
||||||
|
"drop",
|
||||||
|
"quest",
|
||||||
|
"restrict",
|
||||||
|
"nDam", "fDam", "wDam", "aDam", "tDam", "eDam",
|
||||||
|
"atkSpd",
|
||||||
|
"hp",
|
||||||
|
"fDef", "wDef", "aDef", "tDef", "eDef",
|
||||||
|
"lvl",
|
||||||
|
"classReq",
|
||||||
|
"strReq", "dexReq", "intReq", "defReq", "agiReq",
|
||||||
|
"str", "dex", "int", "agi", "def",
|
||||||
|
"fixID",
|
||||||
|
"category",
|
||||||
|
"id",
|
||||||
|
"skillpoints",
|
||||||
|
"reqs",
|
||||||
|
"nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_",
|
||||||
|
"majorIds",
|
||||||
|
"damMobs",
|
||||||
|
"defMobs",
|
||||||
|
// wynn2 damages.
|
||||||
|
"eDamAddMin","eDamAddMax",
|
||||||
|
"tDamAddMin","tDamAddMax",
|
||||||
|
"wDamAddMin","wDamAddMax",
|
||||||
|
"fDamAddMin","fDamAddMax",
|
||||||
|
"aDamAddMin","aDamAddMax",
|
||||||
|
"nDamAddMin","nDamAddMax", // neutral which is now an element
|
||||||
|
"damAddMin","damAddMax", // all
|
||||||
|
"rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral).
|
||||||
|
];
|
||||||
|
let rolledIDs = [
|
||||||
|
"hprPct",
|
||||||
|
"mr",
|
||||||
|
"sdPct",
|
||||||
|
"mdPct",
|
||||||
|
"ls",
|
||||||
|
"ms",
|
||||||
|
"xpb",
|
||||||
|
"lb",
|
||||||
|
"ref",
|
||||||
|
"thorns",
|
||||||
|
"expd",
|
||||||
|
"spd",
|
||||||
|
"atkTier",
|
||||||
|
"poison",
|
||||||
|
"hpBonus",
|
||||||
|
"spRegen",
|
||||||
|
"eSteal",
|
||||||
|
"hprRaw",
|
||||||
|
"sdRaw",
|
||||||
|
"mdRaw",
|
||||||
|
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
|
||||||
|
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
|
||||||
|
"spPct1", "spRaw1",
|
||||||
|
"spPct2", "spRaw2",
|
||||||
|
"spPct3", "spRaw3",
|
||||||
|
"spPct4", "spRaw4",
|
||||||
|
"pDamRaw",
|
||||||
|
"sprint",
|
||||||
|
"sprintReg",
|
||||||
|
"jh",
|
||||||
|
"lq",
|
||||||
|
"gXp",
|
||||||
|
"gSpd",
|
||||||
|
// wynn2 damages.
|
||||||
|
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct"*/,"eDamRaw","eDamAddMin","eDamAddMax",
|
||||||
|
"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct"*/,"tDamRaw","tDamAddMin","tDamAddMax",
|
||||||
|
"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct"*/,"wDamRaw","wDamAddMin","wDamAddMax",
|
||||||
|
"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct"*/,"fDamRaw","fDamAddMin","fDamAddMax",
|
||||||
|
"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct"*/,"aDamRaw","aDamAddMin","aDamAddMax",
|
||||||
|
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
|
||||||
|
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
|
||||||
|
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
|
||||||
|
];
|
||||||
|
let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take an item with id list and turn it into a set of minrolls and maxrolls.
|
||||||
|
*/
|
||||||
|
function expandItem(item) {
|
||||||
|
let minRolls = new Map();
|
||||||
|
let maxRolls = new Map();
|
||||||
|
let expandedItem = new Map();
|
||||||
|
if (item.fixID) { //The item has fixed IDs.
|
||||||
|
expandedItem.set("fixID",true);
|
||||||
|
for (const id of rolledIDs) { //all rolled IDs are numerical
|
||||||
|
let val = (item[id] || 0);
|
||||||
|
minRolls.set(id,val);
|
||||||
|
maxRolls.set(id,val);
|
||||||
|
}
|
||||||
|
} else { //The item does not have fixed IDs.
|
||||||
|
for (const id of rolledIDs) {
|
||||||
|
let val = (item[id] || 0);
|
||||||
|
if (val > 0) { // positive rolled IDs
|
||||||
|
if (reversedIDs.includes(id)) {
|
||||||
|
maxRolls.set(id,idRound(val*0.3));
|
||||||
|
minRolls.set(id,idRound(val*1.3));
|
||||||
|
} else {
|
||||||
|
maxRolls.set(id,idRound(val*1.3));
|
||||||
|
minRolls.set(id,idRound(val*0.3));
|
||||||
|
}
|
||||||
|
} else if (val < 0) { //negative rolled IDs
|
||||||
|
if (reversedIDs.includes(id)) {
|
||||||
|
maxRolls.set(id,idRound(val*1.3));
|
||||||
|
minRolls.set(id,idRound(val*0.7));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
maxRolls.set(id,idRound(val*0.7));
|
||||||
|
minRolls.set(id,idRound(val*1.3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // if val == 0
|
||||||
|
// NOTE: DO NOT remove this case! idRound behavior does not round to 0!
|
||||||
|
maxRolls.set(id,0);
|
||||||
|
minRolls.set(id,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const id of nonRolledIDs) {
|
||||||
|
expandedItem.set(id,item[id]);
|
||||||
|
}
|
||||||
|
expandedItem.set("minRolls",minRolls);
|
||||||
|
expandedItem.set("maxRolls",maxRolls);
|
||||||
|
return expandedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Item {
|
||||||
|
constructor(item_obj) {
|
||||||
|
this.statMap = expandItem(item_obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes in an ingredient object and returns an equivalent Map().
|
||||||
|
*/
|
||||||
|
function expandIngredient(ing) {
|
||||||
|
let expandedIng = new Map();
|
||||||
|
let mapIds = ['consumableIDs', 'itemIDs', 'posMods'];
|
||||||
|
for (const id of mapIds) {
|
||||||
|
let idMap = new Map();
|
||||||
|
for (const key of Object.keys(ing[id])) {
|
||||||
|
idMap.set(key, ing[id][key]);
|
||||||
|
}
|
||||||
|
expandedIng.set(id, idMap);
|
||||||
|
}
|
||||||
|
let normIds = ['lvl','name', 'displayName','tier','skills','id'];
|
||||||
|
for (const id of normIds) {
|
||||||
|
expandedIng.set(id, ing[id]);
|
||||||
|
}
|
||||||
|
if (ing['isPowder']) {
|
||||||
|
expandedIng.set("isPowder",ing['isPowder']);
|
||||||
|
expandedIng.set("pid",ing['pid']);
|
||||||
|
}
|
||||||
|
//now the actually hard one
|
||||||
|
let idMap = new Map();
|
||||||
|
idMap.set("minRolls", new Map());
|
||||||
|
idMap.set("maxRolls", new Map());
|
||||||
|
for (const field of ingFields) {
|
||||||
|
let val = (ing['ids'][field] || 0);
|
||||||
|
idMap.get("minRolls").set(field, val['minimum']);
|
||||||
|
idMap.get("maxRolls").set(field, val['maximum']);
|
||||||
|
}
|
||||||
|
expandedIng.set("ids",idMap);
|
||||||
|
return expandedIng;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes in a recipe object and returns an equivalent Map().
|
||||||
|
*/
|
||||||
|
function expandRecipe(recipe) {
|
||||||
|
let expandedRecipe = new Map();
|
||||||
|
let normIDs = ["name", "skill", "type","id"];
|
||||||
|
for (const id of normIDs) {
|
||||||
|
expandedRecipe.set(id,recipe[id]);
|
||||||
|
}
|
||||||
|
let rangeIDs = ["durability","lvl", "healthOrDamage", "duration", "basicDuration"];
|
||||||
|
for (const id of rangeIDs) {
|
||||||
|
if(recipe[id]){
|
||||||
|
expandedRecipe.set(id, [recipe[id]['minimum'], recipe[id]['maximum']]);
|
||||||
|
} else {
|
||||||
|
expandedRecipe.set(id, [0,0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expandedRecipe.set("materials", [ new Map([ ["item", recipe['materials'][0]['item']], ["amount", recipe['materials'][0]['amount']] ]) , new Map([ ["item", recipe['materials'][1]['item']], ["amount",recipe['materials'][1]['amount'] ] ]) ]);
|
||||||
|
return expandedRecipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*An independent helper function that rounds a rolled ID to the nearest integer OR brings the roll away from 0.
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
function idRound(id){
|
||||||
|
rounded = Math.round(id);
|
||||||
|
if(rounded == 0){
|
||||||
|
return 1; //this is a hack, will need changing along w/ rest of ID system if anything changes
|
||||||
|
}else{
|
||||||
|
return rounded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1315
js/builder.js
1166
js/builder_graph.js
Normal file
263
js/computation_graph.js
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
let all_nodes = [];
|
||||||
|
class ComputeNode {
|
||||||
|
/**
|
||||||
|
* Make a generic compute node.
|
||||||
|
* Adds the node to the global map of nodenames to nodes (for calling from html listeners).
|
||||||
|
*
|
||||||
|
* @param name : Name of the node (string). Must be unique. Must "fit in" a JS string (terminated by single quotes).
|
||||||
|
*/
|
||||||
|
constructor(name) {
|
||||||
|
this.inputs = []; // parent nodes
|
||||||
|
this.input_translation = new Map();
|
||||||
|
this.children = [];
|
||||||
|
this.value = null;
|
||||||
|
this.name = name;
|
||||||
|
this.update_task = null;
|
||||||
|
this.fail_cb = false; // Set to true to force updates even if parent failed.
|
||||||
|
this.dirty = 2; // 3 states:
|
||||||
|
// 2: dirty
|
||||||
|
// 1: possibly dirty
|
||||||
|
// 0: clean
|
||||||
|
this.inputs_dirty = new Map();
|
||||||
|
this.inputs_dirty_count = 0;
|
||||||
|
all_nodes.push(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request update of this compute node. Pushes updates to children.
|
||||||
|
*/
|
||||||
|
update() {
|
||||||
|
if (this.inputs_dirty_count != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.dirty === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.dirty == 2) {
|
||||||
|
let calc_inputs = new Map();
|
||||||
|
for (const input of this.inputs) {
|
||||||
|
calc_inputs.set(this.input_translation.get(input.name), input.value);
|
||||||
|
}
|
||||||
|
this.value = this.compute_func(calc_inputs);
|
||||||
|
}
|
||||||
|
this.dirty = 0;
|
||||||
|
for (const child of this.children) {
|
||||||
|
child.mark_input_clean(this.name, this.value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark parent as not dirty. Propagates calculation if all inputs are present.
|
||||||
|
*/
|
||||||
|
mark_input_clean(input_name, value) {
|
||||||
|
if (value !== null || this.fail_cb) {
|
||||||
|
if (this.inputs_dirty.get(input_name)) {
|
||||||
|
this.inputs_dirty.set(input_name, false);
|
||||||
|
this.inputs_dirty_count -= 1;
|
||||||
|
}
|
||||||
|
if (this.inputs_dirty_count === 0) {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_input_dirty(input_name) {
|
||||||
|
if (!this.inputs_dirty.get(input_name)) {
|
||||||
|
this.inputs_dirty.set(input_name, true);
|
||||||
|
this.inputs_dirty_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_dirty(dirty_state=2) {
|
||||||
|
if (this.dirty < dirty_state) {
|
||||||
|
this.dirty = dirty_state;
|
||||||
|
for (const child of this.children) {
|
||||||
|
child.mark_input_dirty(this.name);
|
||||||
|
child.mark_dirty(dirty_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value of this compute node. Can't trigger update cascades (push based update, not pull based.)
|
||||||
|
*/
|
||||||
|
get_value() {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method for computing something. Return value is set into this.value
|
||||||
|
*/
|
||||||
|
compute_func(input_map) {
|
||||||
|
throw "no compute func specified";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add link to a parent compute node, optionally with an alias.
|
||||||
|
*/
|
||||||
|
link_to(parent_node, link_name) {
|
||||||
|
this.inputs.push(parent_node)
|
||||||
|
link_name = (link_name !== undefined) ? link_name : parent_node.name;
|
||||||
|
this.input_translation.set(parent_node.name, link_name);
|
||||||
|
if (parent_node.dirty || (parent_node.value === null && !this.fail_cb)) {
|
||||||
|
this.inputs_dirty_count += 1;
|
||||||
|
this.inputs_dirty.set(parent_node.name, true);
|
||||||
|
}
|
||||||
|
parent_node.children.push(this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a link to a parent node.
|
||||||
|
* TODO: time complexity of list deletion (not super relevant but it hurts my soul)
|
||||||
|
*/
|
||||||
|
remove_link(parent_node) {
|
||||||
|
const idx = this.inputs.indexOf(parent_node); // Get idx
|
||||||
|
this.inputs.splice(idx, 1); // remove element
|
||||||
|
|
||||||
|
this.input_translation.delete(parent_node.name);
|
||||||
|
const was_dirty = this.inputs_dirty.get(parent_node.name);
|
||||||
|
this.inputs_dirty.delete(parent_node.name);
|
||||||
|
if (was_dirty) {
|
||||||
|
this.inputs_dirty_count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx2 = parent_node.children.indexOf(this);
|
||||||
|
parent_node.children.splice(idx2, 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValueCheckComputeNode extends ComputeNode {
|
||||||
|
constructor(name) { super(name); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request update of this compute node. Pushes updates to children,
|
||||||
|
* but only if this node's value changed.
|
||||||
|
*/
|
||||||
|
update() {
|
||||||
|
if (this.inputs_dirty_count != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.dirty === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let calc_inputs = new Map();
|
||||||
|
for (const input of this.inputs) {
|
||||||
|
calc_inputs.set(this.input_translation.get(input.name), input.value);
|
||||||
|
}
|
||||||
|
let val = this.compute_func(calc_inputs);
|
||||||
|
if (val !== this.value) {
|
||||||
|
super.mark_dirty(2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("soft update");
|
||||||
|
}
|
||||||
|
this.value = val;
|
||||||
|
|
||||||
|
this.dirty = 0;
|
||||||
|
for (const child of this.children) {
|
||||||
|
child.mark_input_clean(this.name, this.value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defaulting to "dusty" state.
|
||||||
|
*/
|
||||||
|
mark_dirty(dirty_state="unused") {
|
||||||
|
return super.mark_dirty(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a ComputeNode to be updated.
|
||||||
|
*
|
||||||
|
* @param node : ComputeNode to schedule an update for.
|
||||||
|
*/
|
||||||
|
function calcSchedule(node, timeout) {
|
||||||
|
if (node.update_task !== null) {
|
||||||
|
clearTimeout(node.update_task);
|
||||||
|
}
|
||||||
|
node.mark_dirty();
|
||||||
|
node.update_task = setTimeout(function() {
|
||||||
|
node.update();
|
||||||
|
node.update_task = null;
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrintNode extends ComputeNode {
|
||||||
|
|
||||||
|
constructor(name) {
|
||||||
|
super(name);
|
||||||
|
this.fail_cb = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_func(input_map) {
|
||||||
|
console.log([this.name, input_map]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node for getting an input from an input field.
|
||||||
|
* Fires updates whenever the input field is updated.
|
||||||
|
*
|
||||||
|
* Signature: InputNode() => str
|
||||||
|
*/
|
||||||
|
class InputNode extends ComputeNode {
|
||||||
|
constructor(name, input_field) {
|
||||||
|
super(name);
|
||||||
|
this.input_field = input_field;
|
||||||
|
this.input_field.addEventListener("input", () => calcSchedule(this, 500));
|
||||||
|
this.input_field.addEventListener("change", () => calcSchedule(this, 5));
|
||||||
|
//calcSchedule(this); Manually fire first update for better control
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_func(input_map) {
|
||||||
|
return this.input_field.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passthrough node for simple aggregation.
|
||||||
|
* Unfortunately if you use this too much you get layers and layers of maps...
|
||||||
|
*
|
||||||
|
* Signature: PassThroughNode(**kwargs) => Map[...]
|
||||||
|
*/
|
||||||
|
class PassThroughNode extends ComputeNode {
|
||||||
|
constructor(name) {
|
||||||
|
super(name);
|
||||||
|
this.breakout_nodes = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_func(input_map) {
|
||||||
|
return input_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a ComputeNode that will "break out" one part of this aggregation input.
|
||||||
|
* There is some overhead to this operation because ComputeNode is not exactly a free abstraction... oof
|
||||||
|
* Also you will recv updates whenever any input that is part of the aggregation changes even
|
||||||
|
* if the specific sub-input didn't change.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* sub-input: The key to listen to
|
||||||
|
*/
|
||||||
|
get_node(sub_input) {
|
||||||
|
if (this.breakout_nodes.has(sub_input)) {
|
||||||
|
return this.breakout_nodes.get(sub_input);
|
||||||
|
}
|
||||||
|
const _name = this.name;
|
||||||
|
const ret = new (class extends ComputeNode {
|
||||||
|
constructor() { super('passthrough-'+_name+'-'+sub_input); }
|
||||||
|
compute_func(input_map) { return input_map.get(_name).get(sub_input); }
|
||||||
|
})().link_to(this);
|
||||||
|
this.breakout_nodes.set(sub_input, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
|
@ -170,7 +170,6 @@ class Craft{
|
||||||
statMap.set(e + "Dam", "0-0");
|
statMap.set(e + "Dam", "0-0");
|
||||||
statMap.set(e + "DamLow", "0-0");
|
statMap.set(e + "DamLow", "0-0");
|
||||||
}
|
}
|
||||||
//statMap.set("damageBonus", [statMap.get("eDamPct"), statMap.get("tDamPct"), statMap.get("wDamPct"), statMap.get("fDamPct"), statMap.get("aDamPct")]);
|
|
||||||
statMap.set("category","weapon");
|
statMap.set("category","weapon");
|
||||||
statMap.set("atkSpd",this.atkSpd);
|
statMap.set("atkSpd",this.atkSpd);
|
||||||
}
|
}
|
||||||
|
@ -190,7 +189,6 @@ class Craft{
|
||||||
let amounts = this.recipe.get("materials").map(x=> x.get("amount"));
|
let amounts = this.recipe.get("materials").map(x=> x.get("amount"));
|
||||||
//Mat Multipliers - should work!
|
//Mat Multipliers - should work!
|
||||||
matmult = (tierToMult[tiers[0]]*amounts[0] + tierToMult[tiers[1]]*amounts[1]) / (amounts[0]+amounts[1]);
|
matmult = (tierToMult[tiers[0]]*amounts[0] + tierToMult[tiers[1]]*amounts[1]) / (amounts[0]+amounts[1]);
|
||||||
console.log(matmult);
|
|
||||||
|
|
||||||
let low = this.recipe.get("healthOrDamage")[0];
|
let low = this.recipe.get("healthOrDamage")[0];
|
||||||
let high = this.recipe.get("healthOrDamage")[1];
|
let high = this.recipe.get("healthOrDamage")[1];
|
||||||
|
@ -382,12 +380,10 @@ class Craft{
|
||||||
|
|
||||||
statMap.set("reqs",[0,0,0,0,0]);
|
statMap.set("reqs",[0,0,0,0,0]);
|
||||||
statMap.set("skillpoints", [0,0,0,0,0]);
|
statMap.set("skillpoints", [0,0,0,0,0]);
|
||||||
statMap.set("damageBonus",[0,0,0,0,0]);
|
|
||||||
for (const e in skp_order) {
|
for (const e in skp_order) {
|
||||||
statMap.set(skp_order[e], statMap.get("maxRolls").has(skp_order[e]) ? statMap.get("maxRolls").get(skp_order[e]) : 0);
|
statMap.set(skp_order[e], statMap.get("maxRolls").has(skp_order[e]) ? statMap.get("maxRolls").get(skp_order[e]) : 0);
|
||||||
statMap.get("skillpoints")[e] = statMap.get("maxRolls").has(skp_order[e]) ? statMap.get("maxRolls").get(skp_order[e]) : 0;
|
statMap.get("skillpoints")[e] = statMap.get("maxRolls").has(skp_order[e]) ? statMap.get("maxRolls").get(skp_order[e]) : 0;
|
||||||
statMap.get("reqs")[e] = statMap.has(skp_order[e]+"Req") && !consumableTypes.includes(statMap.get("type"))? statMap.get(skp_order[e]+"Req") : 0;
|
statMap.get("reqs")[e] = statMap.has(skp_order[e]+"Req") && !consumableTypes.includes(statMap.get("type"))? statMap.get(skp_order[e]+"Req") : 0;
|
||||||
statMap.get("damageBonus")[e] = statMap.has(skp_order[e]+"DamPct") ? statMap.get(skp_order[e]+"DamPct") : 0;
|
|
||||||
}
|
}
|
||||||
for (const id of rolledIDs) {
|
for (const id of rolledIDs) {
|
||||||
if (statMap.get("minRolls").has(id)) {
|
if (statMap.get("minRolls").has(id)) {
|
||||||
|
|
|
@ -22,10 +22,10 @@ const ING_BUILD_VERSION = "7.0.1";
|
||||||
*/
|
*/
|
||||||
let player_craft;
|
let player_craft;
|
||||||
|
|
||||||
function setTitle() {
|
// function setTitle() {
|
||||||
document.getElementById("header").textContent = "WynnCrafter version "+ING_BUILD_VERSION+" (ingredient db version "+ING_DB_VERSION+")";
|
// document.getElementById("header").textContent = "WynnCrafter version "+ING_BUILD_VERSION+" (ingredient db version "+ING_DB_VERSION+")";
|
||||||
document.getElementById("header").classList.add("funnynumber");
|
// document.getElementById("header").classList.add("funnynumber");
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,16 +44,32 @@ function init_crafter() {
|
||||||
try {
|
try {
|
||||||
document.getElementById("recipe-choice").addEventListener("change", (event) => {
|
document.getElementById("recipe-choice").addEventListener("change", (event) => {
|
||||||
updateMaterials();
|
updateMaterials();
|
||||||
|
updateCraftedImage();
|
||||||
|
calculateCraftSchedule();
|
||||||
|
});
|
||||||
|
document.getElementById("recipe-choice").addEventListener("oninput", (event) => {
|
||||||
|
updateCraftedImage();
|
||||||
});
|
});
|
||||||
document.getElementById("level-choice").addEventListener("change", (event) => {
|
document.getElementById("level-choice").addEventListener("change", (event) => {
|
||||||
updateMaterials();
|
updateMaterials();
|
||||||
|
calculateCraftSchedule();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (let i = 1; i < 4; ++i) {
|
||||||
|
document.getElementById("mat-1-"+i).setAttribute("onclick", document.getElementById("mat-1-"+i).getAttribute("onclick") + "; calculateCraftSchedule();");
|
||||||
|
document.getElementById("mat-2-"+i).setAttribute("onclick", document.getElementById("mat-2-"+i).getAttribute("onclick") + "; calculateCraftSchedule();");
|
||||||
|
}
|
||||||
|
for (let i = 1; i < 7; ++i) {
|
||||||
|
document.getElementById("ing-choice-" + i ).setAttribute("oninput", "calculateCraftSchedule();");
|
||||||
|
}
|
||||||
|
for (const str of ["slow", "normal", "fast"]) {
|
||||||
|
document.getElementById(str + "-atk-button").setAttribute("onclick", document.getElementById(str + "-atk-button").getAttribute("onclick") + "; calculateCraftSchedule();");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
populateFields();
|
populateFields();
|
||||||
decodeCraft(ing_url_tag);
|
decodeCraft(ing_url_tag);
|
||||||
setTitle();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("If you are seeing this while building, do not worry. Oherwise, panic! (jk contact ferricles)");
|
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +105,20 @@ function toggleAtkSpd(buttonId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let doCraftTask = null;
|
||||||
|
|
||||||
|
function calculateCraftSchedule(){
|
||||||
|
console.log("Craft Schedule called");
|
||||||
|
if (doCraftTask !== null) {
|
||||||
|
clearTimeout(doCraftTask);
|
||||||
|
}
|
||||||
|
doCraftTask = setTimeout(function(){
|
||||||
|
doCraftTask = null;
|
||||||
|
calculateCraft();
|
||||||
|
window.dispatchEvent(new Event('resize'));
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
function calculateCraft() {
|
function calculateCraft() {
|
||||||
//Make things display.
|
//Make things display.
|
||||||
for (let i of document.getElementsByClassName("hide-container-block")) {
|
for (let i of document.getElementsByClassName("hide-container-block")) {
|
||||||
|
@ -117,7 +147,8 @@ function calculateCraft() {
|
||||||
}
|
}
|
||||||
let ingreds = [];
|
let ingreds = [];
|
||||||
for (i = 1; i < 7; i++) {
|
for (i = 1; i < 7; i++) {
|
||||||
console.log(getValue("ing-choice-"+i));
|
console.log("ing-choice-"+i);
|
||||||
|
// console.log(getValue("ing-choice-"+i));
|
||||||
getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i))));
|
getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i))));
|
||||||
}
|
}
|
||||||
let atkSpd = "NORMAL"; //default attack speed will be normal.
|
let atkSpd = "NORMAL"; //default attack speed will be normal.
|
||||||
|
@ -138,16 +169,19 @@ function calculateCraft() {
|
||||||
console.log(levelrange)
|
console.log(levelrange)
|
||||||
console.log(mat_tiers)
|
console.log(mat_tiers)
|
||||||
console.log(ingreds)*/
|
console.log(ingreds)*/
|
||||||
|
|
||||||
document.getElementById("mat-1").textContent = recipe.get("materials")[0].get("item").split(" ").slice(1).join(" ") + " Tier:";
|
document.getElementById("mat-1").textContent = recipe.get("materials")[0].get("item").split(" ").slice(1).join(" ") + " Tier:";
|
||||||
document.getElementById("mat-2").textContent = recipe.get("materials")[1].get("item").split(" ").slice(1).join(" ") + " Tier:";
|
document.getElementById("mat-2").textContent = recipe.get("materials")[1].get("item").split(" ").slice(1).join(" ") + " Tier:";
|
||||||
|
|
||||||
//Display Recipe Stats
|
//Display Recipe Stats
|
||||||
displayRecipeStats(player_craft, "recipe-stats");
|
displayRecipeStats(player_craft, "recipe-stats");
|
||||||
for(let i = 0; i < 6; i++) {
|
|
||||||
displayExpandedIngredient(player_craft["ingreds"][i],"tooltip-" + i);
|
|
||||||
}
|
|
||||||
//Display Craft Stats
|
//Display Craft Stats
|
||||||
displayCraftStats(player_craft, "craft-stats");
|
// displayCraftStats(player_craft, "craft-stats");
|
||||||
|
let mock_item = player_craft.statMap;
|
||||||
|
if (mock_item.get('category') === 'weapon') { apply_weapon_powders(mock_item) };
|
||||||
|
displayExpandedItem(mock_item, "craft-stats");
|
||||||
|
|
||||||
//Display Ingredients' Stats
|
//Display Ingredients' Stats
|
||||||
for (let i = 1; i < 7; i++) {
|
for (let i = 1; i < 7; i++) {
|
||||||
displayExpandedIngredient(player_craft.ingreds[i-1] , "ing-"+i+"-stats");
|
displayExpandedIngredient(player_craft.ingreds[i-1] , "ing-"+i+"-stats");
|
||||||
|
@ -187,6 +221,7 @@ function decodeCraft(ing_url_tag) {
|
||||||
//console.log(Base64.toInt(tag.substring(12,14)));
|
//console.log(Base64.toInt(tag.substring(12,14)));
|
||||||
recipesName = recipe.split("-");
|
recipesName = recipe.split("-");
|
||||||
setValue("recipe-choice",recipesName[0]);
|
setValue("recipe-choice",recipesName[0]);
|
||||||
|
updateCraftedImage();
|
||||||
setValue("level-choice",recipesName[1]+"-"+recipesName[2]);
|
setValue("level-choice",recipesName[1]+"-"+recipesName[2]);
|
||||||
tierNum = Base64.toInt(tag.substring(14,15));
|
tierNum = Base64.toInt(tag.substring(14,15));
|
||||||
mat_tiers = [];
|
mat_tiers = [];
|
||||||
|
@ -227,11 +262,20 @@ function populateFields() {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Copies the CR Hash (CR-blahblahblah)
|
||||||
/* Copy the link
|
|
||||||
*/
|
*/
|
||||||
function copyRecipe(){
|
function copyRecipeHash() {
|
||||||
|
if (player_craft) {
|
||||||
|
copyTextToClipboard("CR-"+location.hash.slice(1));
|
||||||
|
document.getElementById("copy-hash-button").textContent = "Copied!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copies the link (hppeng-wynn.github.io/crafter/#blahblah)
|
||||||
|
*/
|
||||||
|
function copyRecipe() {
|
||||||
if (player_craft) {
|
if (player_craft) {
|
||||||
copyTextToClipboard(ing_url_base+location.hash);
|
copyTextToClipboard(ing_url_base+location.hash);
|
||||||
document.getElementById("copy-button").textContent = "Copied!";
|
document.getElementById("copy-button").textContent = "Copied!";
|
||||||
|
@ -240,7 +284,7 @@ function copyRecipe(){
|
||||||
|
|
||||||
/* Copy the link AND a display of all ingredients
|
/* Copy the link AND a display of all ingredients
|
||||||
*/
|
*/
|
||||||
function shareRecipe(){
|
function shareRecipe() {
|
||||||
if (player_craft) {
|
if (player_craft) {
|
||||||
let copyString = ing_url_base+location.hash + "\n";
|
let copyString = ing_url_base+location.hash + "\n";
|
||||||
let name = player_craft.recipe.get("name").split("-");
|
let name = player_craft.recipe.get("name").split("-");
|
||||||
|
@ -296,6 +340,16 @@ function toggleMaterial(buttonId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Updates the crafted icon.
|
||||||
|
*/
|
||||||
|
function updateCraftedImage() {
|
||||||
|
let input = document.getElementById("recipe-choice");
|
||||||
|
if (all_types.includes(input.value)) {
|
||||||
|
document.getElementById("recipe-img").src = "../media/items/" + (newIcons ? "new/":"old/") + "generic-" + input.value.toLowerCase() + ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset all fields
|
/* Reset all fields
|
||||||
*/
|
*/
|
||||||
function resetFields() {
|
function resetFields() {
|
||||||
|
@ -313,4 +367,8 @@ function resetFields() {
|
||||||
calculateCraft();
|
calculateCraft();
|
||||||
}
|
}
|
||||||
|
|
||||||
load_ing_init(init_crafter);
|
(async function() {
|
||||||
|
let load_promises = [ load_ing_init() ];
|
||||||
|
await Promise.all(load_promises);
|
||||||
|
init_crafter();
|
||||||
|
})();
|
||||||
|
|
166
js/custom.js
|
@ -1,5 +1,6 @@
|
||||||
const ci_save_order = ["name", "lore", "tier", "set", "slots", "type", "material", "drop", "quest", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq","str", "dex", "int", "agi", "def", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "majorIds", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd","durability","duration","charges"];
|
const ci_save_order = ["name", "lore", "tier", "set", "slots", "type", "material", "drop", "quest", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "str", "dex", "int", "agi", "def", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "majorIds", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "durability", "duration", "charges"];
|
||||||
const nonRolled_strings = ["name","lore", "tier","set","type","material","drop","quest","majorIds","classReq","atkSpd","displayName", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "durability", "duration"];
|
const nonRolled_strings = ["name", "lore", "tier", "set", "type", "material", "drop", "quest", "majorIds", "classReq", "atkSpd", "displayName", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_", "durability", "duration"];
|
||||||
|
|
||||||
//omitted restrict - it's always "Custom Item"
|
//omitted restrict - it's always "Custom Item"
|
||||||
//omitted displayName - either it's the same as name (repetitive) or it's "Custom Item"
|
//omitted displayName - either it's the same as name (repetitive) or it's "Custom Item"
|
||||||
//omitted category - can always get this from type
|
//omitted category - can always get this from type
|
||||||
|
@ -16,7 +17,7 @@ const nonRolled_strings = ["name","lore", "tier","set","type","material","drop",
|
||||||
function encodeCustom(custom, verbose) {
|
function encodeCustom(custom, verbose) {
|
||||||
if (custom) {
|
if (custom) {
|
||||||
if (custom.statMap) {
|
if (custom.statMap) {
|
||||||
custom = custom.statMap;
|
custom = custom.statMap;
|
||||||
}
|
}
|
||||||
let hash = "1";
|
let hash = "1";
|
||||||
//version 1
|
//version 1
|
||||||
|
@ -34,55 +35,53 @@ function encodeCustom(custom, verbose) {
|
||||||
// 1 - min neg max pos
|
// 1 - min neg max pos
|
||||||
// 2 - min pos max neg (how?)
|
// 2 - min pos max neg (how?)
|
||||||
// 3 - min neg max neg
|
// 3 - min neg max neg
|
||||||
let sign = (Boolean(val_min / Math.abs(val_min) < 0) | 0) + 2*(Boolean(val_max / Math.abs(val_max) < 0) | 0);
|
let sign = (Boolean(val_min / Math.abs(val_min) < 0) | 0) + 2 * (Boolean(val_max / Math.abs(val_max) < 0) | 0);
|
||||||
//console.log(id + ": " + sign);
|
//console.log(id + ": " + sign);
|
||||||
let min_len = Math.max(1,Math.ceil(log(64,Math.abs(val_min)+1)));
|
let min_len = Math.max(1, Math.ceil(log(64, Math.abs(val_min) + 1)));
|
||||||
let max_len = Math.max(1,Math.ceil(log(64,Math.abs(val_max)+1)));
|
let max_len = Math.max(1, Math.ceil(log(64, Math.abs(val_max) + 1)));
|
||||||
let len = Math.max(min_len,max_len);
|
let len = Math.max(min_len, max_len);
|
||||||
val_min = Math.abs(val_min);
|
val_min = Math.abs(val_min);
|
||||||
val_max = Math.abs(val_max);
|
val_max = Math.abs(val_max);
|
||||||
|
|
||||||
if ( val_min != 0 || val_max != 0 ) {
|
if (val_min != 0 || val_max != 0) {
|
||||||
if (custom.get("fixID")) {
|
if (custom.get("fixID")) {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + sign + Base64.fromIntN(val_min, len);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(len, 2) + sign + Base64.fromIntN(val_min, len);
|
||||||
} else {
|
} else {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + sign + Base64.fromIntN(val_min, len) + Base64.fromIntN(val_max,len);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(len, 2) + sign + Base64.fromIntN(val_min, len) + Base64.fromIntN(val_max, len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let damages = ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]; //"nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_"
|
let damages = ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]; //"nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_"
|
||||||
let val = custom.get(id);
|
let val = custom.get(id);
|
||||||
if (id == "majorIds") {
|
if (id == "majorIds") {
|
||||||
console.log(val);
|
|
||||||
if (val.length > 0) {
|
if (val.length > 0) {
|
||||||
val = val[0];
|
val = val[0];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val = "";
|
val = "";
|
||||||
}
|
}
|
||||||
console.log(val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof(val) === "string" && val !== "") {
|
if (typeof (val) === "string" && val !== "") {
|
||||||
if ((damages.includes(id) && val === "0-0") || (!verbose && ["lore","majorIds","quest","materials","drop","set"].includes(id))) { continue; }
|
if ((damages.includes(id) && val === "0-0") || (!verbose && ["lore", "majorIds", "quest", "materials", "drop", "set"].includes(id))) { continue; }
|
||||||
if (id === "type") {
|
if (id === "type") {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(types.indexOf(val.substring(0,1).toUpperCase()+val.slice(1)),1);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(types.indexOf(val.substring(0, 1).toUpperCase() + val.slice(1)), 1);
|
||||||
} else if (id === "tier") {
|
} else if (id === "tier") {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(tiers.indexOf(val),1);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(tiers.indexOf(val), 1);
|
||||||
} else if (id === "atkSpd") {
|
} else if (id === "atkSpd") {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(attackSpeeds.indexOf(val),1);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(attackSpeeds.indexOf(val), 1);
|
||||||
} else if (id === "classReq") {
|
} else if (id === "classReq") {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(classes.indexOf(val),1);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(classes.indexOf(val), 1);
|
||||||
} else {
|
} else {
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(val.replaceAll(" ", "%20").length,2) + val.replaceAll(" ", "%20"); //values cannot go above 4096 chars!!!! Is this ok?
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(val.replaceAll(" ", "%20").length, 2) + val.replaceAll(" ", "%20"); //values cannot go above 4096 chars!!!! Is this ok?
|
||||||
}
|
}
|
||||||
} else if (typeof(val) === "number" && val != 0) {
|
} else if (typeof (val) === "number" && val != 0) {
|
||||||
let len = Math.max(1,Math.ceil(log(64,Math.abs(val))));
|
let len = Math.max(1, Math.ceil(log(64, Math.abs(val))));
|
||||||
let sign = Boolean(val / Math.abs(val) < 0) | 0;
|
let sign = Boolean(val / Math.abs(val) < 0) | 0;
|
||||||
//console.log(sign);
|
//console.log(sign);
|
||||||
//hash += Base64.fromIntN(i,2) + Base64.fromIntN(val,Math.max(1,Math.ceil(log(64,Math.abs(val))))) + "_";
|
//hash += Base64.fromIntN(i,2) + Base64.fromIntN(val,Math.max(1,Math.ceil(log(64,Math.abs(val))))) + "_";
|
||||||
hash += Base64.fromIntN(i,2) + Base64.fromIntN(len,2) + sign + Base64.fromIntN(Math.abs(val),len);
|
hash += Base64.fromIntN(i, 2) + Base64.fromIntN(len, 2) + sign + Base64.fromIntN(Math.abs(val), len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,49 +94,51 @@ function encodeCustom(custom, verbose) {
|
||||||
function getCustomFromHash(hash) {
|
function getCustomFromHash(hash) {
|
||||||
let name = hash.slice();
|
let name = hash.slice();
|
||||||
let statMap;
|
let statMap;
|
||||||
|
console.log("decoding");
|
||||||
try {
|
try {
|
||||||
if (name.slice(0,3) === "CI-") {
|
if (name.slice(0, 3) === "CI-") {
|
||||||
name = name.substring(3);
|
name = name.substring(3);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Not a custom item!");
|
throw new Error("Not a custom item!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//probably change vers and fixID to be encoded and decoded to/from B64 in the future
|
||||||
let version = name.charAt(0);
|
let version = name.charAt(0);
|
||||||
let fixID = Boolean(parseInt(name.charAt(1),10));
|
let fixID = Boolean(parseInt(name.charAt(1), 10));
|
||||||
let tag = name.substring(2);
|
let tag = name.substring(2);
|
||||||
statMap = new Map();
|
statMap = new Map();
|
||||||
statMap.set("minRolls", new Map());
|
statMap.set("minRolls", new Map());
|
||||||
statMap.set("maxRolls", new Map());
|
statMap.set("maxRolls", new Map());
|
||||||
|
|
||||||
if (version === "1") {
|
if (version === "1") {
|
||||||
//do the things
|
//do the things
|
||||||
if (fixID) {
|
if (fixID) {
|
||||||
statMap.set("fixID", true);
|
statMap.set("fixID", true);
|
||||||
}
|
}
|
||||||
while (tag !== "") {
|
while (tag !== "") {
|
||||||
let id = ci_save_order[Base64.toInt(tag.slice(0,2))];
|
let id = ci_save_order[Base64.toInt(tag.slice(0, 2))];
|
||||||
let len = Base64.toInt(tag.slice(2,4));
|
let len = Base64.toInt(tag.slice(2, 4));
|
||||||
if (rolledIDs.includes(id)) {
|
if (rolledIDs.includes(id)) {
|
||||||
let sign = parseInt(tag.slice(4,5),10);
|
let sign = parseInt(tag.slice(4, 5), 10);
|
||||||
let minRoll = Base64.toInt(tag.slice(5,5+len));
|
let minRoll = Base64.toInt(tag.slice(5, 5 + len));
|
||||||
if (!fixID) {
|
if (!fixID) {
|
||||||
let maxRoll = Base64.toInt(tag.slice(5+len,5+2*len));
|
let maxRoll = Base64.toInt(tag.slice(5 + len, 5 + 2 * len));
|
||||||
if (sign > 1) {
|
if (sign > 1) {
|
||||||
maxRoll *= -1;
|
maxRoll *= -1;
|
||||||
}
|
}
|
||||||
if (sign % 2 == 1) {
|
if (sign % 2 == 1) {
|
||||||
minRoll *= -1;
|
minRoll *= -1;
|
||||||
}
|
}
|
||||||
statMap.get("minRolls").set(id,minRoll);
|
statMap.get("minRolls").set(id, minRoll);
|
||||||
statMap.get("maxRolls").set(id,maxRoll);
|
statMap.get("maxRolls").set(id, maxRoll);
|
||||||
tag = tag.slice(5+2*len);
|
tag = tag.slice(5 + 2 * len);
|
||||||
} else {
|
} else {
|
||||||
if (sign != 0) {
|
if (sign != 0) {
|
||||||
minRoll *= -1;
|
minRoll *= -1;
|
||||||
}
|
}
|
||||||
statMap.get("minRolls").set(id,minRoll);
|
statMap.get("minRolls").set(id, minRoll);
|
||||||
statMap.get("maxRolls").set(id,minRoll);
|
statMap.get("maxRolls").set(id, minRoll);
|
||||||
tag = tag.slice(5+len);
|
tag = tag.slice(5 + len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let val;
|
let val;
|
||||||
|
@ -155,17 +156,17 @@ function getCustomFromHash(hash) {
|
||||||
val = classes[Base64.toInt(tag.charAt(2))];
|
val = classes[Base64.toInt(tag.charAt(2))];
|
||||||
len = -1;
|
len = -1;
|
||||||
} else { //general case
|
} else { //general case
|
||||||
val = tag.slice(4,4+len).replaceAll("%20"," ");
|
val = tag.slice(4, 4 + len).replaceAll("%20", " ");
|
||||||
}
|
}
|
||||||
tag = tag.slice(4+len);
|
tag = tag.slice(4 + len);
|
||||||
} else {
|
} else {
|
||||||
let sign = parseInt(tag.slice(4,5),10);
|
let sign = parseInt(tag.slice(4, 5), 10);
|
||||||
val = Base64.toInt(tag.slice(5,5+len));
|
val = Base64.toInt(tag.slice(5, 5 + len));
|
||||||
if (sign == 1) {
|
if (sign == 1) {
|
||||||
val *= -1;
|
val *= -1;
|
||||||
}
|
}
|
||||||
tag = tag.slice(5+len);
|
tag = tag.slice(5 + len);
|
||||||
}
|
}
|
||||||
if (id === "majorIds") {
|
if (id === "majorIds") {
|
||||||
val = [val];
|
val = [val];
|
||||||
console.log(val);
|
console.log(val);
|
||||||
|
@ -173,27 +174,28 @@ function getCustomFromHash(hash) {
|
||||||
statMap.set(id, val);
|
statMap.set(id, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statMap.set("hash","CI-"+name);
|
statMap.set("hash", "CI-" + name);
|
||||||
|
statMap.set("custom", true);
|
||||||
return new Custom(statMap);
|
return new Custom(statMap);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//console.log(statMap);
|
//console.log(statMap);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An object representing a Custom Item. Mostly for vanity purposes.
|
/** An object representing a Custom Item. Mostly for vanity purposes.
|
||||||
* @dep Requires the use of nonRolledIDs and rolledIDs from display_constants.js.
|
* @dep Requires the use of nonRolledIDs and rolledIDs from display_constants.js.
|
||||||
* @dep Requires the use of attackSpeeds from build.js.
|
* @dep Requires the use of attackSpeeds from build.js.
|
||||||
*/
|
*/
|
||||||
class Custom{
|
class Custom {
|
||||||
/**
|
/**
|
||||||
* @description Construct a custom item (CI) from a statMap.
|
* @description Construct a custom item (CI) from a statMap.
|
||||||
* @param {statMap}: A map with keys from rolledIDs or nonRolledIDs or minRolls/maxRolls and values befitting the keys. minRolls and maxRolls are their own maps and have the same keys, but with minimum and maximum values (for rolls).
|
* @param {statMap}: A map with keys from rolledIDs or nonRolledIDs or minRolls/maxRolls and values befitting the keys. minRolls and maxRolls are their own maps and have the same keys, but with minimum and maximum values (for rolls).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
constructor(statMap){
|
constructor(statMap) {
|
||||||
this.statMap = statMap;
|
this.statMap = statMap;
|
||||||
// TODO patch
|
// TODO patch
|
||||||
// this.statMap.set("majorIds", [this.statMap.get("majorIds")]);
|
// this.statMap.set("majorIds", [this.statMap.get("majorIds")]);
|
||||||
|
@ -202,17 +204,17 @@ class Custom{
|
||||||
|
|
||||||
setHash(hash) {
|
setHash(hash) {
|
||||||
let ihash = hash.slice();
|
let ihash = hash.slice();
|
||||||
if (ihash.slice(0,3) !== "CI-") {
|
if (ihash.slice(0, 3) !== "CI-") {
|
||||||
ihash = "CI-" + hash;
|
ihash = "CI-" + hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hash = ihash;
|
this.hash = ihash;
|
||||||
this.statMap.set("hash",ihash);
|
this.statMap.set("hash", ihash);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateName(name) {
|
updateName(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.displayName = name;
|
this.displayName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get all stats for this CI.
|
/* Get all stats for this CI.
|
||||||
|
@ -220,26 +222,24 @@ class Custom{
|
||||||
* Follows the expandedItem item structure, similar to a crafted item.
|
* Follows the expandedItem item structure, similar to a crafted item.
|
||||||
* TODO: Check if this is even useful
|
* TODO: Check if this is even useful
|
||||||
*/
|
*/
|
||||||
initCustomStats(){
|
initCustomStats() {
|
||||||
//this.setHashVerbose(); //do NOT move sethash from here please
|
//this.setHashVerbose(); //do NOT move sethash from here please
|
||||||
|
|
||||||
this.statMap.set("custom", true);
|
|
||||||
console.log(this.statMap);
|
console.log(this.statMap);
|
||||||
|
|
||||||
for (const id of ci_save_order) {
|
for (const id of ci_save_order) {
|
||||||
if (rolledIDs.includes(id)) {
|
if (rolledIDs.includes(id)) {
|
||||||
if (!(this.statMap.get("minRolls").has(id) && this.statMap.get("minRolls").get(id))) {
|
if (!(this.statMap.get("minRolls").has(id) && this.statMap.get("minRolls").get(id))) {
|
||||||
this.statMap.get("minRolls").set(id,0);
|
this.statMap.get("minRolls").set(id, 0);
|
||||||
this.statMap.get("maxRolls").set(id,0);
|
this.statMap.get("maxRolls").set(id, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (nonRolled_strings.includes(id)) {
|
if (nonRolled_strings.includes(id)) {
|
||||||
if (!(this.statMap.has(id)&&this.statMap.get(id))) {
|
if (!(this.statMap.has(id) && this.statMap.get(id))) {
|
||||||
this.statMap.set(id,"");
|
this.statMap.set(id, "");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!(this.statMap.has(id)&&this.statMap.get(id))) {
|
if (!(this.statMap.has(id) && this.statMap.get(id))) {
|
||||||
this.statMap.set(id,0);
|
this.statMap.set(id, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,30 +247,30 @@ class Custom{
|
||||||
let type = this.statMap.get("type").toLowerCase();
|
let type = this.statMap.get("type").toLowerCase();
|
||||||
console.log(type);
|
console.log(type);
|
||||||
if (weaponTypes.includes(type)) {
|
if (weaponTypes.includes(type)) {
|
||||||
for (const n of ["nDam","eDam","tDam","wDam","fDam","aDam"]) {
|
for (const n of ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]) {
|
||||||
if (!(this.statMap.has(n) && this.statMap.get(n))) {
|
if (!(this.statMap.has(n) && this.statMap.get(n))) {
|
||||||
this.statMap.set(n,"0-0");
|
this.statMap.set(n, "0-0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (const n of ["nDam","eDam","tDam","wDam","fDam","aDam"]) {
|
for (const n of ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]) {
|
||||||
if (this.statMap.has(n)) {
|
if (this.statMap.has(n)) {
|
||||||
this.statMap.delete(n);
|
this.statMap.delete(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.statMap.get("type")) {
|
if (this.statMap.get("type")) {
|
||||||
this.statMap.set("type",this.statMap.get("type").toLowerCase());
|
this.statMap.set("type", this.statMap.get("type").toLowerCase());
|
||||||
if (armorTypes.includes(this.statMap.get("type"))) {
|
if (armorTypes.includes(this.statMap.get("type"))) {
|
||||||
this.statMap.set("category","armor");
|
this.statMap.set("category", "armor");
|
||||||
} else if (accessoryTypes.includes(this.statMap.get("type"))) {
|
} else if (accessoryTypes.includes(this.statMap.get("type"))) {
|
||||||
this.statMap.set("category","accessory");
|
this.statMap.set("category", "accessory");
|
||||||
} else if (weaponTypes.includes(this.statMap.get("type"))) {
|
} else if (weaponTypes.includes(this.statMap.get("type"))) {
|
||||||
this.statMap.set("category","weapon");
|
this.statMap.set("category", "weapon");
|
||||||
} else if (consumableTypes.includes(this.statMap.get("type"))) {
|
} else if (consumableTypes.includes(this.statMap.get("type"))) {
|
||||||
this.statMap.set("category","consumable");
|
this.statMap.set("category", "consumable");
|
||||||
} else if (tomeTypes.includes(this.statMap.get("type"))) {
|
} else if (tomeTypes.includes(this.statMap.get("type"))) {
|
||||||
this.statMap.set("category", "tome");
|
this.statMap.set("category", "tome");
|
||||||
}
|
}
|
||||||
|
@ -280,13 +280,13 @@ class Custom{
|
||||||
this.statMap.set("crafted", true);
|
this.statMap.set("crafted", true);
|
||||||
|
|
||||||
for (const e of skp_elements) {
|
for (const e of skp_elements) {
|
||||||
this.statMap.set(e+"DamLow", this.statMap.get(e+"Dam"));
|
this.statMap.set(e + "DamLow", this.statMap.get(e + "Dam"));
|
||||||
}
|
}
|
||||||
this.statMap.set("nDamLow", this.statMap.get("nDam"));
|
this.statMap.set("nDamLow", this.statMap.get("nDam"));
|
||||||
this.statMap.set("hpLow", this.statMap.get("hp"));
|
this.statMap.set("hpLow", this.statMap.get("hp"));
|
||||||
for (const e of skp_order) {
|
for (const e of skp_order) {
|
||||||
this.statMap.get("minRolls").set(e,this.statMap.get(e));
|
this.statMap.get("minRolls").set(e, this.statMap.get(e));
|
||||||
this.statMap.get("maxRolls").set(e,this.statMap.get(e));
|
this.statMap.get("maxRolls").set(e, this.statMap.get(e));
|
||||||
}
|
}
|
||||||
// for (const e of ["durability", "duration"]) {
|
// for (const e of ["durability", "duration"]) {
|
||||||
// if (this.statMap.get(e) === "") {
|
// if (this.statMap.get(e) === "") {
|
||||||
|
@ -296,15 +296,15 @@ class Custom{
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
this.statMap.set("lvlLow",this.statMap.get("lvl"));
|
this.statMap.set("lvlLow", this.statMap.get("lvl"));
|
||||||
if (this.statMap.get("category") === "weapon") {
|
if (this.statMap.get("category") === "weapon") {
|
||||||
//this is for powder purposes.
|
//this is for powder purposes.
|
||||||
//users will likely not stick to the 0.9,1.1 rule because custom item. We will get around this by breaking everything and rewarding users for sticking to 0.9,1.1.
|
//users will likely not stick to the 0.9,1.1 rule because custom item. We will get around this by breaking everything and rewarding users for sticking to 0.9,1.1.
|
||||||
this.statMap.set("nDamBaseLow", Math.floor((parseFloat(this.statMap.get("nDamLow")) + parseFloat(this.statMap.get("nDam"))) / 2) );
|
this.statMap.set("nDamBaseLow", Math.floor((parseFloat(this.statMap.get("nDamLow")) + parseFloat(this.statMap.get("nDam"))) / 2));
|
||||||
this.statMap.set("nDamBaseHigh", Math.floor((parseFloat(this.statMap.get("nDamLow")) + parseFloat(this.statMap.get("nDam"))) / 2) );
|
this.statMap.set("nDamBaseHigh", Math.floor((parseFloat(this.statMap.get("nDamLow")) + parseFloat(this.statMap.get("nDam"))) / 2));
|
||||||
for (const e in skp_elements) {
|
for (const e in skp_elements) {
|
||||||
this.statMap.set(skp_elements[e]+"DamBaseLow", Math.floor((parseFloat(this.statMap.get(skp_elements[e]+"DamLow")) + parseFloat(this.statMap.get(skp_elements[e]+"Dam"))) / 2));
|
this.statMap.set(skp_elements[e] + "DamBaseLow", Math.floor((parseFloat(this.statMap.get(skp_elements[e] + "DamLow")) + parseFloat(this.statMap.get(skp_elements[e] + "Dam"))) / 2));
|
||||||
this.statMap.set(skp_elements[e]+"DamBaseHigh", Math.floor((parseFloat(this.statMap.get(skp_elements[e]+"DamLow")) + parseFloat(this.statMap.get(skp_elements[e]+"Dam"))) / 2));
|
this.statMap.set(skp_elements[e] + "DamBaseHigh", Math.floor((parseFloat(this.statMap.get(skp_elements[e] + "DamLow")) + parseFloat(this.statMap.get(skp_elements[e] + "Dam"))) / 2));
|
||||||
}
|
}
|
||||||
this.statMap.set("ingredPowders", []);
|
this.statMap.set("ingredPowders", []);
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,7 @@ class Custom{
|
||||||
|
|
||||||
if (this.statMap.get("category") !== "weapon") {
|
if (this.statMap.get("category") !== "weapon") {
|
||||||
this.statMap.set("atkSpd", "");
|
this.statMap.set("atkSpd", "");
|
||||||
for (const n in ["nDam","eDam","tDam","wDam","fDam","aDam"]) {
|
for (const n in ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]) {
|
||||||
//this.statMap.set(n,"");
|
//this.statMap.set(n,"");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -325,12 +325,12 @@ class Custom{
|
||||||
} else {
|
} else {
|
||||||
this.statMap.set("displayName", "Custom Item");
|
this.statMap.set("displayName", "Custom Item");
|
||||||
}
|
}
|
||||||
this.statMap.set("powders",[]);
|
this.statMap.set("powders", []);
|
||||||
|
|
||||||
|
|
||||||
this.statMap.set("reqs",[this.statMap.get("strReq"),this.statMap.get("dexReq"),this.statMap.get("intReq"),this.statMap.get("defReq"),this.statMap.get("agiReq")]);
|
this.statMap.set("reqs", [this.statMap.get("strReq"), this.statMap.get("dexReq"), this.statMap.get("intReq"), this.statMap.get("defReq"), this.statMap.get("agiReq")]);
|
||||||
this.statMap.set("skillpoints", [this.statMap.get("str"),this.statMap.get("dex"),this.statMap.get("int"),this.statMap.get("def"),this.statMap.get("agi")]);
|
this.statMap.set("skillpoints", [this.statMap.get("str"), this.statMap.get("dex"), this.statMap.get("int"), this.statMap.get("def"), this.statMap.get("agi")]);
|
||||||
|
|
||||||
|
|
||||||
this.statMap.set("restrict", "Custom Item")
|
this.statMap.set("restrict", "Custom Item")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
const custom_url_base = location.href.split("#")[0];
|
const custom_url_base = location.href.split("#")[0];
|
||||||
const custom_url_tag = location.hash.slice(1);
|
const custom_url_tag = location.hash.slice(1);
|
||||||
|
|
||||||
const CUSTOM_BUILD_VERSION = "7.0.1";
|
|
||||||
|
|
||||||
function setTitle() {
|
|
||||||
let text = "WynnCustom version "+CUSTOM_BUILD_VERSION;
|
|
||||||
document.getElementById("header").classList.add("funnynumber");
|
|
||||||
document.getElementById("header").textContent = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle();
|
|
||||||
|
|
||||||
let player_custom_item;
|
let player_custom_item;
|
||||||
let player_custom_ing;
|
let player_custom_ing;
|
||||||
let base_item; //the item that a user starts from, if any
|
let base_item; //the item that a user starts from, if any
|
||||||
|
@ -51,7 +41,6 @@ function init_customizer() {
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("If you are seeing this while building, do not worry. Oherwise, panic! (jk contact ferricles)");
|
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +63,7 @@ function calculateCustom() {
|
||||||
statMap.set("maxRolls", new Map());
|
statMap.set("maxRolls", new Map());
|
||||||
let inputs = document.getElementsByTagName("input");
|
let inputs = document.getElementsByTagName("input");
|
||||||
|
|
||||||
if (document.getElementById("fixID-choice").textContent === "yes") {//Fixed IDs
|
if (document.getElementById("fixID-choice").classList.contains("toggleOn")) {//Fixed IDs
|
||||||
for (const input of inputs) {
|
for (const input of inputs) {
|
||||||
if (input.id.includes("-min") || input.id.includes("-max")) {
|
if (input.id.includes("-min") || input.id.includes("-max")) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -187,33 +176,34 @@ function calculateCustom() {
|
||||||
|
|
||||||
player_custom_item = new Custom(statMap);
|
player_custom_item = new Custom(statMap);
|
||||||
|
|
||||||
document.getElementById("right-container").classList.remove("sticky-box");
|
|
||||||
let custom_str = encodeCustom(player_custom_item.statMap, true);
|
let custom_str = encodeCustom(player_custom_item.statMap, true);
|
||||||
location.hash = custom_str;
|
location.hash = custom_str;
|
||||||
player_custom_item.setHash(custom_str);
|
player_custom_item.setHash(custom_str);
|
||||||
//console.log(player_custom_item.statMap.get("hash"));
|
|
||||||
|
|
||||||
|
|
||||||
displayExpandedItem(player_custom_item.statMap, "custom-stats");
|
displaysq2ExpandedItem(player_custom_item.statMap, "custom-stats");
|
||||||
|
|
||||||
//console.log(player_custom_item.statMap);
|
|
||||||
|
|
||||||
}catch (error) {
|
}catch (error) {
|
||||||
//USE THE ERROR <p>S!
|
//The error elements no longer exist in the page. Add them back if needed.
|
||||||
|
|
||||||
|
// console.log(error.stack);
|
||||||
|
|
||||||
let msg = error.stack;
|
let msg = error.stack;
|
||||||
let lines = msg.split("\n");
|
let lines = msg.split("\n");
|
||||||
let header = document.getElementById("header");
|
for (line of lines) {
|
||||||
header.textContent = "";
|
console.log(line);
|
||||||
for (const line of lines) {
|
|
||||||
let p = document.createElement("p");
|
|
||||||
p.classList.add("itemp");
|
|
||||||
p.textContent = line;
|
|
||||||
header.appendChild(p);
|
|
||||||
}
|
}
|
||||||
let p2 = document.createElement("p");
|
// let header = document.getElementById("header");
|
||||||
p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
|
// header.textContent = "";
|
||||||
header.appendChild(p2);
|
// for (const line of lines) {
|
||||||
|
// let p = document.createElement("p");
|
||||||
|
// p.classList.add("itemp");
|
||||||
|
// p.textContent = line;
|
||||||
|
// header.appendChild(p);
|
||||||
|
// }
|
||||||
|
// let p2 = document.createElement("p");
|
||||||
|
// p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
|
||||||
|
// header.appendChild(p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -236,13 +226,13 @@ function decodeCustom(custom_url_tag) {
|
||||||
if (fixID) {
|
if (fixID) {
|
||||||
statMap.set("fixId", true);
|
statMap.set("fixId", true);
|
||||||
toggleButton("fixID-choice");
|
toggleButton("fixID-choice");
|
||||||
toggleYN("fixID-choice");
|
toggleFixed(document.getElementById("fixID-choice").classList.contains("toggleOn"));
|
||||||
toggleFixed(document.getElementById("fixID-choice"));
|
|
||||||
}
|
}
|
||||||
while (tag !== "") {
|
while (tag !== "") {
|
||||||
let id = ci_save_order[Base64.toInt(tag.slice(0,2))];
|
let id = ci_save_order[Base64.toInt(tag.slice(0,2))];
|
||||||
//console.log(tag.slice(0, 2) + ": " + id);
|
//console.log(tag.slice(0, 2) + ": " + id);
|
||||||
let len = Base64.toInt(tag.slice(2,4));
|
let len = Base64.toInt(tag.slice(2,4));
|
||||||
|
|
||||||
if (rolledIDs.includes(id)) {
|
if (rolledIDs.includes(id)) {
|
||||||
let sign = parseInt(tag.slice(4,5),10);
|
let sign = parseInt(tag.slice(4,5),10);
|
||||||
let minRoll = Base64.toInt(tag.slice(5,5+len));
|
let minRoll = Base64.toInt(tag.slice(5,5+len));
|
||||||
|
@ -350,27 +340,15 @@ function populateFields() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Changes an element's text content from yes to no or vice versa
|
|
||||||
*/
|
|
||||||
function toggleYN(elemId) {
|
|
||||||
let elem = document.getElementById(elemId);
|
|
||||||
if (elem.textContent && elem.textContent === "no") {
|
|
||||||
elem.textContent = "yes";
|
|
||||||
} else if (elem.textContent === "yes") {
|
|
||||||
elem.textContent = "no";
|
|
||||||
} else {
|
|
||||||
elem.textContent = "no";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fixed : a boolean for the state of the fixID button.
|
* @param fixed : a boolean for the state of the fixID button.
|
||||||
*/
|
*/
|
||||||
function toggleFixed(fixed) {
|
function toggleFixed() {
|
||||||
|
let fixedID_bool = document.getElementById("fixID-choice").classList.contains("toggleOn");
|
||||||
for (const id of rolledIDs) {
|
for (const id of rolledIDs) {
|
||||||
let elem = document.getElementById(id);
|
let elem = document.getElementById(id);
|
||||||
if (elem) {
|
if (elem) {
|
||||||
if (fixed.textContent === "yes") { //now fixed IDs -> go to 1 input
|
if (fixedID_bool) { //now fixed IDs -> go to 1 input
|
||||||
document.getElementById(id+"-choice-fixed-container").style = "";
|
document.getElementById(id+"-choice-fixed-container").style = "";
|
||||||
document.getElementById(id+"-choice-container").style = "display:none";
|
document.getElementById(id+"-choice-container").style = "display:none";
|
||||||
} else { //now rollable -> go to 2 inputs
|
} else { //now rollable -> go to 2 inputs
|
||||||
|
@ -392,7 +370,7 @@ function useBaseItem(elem) {
|
||||||
//Check items db.
|
//Check items db.
|
||||||
for (const [name,itemObj] of itemMap) {
|
for (const [name,itemObj] of itemMap) {
|
||||||
if (itemName === name) {
|
if (itemName === name) {
|
||||||
baseItem = expandItem(itemObj, []);
|
baseItem = expandItem(itemObj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,190 +1,299 @@
|
||||||
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.10], ["bash", 0.50] ]);
|
||||||
// Calculate spell damage given a spell elemental conversion table, and a spell multiplier.
|
|
||||||
// If spell mult is 0, its melee damage and we don't multiply by attack speed.
|
|
||||||
// externalStats should be a map
|
|
||||||
function calculateSpellDamage(stats, spellConversions, rawModifier, pctModifier, spellMultiplier, weapon, total_skillpoints, damageMultiplier, externalStats) {
|
|
||||||
let buildStats = new Map(stats);
|
|
||||||
let tooltipinfo = new Map();
|
|
||||||
//6x for damages, normal min normal max crit min crit max
|
|
||||||
let damageformulas = [["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "],["Min: = ","Max: = ","Min: = ","Max: = "]];
|
|
||||||
|
|
||||||
if(externalStats) { //if nothing is passed in, then this hopefully won't trigger
|
function get_base_dps(item) {
|
||||||
for (const entry of externalStats) {
|
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];
|
||||||
const key = entry[0];
|
//SUPER JANK @HPP PLS FIX
|
||||||
const value = entry[1];
|
if (item.get("tier") !== "Crafted") {
|
||||||
if (typeof value === "number") {
|
let total_damage = 0;
|
||||||
buildStats.set(key, buildStats.get(key) + value);
|
for (const damage_k of damage_keys) {
|
||||||
} else if (Array.isArray(value)) {
|
damages = item.get(damage_k);
|
||||||
arr = [];
|
total_damage += damages[0] + damages[1];
|
||||||
for (let j = 0; j < value.length; j++) {
|
|
||||||
arr[j] = buildStats.get(key)[j] + value[j];
|
|
||||||
}
|
|
||||||
buildStats.set(key, arr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
return total_damage * attack_speed_mult / 2;
|
||||||
|
|
||||||
let powders = weapon.get("powders").slice();
|
|
||||||
|
|
||||||
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
|
|
||||||
let damages = [];
|
|
||||||
const rawDamages = buildStats.get("damageRaw");
|
|
||||||
for (let i = 0; i < rawDamages.length; i++) {
|
|
||||||
const damage_vals = rawDamages[i].split("-").map(Number);
|
|
||||||
damages.push(damage_vals);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Applying spell conversions
|
|
||||||
let neutralBase = damages[0].slice();
|
|
||||||
let neutralRemainingRaw = damages[0].slice();
|
|
||||||
|
|
||||||
|
|
||||||
//powder application for custom crafted weapons is inherently fucked because there is no base. Unsure what to do.
|
|
||||||
|
|
||||||
//Powder application for Crafted weapons - this implementation is RIGHT YEAAAAAAAAA
|
|
||||||
//1st round - apply each as ingred, 2nd round - apply as normal
|
|
||||||
if (weapon.get("tier") === "Crafted") {
|
|
||||||
let damageBases = buildStats.get("damageBases").slice();
|
|
||||||
for (const p of powders.concat(weapon.get("ingredPowders"))) {
|
|
||||||
let powder = powderStats[p]; //use min, max, and convert
|
|
||||||
let element = Math.floor((p+0.01)/6); //[0,4], the +0.01 attempts to prevent division error
|
|
||||||
let diff = Math.floor(damageBases[0] * powder.convert/100);
|
|
||||||
damageBases[0] -= diff;
|
|
||||||
damageBases[element+1] += diff + Math.floor( (powder.min + powder.max) / 2 );
|
|
||||||
}
|
|
||||||
//update all damages
|
|
||||||
if(!weapon.get("custom")) {
|
|
||||||
for (let i = 0; i < damages.length; i++) {
|
|
||||||
damages[i] = [Math.floor(damageBases[i] * 0.9), Math.floor(damageBases[i] * 1.1)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
neutralRemainingRaw = damages[0].slice();
|
|
||||||
neutralBase = damages[0].slice();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 5; ++i) {
|
|
||||||
let conversionRatio = spellConversions[i+1]/100;
|
|
||||||
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
|
|
||||||
let max_diff = Math.min(neutralRemainingRaw[1], conversionRatio * neutralBase[1]);
|
|
||||||
damages[i+1][0] = Math.floor(round_near(damages[i+1][0] + min_diff));
|
|
||||||
damages[i+1][1] = Math.floor(round_near(damages[i+1][1] + max_diff));
|
|
||||||
neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
|
|
||||||
neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
|
|
||||||
}
|
|
||||||
|
|
||||||
//apply powders to weapon
|
|
||||||
for (const powderID of powders) {
|
|
||||||
const powder = powderStats[powderID];
|
|
||||||
// Bitwise to force conversion to integer (integer division).
|
|
||||||
const element = (powderID/6) | 0;
|
|
||||||
let conversionRatio = powder.convert/100;
|
|
||||||
if (neutralRemainingRaw[1] > 0) {
|
|
||||||
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
|
|
||||||
let max_diff = Math.min(neutralRemainingRaw[1], conversionRatio * neutralBase[1]);
|
|
||||||
damages[element+1][0] = Math.floor(round_near(damages[element+1][0] + min_diff));
|
|
||||||
damages[element+1][1] = Math.floor(round_near(damages[element+1][1] + max_diff));
|
|
||||||
neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
|
|
||||||
neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
|
|
||||||
}
|
|
||||||
damages[element+1][0] += powder.min;
|
|
||||||
damages[element+1][1] += powder.max;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//console.log(tooltipinfo);
|
|
||||||
|
|
||||||
damages[0] = neutralRemainingRaw;
|
|
||||||
tooltipinfo.set("damageBases", damages);
|
|
||||||
|
|
||||||
let damageMult = damageMultiplier;
|
|
||||||
let melee = false;
|
|
||||||
// If we are doing melee calculations:
|
|
||||||
tooltipinfo.set("dmgMult", damageMult);
|
|
||||||
if (spellMultiplier == 0) {
|
|
||||||
spellMultiplier = 1;
|
|
||||||
melee = true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tooltipinfo.set("dmgMult", `(${tooltipinfo.get("dmgMult")} * ${spellMultiplier} * ${baseDamageMultiplier[attackSpeeds.indexOf(buildStats.get("atkSpd"))]})`)
|
let total_damage_min = 0;
|
||||||
damageMult *= spellMultiplier * baseDamageMultiplier[attackSpeeds.indexOf(buildStats.get("atkSpd"))];
|
let total_damage_max = 0;
|
||||||
}
|
for (const damage_k of damage_keys) {
|
||||||
//console.log(damages);
|
damages = item.get(damage_k);
|
||||||
//console.log(damageMult);
|
total_damage_min += damages[0][0] + damages[0][1];
|
||||||
tooltipinfo.set("rawModifier", `(${rawModifier} * ${spellMultiplier} * ${damageMultiplier})`);
|
total_damage_max += damages[1][0] + damages[1][1];
|
||||||
rawModifier *= spellMultiplier * damageMultiplier;
|
|
||||||
let totalDamNorm = [0, 0];
|
|
||||||
let totalDamCrit = [0, 0];
|
|
||||||
let damages_results = [];
|
|
||||||
// 0th skillpoint is strength, 1st is dex.
|
|
||||||
let str = total_skillpoints[0];
|
|
||||||
let strBoost = 1 + skillPointsToPercentage(str);
|
|
||||||
if(!melee){
|
|
||||||
let baseDam = rawModifier * strBoost;
|
|
||||||
let baseDamCrit = rawModifier * (1 + strBoost);
|
|
||||||
totalDamNorm = [baseDam, baseDam];
|
|
||||||
totalDamCrit = [baseDamCrit, baseDamCrit];
|
|
||||||
for (let arr of damageformulas) {
|
|
||||||
arr = arr.map(x => x + " + " +tooltipinfo.get("rawModifier"));
|
|
||||||
}
|
}
|
||||||
|
total_damage_min = attack_speed_mult * total_damage_min / 2;
|
||||||
|
total_damage_max = attack_speed_mult * total_damage_max / 2;
|
||||||
|
return [total_damage_min, total_damage_max];
|
||||||
}
|
}
|
||||||
let staticBoost = (pctModifier / 100.);
|
|
||||||
tooltipinfo.set("staticBoost", `${(pctModifier/ 100.).toFixed(2)}`);
|
|
||||||
tooltipinfo.set("skillBoost",["","","","","",""]);
|
|
||||||
let skillBoost = [0];
|
|
||||||
for (let i in total_skillpoints) {
|
|
||||||
skillBoost.push(skillPointsToPercentage(total_skillpoints[i]) + buildStats.get("damageBonus")[i] / 100.);
|
|
||||||
tooltipinfo.get("skillBoost")[parseInt(i,10)+1] = `(${skillPointsToPercentage(total_skillpoints[i]).toFixed(2)} + ${(buildStats.get("damageBonus")[i]/100.).toFixed(2)})`
|
|
||||||
}
|
|
||||||
tooltipinfo.get("skillBoost")[0] = undefined;
|
|
||||||
|
|
||||||
for (let i in damages) {
|
|
||||||
let damageBoost = 1 + skillBoost[i] + staticBoost;
|
|
||||||
tooltipinfo.set("damageBoost", `(1 + ${(tooltipinfo.get("skillBoost")[i] ? tooltipinfo.get("skillBoost")[i] + " + " : "")} ${tooltipinfo.get("staticBoost")})`)
|
|
||||||
damages_results.push([
|
|
||||||
Math.max(damages[i][0] * strBoost * Math.max(damageBoost,0) * damageMult, 0), // Normal min
|
|
||||||
Math.max(damages[i][1] * strBoost * Math.max(damageBoost,0) * damageMult, 0), // Normal max
|
|
||||||
Math.max(damages[i][0] * (strBoost + 1) * Math.max(damageBoost,0) * damageMult, 0), // Crit min
|
|
||||||
Math.max(damages[i][1] * (strBoost + 1) * Math.max(damageBoost,0) * damageMult, 0), // Crit max
|
|
||||||
]);
|
|
||||||
damageformulas[i][0] += `(max((${tooltipinfo.get("damageBases")[i][0]} * ${strBoost} * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))`
|
|
||||||
damageformulas[i][1] += `(max((${tooltipinfo.get("damageBases")[i][1]} * ${strBoost} * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))`
|
|
||||||
damageformulas[i][2] += `(max((${tooltipinfo.get("damageBases")[i][0]} * ${strBoost} * 2 * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))`
|
|
||||||
damageformulas[i][3] += `(max((${tooltipinfo.get("damageBases")[i][1]} * ${strBoost} * 2 * max(${tooltipinfo.get("damageBoost")}, 0) * ${tooltipinfo.get("dmgMult")}), 0))`
|
|
||||||
totalDamNorm[0] += damages_results[i][0];
|
|
||||||
totalDamNorm[1] += damages_results[i][1];
|
|
||||||
totalDamCrit[0] += damages_results[i][2];
|
|
||||||
totalDamCrit[1] += damages_results[i][3];
|
|
||||||
}
|
|
||||||
if (melee) {
|
|
||||||
totalDamNorm[0] += Math.max(strBoost*rawModifier, -damages_results[0][0]);
|
|
||||||
totalDamNorm[1] += Math.max(strBoost*rawModifier, -damages_results[0][1]);
|
|
||||||
totalDamCrit[0] += Math.max((strBoost+1)*rawModifier, -damages_results[0][2]);
|
|
||||||
totalDamCrit[1] += Math.max((strBoost+1)*rawModifier, -damages_results[0][3]);
|
|
||||||
}
|
|
||||||
damages_results[0][0] += strBoost*rawModifier;
|
|
||||||
damages_results[0][1] += strBoost*rawModifier;
|
|
||||||
damages_results[0][2] += (strBoost + 1)*rawModifier;
|
|
||||||
damages_results[0][3] += (strBoost + 1)*rawModifier;
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
damageformulas[0][i] += ` + (${strBoost} * ${tooltipinfo.get("rawModifier")})`
|
|
||||||
}
|
|
||||||
for (let i = 2; i < 4; i++) {
|
|
||||||
damageformulas[0][i] += ` + (2 * ${strBoost} * ${tooltipinfo.get("rawModifier")})`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalDamNorm[0] < 0) totalDamNorm[0] = 0;
|
|
||||||
if (totalDamNorm[1] < 0) totalDamNorm[1] = 0;
|
|
||||||
if (totalDamCrit[0] < 0) totalDamCrit[0] = 0;
|
|
||||||
if (totalDamCrit[1] < 0) totalDamCrit[1] = 0;
|
|
||||||
|
|
||||||
tooltipinfo.set("damageformulas", damageformulas);
|
|
||||||
return [totalDamNorm, totalDamCrit, damages_results, tooltipinfo];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, ignore_speed=false) {
|
||||||
|
// TODO: Roll all the loops together maybe
|
||||||
|
|
||||||
|
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
|
||||||
|
// 1. Get weapon damage (with powders).
|
||||||
|
let weapon_damages;
|
||||||
|
if (weapon.get('tier') === 'Crafted') {
|
||||||
|
weapon_damages = damage_keys.map(x => weapon.get(x)[1]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
weapon_damages = damage_keys.map(x => weapon.get(x));
|
||||||
|
}
|
||||||
|
let present = weapon.get(damage_present_key);
|
||||||
|
|
||||||
|
// 2. Conversions.
|
||||||
|
// 2.1. First, apply neutral conversion (scale weapon damage). Keep track of total weapon damage here.
|
||||||
|
let damages = [];
|
||||||
|
const neutral_convert = conversions[0] / 100;
|
||||||
|
let weapon_min = 0;
|
||||||
|
let weapon_max = 0;
|
||||||
|
for (const damage of weapon_damages) {
|
||||||
|
let min_dmg = damage[0] * neutral_convert;
|
||||||
|
let max_dmg = damage[1] * neutral_convert;
|
||||||
|
damages.push([min_dmg, max_dmg]);
|
||||||
|
weapon_min += damage[0];
|
||||||
|
weapon_max += damage[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
for (let i = 1; i <= 5; ++i) {
|
||||||
|
if (conversions[i] > 0) {
|
||||||
|
const conv_frac = conversions[i]/100;
|
||||||
|
damages[i][0] += conv_frac * weapon_min;
|
||||||
|
damages[i][1] += conv_frac * weapon_max;
|
||||||
|
present[i] = true;
|
||||||
|
total_convert += conv_frac
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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"))];
|
||||||
|
for (let i = 0; i < 6; ++i) {
|
||||||
|
damages[i][0] *= attack_speed_mult;
|
||||||
|
damages[i][1] *= attack_speed_mult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Add additive damage. TODO: Is there separate additive damage?
|
||||||
|
for (let i = 0; i < 6; ++i) {
|
||||||
|
if (present[i]) {
|
||||||
|
damages[i][0] += stats.get(damage_elements[i]+'DamAddMin');
|
||||||
|
damages[i][1] += stats.get(damage_elements[i]+'DamAddMax');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. ID bonus.
|
||||||
|
let specific_boost_str = 'Md';
|
||||||
|
if (use_spell_damage) {
|
||||||
|
specific_boost_str = 'Sd';
|
||||||
|
}
|
||||||
|
// 5.1: %boost application
|
||||||
|
let skill_boost = [0]; // no neutral skillpoint booster
|
||||||
|
for (let i in skp_order) {
|
||||||
|
const skp = skp_order[i];
|
||||||
|
skill_boost.push(skillPointsToPercentage(stats.get(skp)) * skillpoint_damage_mult[i]);
|
||||||
|
}
|
||||||
|
let static_boost = (stats.get(specific_boost_str.toLowerCase()+'Pct') + stats.get('damPct')) / 100;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
let damageBoost = 1 + skill_boost[i] + static_boost
|
||||||
|
+ ((stats.get(damage_prefix+'Pct') + stats.get(damage_elements[i]+'DamPct')) /100);
|
||||||
|
damages[i][0] *= Math.max(damageBoost, 0);
|
||||||
|
damages[i][1] *= Math.max(damageBoost, 0);
|
||||||
|
// Collect total damage post %boost
|
||||||
|
total_min += damages[i][0];
|
||||||
|
total_max += damages[i][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_elem_min = total_min - damages[0][0];
|
||||||
|
let total_elem_max = total_max - damages[0][1];
|
||||||
|
|
||||||
|
// 5.2: Raw application.
|
||||||
|
let prop_raw = stats.get(specific_boost_str.toLowerCase()+'Raw') + stats.get('damRaw');
|
||||||
|
let rainbow_raw = stats.get('r'+specific_boost_str+'Raw') + stats.get('rDamRaw');
|
||||||
|
for (let i in damages) {
|
||||||
|
let damages_obj = damages[i];
|
||||||
|
let damage_prefix = damage_elements[i] + specific_boost_str;
|
||||||
|
// Normie raw
|
||||||
|
let raw_boost = 0;
|
||||||
|
if (present[i]) {
|
||||||
|
raw_boost += stats.get(damage_prefix+'Raw') + stats.get(damage_elements[i]+'DamRaw');
|
||||||
|
}
|
||||||
|
// Next, rainraw and propRaw
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
min_boost += (damages_obj[0] / total_elem_min) * rainbow_raw;
|
||||||
|
}
|
||||||
|
max_boost += (damages_obj[1] / total_elem_max) * rainbow_raw;
|
||||||
|
}
|
||||||
|
damages_obj[0] += min_boost * total_convert;
|
||||||
|
damages_obj[1] += max_boost * total_convert;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Strength boosters
|
||||||
|
// str/dex, as well as any other mutually multiplicative effects
|
||||||
|
let strBoost = 1 + skill_boost[1];
|
||||||
|
let total_dam_norm = [0, 0];
|
||||||
|
let total_dam_crit = [0, 0];
|
||||||
|
let damages_results = [];
|
||||||
|
const damage_mult = stats.get("damageMultiplier");
|
||||||
|
|
||||||
|
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
|
||||||
|
];
|
||||||
|
damages_results.push(res);
|
||||||
|
total_dam_norm[0] += res[0];
|
||||||
|
total_dam_norm[1] += res[1];
|
||||||
|
total_dam_crit[0] += res[2];
|
||||||
|
total_dam_crit[1] += res[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_dam_norm[0] < 0) total_dam_norm[0] = 0;
|
||||||
|
if (total_dam_norm[1] < 0) total_dam_norm[1] = 0;
|
||||||
|
if (total_dam_crit[0] < 0) total_dam_crit[0] = 0;
|
||||||
|
if (total_dam_crit[1] < 0) total_dam_crit[1] = 0;
|
||||||
|
|
||||||
|
return [total_dam_norm, total_dam_crit, damages_results];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Spell schema:
|
||||||
|
|
||||||
|
spell: {
|
||||||
|
name: str internal string name for the spell. Unique identifier, also display
|
||||||
|
cost: Optional[int] ignored for spells that are not id 1-4
|
||||||
|
base_spell: int spell index. 0-4 are reserved (0 is melee, 1-4 is common 4 spells)
|
||||||
|
spell_type: str [TODO: DEPRECATED/REMOVE] "healing" or "damage"
|
||||||
|
scaling: Optional[str] [DEFAULT: "spell"] "melee" or "spell"
|
||||||
|
use_atkspd: Optional[bool] [DEFAULT: true] true to factor attack speed, false otherwise.
|
||||||
|
display: Optional[str] [DEFAULT: "total"] "total" to sum all parts. Or, the name of a spell part
|
||||||
|
parts: List[part] Parts of this spell (different stuff the spell does basically)
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTE: when using `replace_spell` on an existing spell, all fields become optional.
|
||||||
|
Specified fields overwrite existing fields; unspecified fields are left unchanged.
|
||||||
|
|
||||||
|
|
||||||
|
There are three possible spell "part" types: damage, heal, and total.
|
||||||
|
|
||||||
|
part: spell_damage | spell_heal | spell_total
|
||||||
|
|
||||||
|
spell_damage: {
|
||||||
|
name: str != "total" Name of the part.
|
||||||
|
type: "damage" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
|
||||||
|
multipliers: array[num, 6] floating point spellmults (though supposedly wynn only supports integer mults)
|
||||||
|
}
|
||||||
|
spell_heal: {
|
||||||
|
name: str != "total" Name of the part.
|
||||||
|
type: "heal" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
|
||||||
|
power: num floating point healing power (1 is 100% of max hp).
|
||||||
|
}
|
||||||
|
spell_total: {
|
||||||
|
name: str != "total" Name of the part.
|
||||||
|
type: "total" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
|
||||||
|
hits: Map[str, num] Keys are other part names, numbers are the multipliers. Undefined behavior if subparts
|
||||||
|
are not the same type of spell. Can only pull from spells defined before it.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Before passing to display, use the following structs.
|
||||||
|
NOTE: total is collapsed into damage or healing.
|
||||||
|
|
||||||
|
spell_damage: {
|
||||||
|
type: "damage" Internal use
|
||||||
|
name: str Display name of part. Should be human readable
|
||||||
|
normal_min: array[num, 6] floating point damages (no crit, min), can be less than zero. Order: NETWFA
|
||||||
|
normal_max: array[num, 6] floating point damages (no crit, max)
|
||||||
|
normal_total: array[num, 2] (min, max) noncrit total damage (not negative)
|
||||||
|
crit_min: array[num, 6] floating point damages (crit, min), can be less than zero. Order: NETWFA
|
||||||
|
crit_max: array[num, 6] floating point damages (crit, max)
|
||||||
|
crit_total: array[num, 2] (min, max) crit total damage (not negative)
|
||||||
|
}
|
||||||
|
spell_heal: {
|
||||||
|
type: "heal" Internal use
|
||||||
|
name: str Display name of part. Should be human readable
|
||||||
|
heal_amount: num floating point HP healed (self)
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const default_spells = {
|
||||||
|
wand: [{
|
||||||
|
type: "replace_spell", // not needed but makes this usable as an "abil part"
|
||||||
|
name: "Wand Melee", // TODO: name for melee attacks?
|
||||||
|
base_spell: 0,
|
||||||
|
scaling: "melee", use_atkspd: false,
|
||||||
|
display: "Melee",
|
||||||
|
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
|
||||||
|
}, {
|
||||||
|
name: "Heal", // TODO: name for melee attacks? // JUST FOR TESTING...
|
||||||
|
base_spell: 1,
|
||||||
|
display: "Total Heal",
|
||||||
|
parts: [
|
||||||
|
{ name: "First Pulse", power: 0.12 },
|
||||||
|
{ name: "Second and Third Pulses", power: 0.06 },
|
||||||
|
{ name: "Total Heal", hits: { "First Pulse": 1, "Second and Third Pulses": 2 } }
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
spear: [{
|
||||||
|
type: "replace_spell", // not needed but makes this usable as an "abil part"
|
||||||
|
name: "Melee", // TODO: name for melee attacks?
|
||||||
|
base_spell: 0,
|
||||||
|
scaling: "melee", use_atkspd: false,
|
||||||
|
display: "Melee",
|
||||||
|
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
|
||||||
|
}],
|
||||||
|
bow: [{
|
||||||
|
type: "replace_spell", // not needed but makes this usable as an "abil part"
|
||||||
|
name: "Bow Shot", // TODO: name for melee attacks?
|
||||||
|
base_spell: 0,
|
||||||
|
scaling: "melee", use_atkspd: false,
|
||||||
|
display: "Single Shot",
|
||||||
|
parts: [{ name: "Single Shot", multipliers: [100, 0, 0, 0, 0, 0] }]
|
||||||
|
}],
|
||||||
|
dagger: [{
|
||||||
|
type: "replace_spell", // not needed but makes this usable as an "abil part"
|
||||||
|
name: "Melee", // TODO: name for melee attacks?
|
||||||
|
base_spell: 0,
|
||||||
|
scaling: "melee", use_atkspd: false,
|
||||||
|
display: "Melee",
|
||||||
|
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
|
||||||
|
}],
|
||||||
|
relik: [{
|
||||||
|
type: "replace_spell", // not needed but makes this usable as an "abil part"
|
||||||
|
name: "Relik Melee", // TODO: name for melee attacks?
|
||||||
|
base_spell: 0,
|
||||||
|
spell_type: "damage",
|
||||||
|
scaling: "melee", use_atkspd: false,
|
||||||
|
display: "Total",
|
||||||
|
parts: [
|
||||||
|
{ name: "Single Beam", multipliers: [33, 0, 0, 0, 0, 0] },
|
||||||
|
{ name: "Total", hits: { "Single Beam": 3 } }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
const spell_table = {
|
const spell_table = {
|
||||||
"wand": [
|
"wand": [
|
||||||
|
|
67
js/dev.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
function init_dev() {
|
||||||
|
let sections = document.getElementsByClassName("section");
|
||||||
|
|
||||||
|
for (const section of sections) {
|
||||||
|
//so clicking works
|
||||||
|
section.classList.add("down");
|
||||||
|
|
||||||
|
//add title and toggle character
|
||||||
|
let title_row = document.createElement("div");
|
||||||
|
title_row.classList.add("row", "section-title");
|
||||||
|
let title = document.createElement("div");
|
||||||
|
title.classList.add("col");
|
||||||
|
title.textContent = section.title ? section.title : "";
|
||||||
|
title_row.appendChild(title);
|
||||||
|
section.insertBefore(title_row, section.firstChild);
|
||||||
|
|
||||||
|
let toggle_char = document.createElement("div");
|
||||||
|
toggle_char.classList.add("col-auto", "arrow");
|
||||||
|
toggle_char.textContent = "V";
|
||||||
|
title_row.appendChild(toggle_char);
|
||||||
|
title_row.addEventListener("click", (event) =>
|
||||||
|
{
|
||||||
|
toggleSection(section);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
for (const child of section.children) {
|
||||||
|
if (!child.classList.contains("section-title")) {
|
||||||
|
child.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Toggles section content as well as up and down arrow
|
||||||
|
@params:
|
||||||
|
*/
|
||||||
|
function toggleSection(section) {
|
||||||
|
//has down arrow (default state)
|
||||||
|
let down = section.classList.contains("down");
|
||||||
|
let arrow_elem = section.getElementsByClassName("arrow")[0];
|
||||||
|
|
||||||
|
if (down) {
|
||||||
|
section.classList.remove("down");
|
||||||
|
section.classList.add("up");
|
||||||
|
arrow_elem.style.transform = 'rotate(180deg)';
|
||||||
|
|
||||||
|
for (const elem of section.children) {
|
||||||
|
if (!elem.classList.contains("section-title")) {
|
||||||
|
elem.style.display = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
section.classList.remove("up");
|
||||||
|
section.classList.add("down");
|
||||||
|
arrow_elem.style.transform = 'rotate(0deg)';
|
||||||
|
for (const elem of section.children) {
|
||||||
|
if (!elem.classList.contains("section-title")) {
|
||||||
|
elem.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
init_dev();
|
1797
js/display.js
|
@ -1,99 +1,3 @@
|
||||||
let nonRolledIDs = [
|
|
||||||
"name",
|
|
||||||
"lore",
|
|
||||||
"displayName",
|
|
||||||
"tier",
|
|
||||||
"set",
|
|
||||||
"slots",
|
|
||||||
"type",
|
|
||||||
"material",
|
|
||||||
"drop",
|
|
||||||
"quest",
|
|
||||||
"restrict",
|
|
||||||
"nDam",
|
|
||||||
"fDam",
|
|
||||||
"wDam",
|
|
||||||
"aDam",
|
|
||||||
"tDam",
|
|
||||||
"eDam",
|
|
||||||
"atkSpd",
|
|
||||||
"hp",
|
|
||||||
"fDef",
|
|
||||||
"wDef",
|
|
||||||
"aDef",
|
|
||||||
"tDef",
|
|
||||||
"eDef",
|
|
||||||
"lvl",
|
|
||||||
"classReq",
|
|
||||||
"strReq",
|
|
||||||
"dexReq",
|
|
||||||
"intReq",
|
|
||||||
"defReq",
|
|
||||||
"agiReq","str",
|
|
||||||
"dex",
|
|
||||||
"int",
|
|
||||||
"agi",
|
|
||||||
"def",
|
|
||||||
"fixID",
|
|
||||||
"category",
|
|
||||||
"id",
|
|
||||||
"skillpoints",
|
|
||||||
"reqs",
|
|
||||||
"nDam_",
|
|
||||||
"fDam_",
|
|
||||||
"wDam_",
|
|
||||||
"aDam_",
|
|
||||||
"tDam_",
|
|
||||||
"eDam_",
|
|
||||||
"majorIds"];
|
|
||||||
let rolledIDs = [
|
|
||||||
"hprPct",
|
|
||||||
"mr",
|
|
||||||
"sdPct",
|
|
||||||
"mdPct",
|
|
||||||
"ls",
|
|
||||||
"ms",
|
|
||||||
"xpb",
|
|
||||||
"lb",
|
|
||||||
"ref",
|
|
||||||
"thorns",
|
|
||||||
"expd",
|
|
||||||
"spd",
|
|
||||||
"atkTier",
|
|
||||||
"poison",
|
|
||||||
"hpBonus",
|
|
||||||
"spRegen",
|
|
||||||
"eSteal",
|
|
||||||
"hprRaw",
|
|
||||||
"sdRaw",
|
|
||||||
"mdRaw",
|
|
||||||
"fDamPct",
|
|
||||||
"wDamPct",
|
|
||||||
"aDamPct",
|
|
||||||
"tDamPct",
|
|
||||||
"eDamPct",
|
|
||||||
"fDefPct",
|
|
||||||
"wDefPct",
|
|
||||||
"aDefPct",
|
|
||||||
"tDefPct",
|
|
||||||
"eDefPct",
|
|
||||||
"spPct1",
|
|
||||||
"spRaw1",
|
|
||||||
"spPct2",
|
|
||||||
"spRaw2",
|
|
||||||
"spPct3",
|
|
||||||
"spRaw3",
|
|
||||||
"spPct4",
|
|
||||||
"spRaw4",
|
|
||||||
"rainbowRaw",
|
|
||||||
"sprint",
|
|
||||||
"sprintReg",
|
|
||||||
"jh",
|
|
||||||
"lq",
|
|
||||||
"gXp",
|
|
||||||
"gSpd"
|
|
||||||
];
|
|
||||||
let reversedIDs = [ "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ];
|
|
||||||
let colorMap = new Map(
|
let colorMap = new Map(
|
||||||
[
|
[
|
||||||
["Normal", "#fff"],
|
["Normal", "#fff"],
|
||||||
|
@ -305,23 +209,24 @@ let posModSuffixes = {
|
||||||
/*
|
/*
|
||||||
* Display commands
|
* Display commands
|
||||||
*/
|
*/
|
||||||
let build_overall_display_commands = [
|
let build_all_display_commands = [
|
||||||
"#table",
|
"#defense-stats",
|
||||||
"str", "dex", "int", "def", "agi",
|
"str", "dex", "int", "def", "agi",
|
||||||
|
"!spacer",
|
||||||
"mr", "ms",
|
"mr", "ms",
|
||||||
"hprRaw", "hprPct",
|
"hprRaw", "hprPct",
|
||||||
|
"ls",
|
||||||
"sdRaw", "sdPct",
|
"sdRaw", "sdPct",
|
||||||
"mdRaw", "mdPct",
|
"mdRaw", "mdPct",
|
||||||
"ref", "thorns",
|
|
||||||
"ls",
|
|
||||||
"poison",
|
|
||||||
"expd",
|
|
||||||
"spd",
|
|
||||||
"atkTier",
|
|
||||||
"!elemental",
|
"!elemental",
|
||||||
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
|
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
|
||||||
"!elemental",
|
"!elemental",
|
||||||
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
|
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
|
||||||
|
"atkTier",
|
||||||
|
"poison",
|
||||||
|
"ref", "thorns",
|
||||||
|
"expd",
|
||||||
|
"spd",
|
||||||
"rainbowRaw",
|
"rainbowRaw",
|
||||||
"sprint", "sprintReg",
|
"sprint", "sprintReg",
|
||||||
"jh",
|
"jh",
|
||||||
|
@ -331,26 +236,51 @@ let build_overall_display_commands = [
|
||||||
"gXp", "gSpd",
|
"gXp", "gSpd",
|
||||||
];
|
];
|
||||||
|
|
||||||
let item_display_commands = [
|
let build_offensive_display_commands = [
|
||||||
"#cdiv",
|
"str", "dex", "int", "def", "agi",
|
||||||
|
"mr", "ms",
|
||||||
|
"sdRaw", "sdPct",
|
||||||
|
"mdRaw", "mdPct",
|
||||||
|
"ref", "thorns",
|
||||||
|
"ls",
|
||||||
|
"poison",
|
||||||
|
"expd",
|
||||||
|
"spd",
|
||||||
|
"atkTier",
|
||||||
|
"rainbowRaw",
|
||||||
|
"!elemental",
|
||||||
|
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
|
||||||
|
"!elemental",
|
||||||
|
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
|
||||||
|
];
|
||||||
|
|
||||||
|
let build_basic_display_commands = [
|
||||||
|
'#defense-stats',
|
||||||
|
// defense stats [hp, ehp, hpr, ]
|
||||||
|
// "sPot", // base * atkspd + spell raws
|
||||||
|
// melee potential
|
||||||
|
// "mPot", // melee% * (base * atkspd) + melee raws
|
||||||
|
"mr", "ms",
|
||||||
|
"ls",
|
||||||
|
"poison",
|
||||||
|
"spd",
|
||||||
|
"atkTier",
|
||||||
|
]
|
||||||
|
|
||||||
|
let sq2_item_display_commands = [
|
||||||
"displayName",
|
"displayName",
|
||||||
//"type", //REPLACE THIS WITH SKIN
|
|
||||||
"#ldiv",
|
|
||||||
"atkSpd",
|
"atkSpd",
|
||||||
"#ldiv",
|
|
||||||
"!elemental",
|
"!elemental",
|
||||||
"hp",
|
"hp",
|
||||||
"nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_",
|
"nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_",
|
||||||
|
"!spacer",
|
||||||
"fDef", "wDef", "aDef", "tDef", "eDef",
|
"fDef", "wDef", "aDef", "tDef", "eDef",
|
||||||
"!elemental",
|
"!elemental",
|
||||||
"#ldiv",
|
|
||||||
"classReq",
|
"classReq",
|
||||||
"lvl",
|
"lvl",
|
||||||
"strReq", "dexReq", "intReq", "defReq","agiReq",
|
"strReq", "dexReq", "intReq", "defReq","agiReq",
|
||||||
"#ldiv",
|
"!spacer",
|
||||||
"str", "dex", "int", "def", "agi",
|
"str", "dex", "int", "def", "agi",
|
||||||
"#table",
|
|
||||||
"str", "dex", "int", "def", "agi", //jank lmao
|
|
||||||
"hpBonus",
|
"hpBonus",
|
||||||
"hprRaw", "hprPct",
|
"hprRaw", "hprPct",
|
||||||
"sdRaw", "sdPct",
|
"sdRaw", "sdPct",
|
||||||
|
@ -374,11 +304,33 @@ let item_display_commands = [
|
||||||
"spRegen",
|
"spRegen",
|
||||||
"eSteal",
|
"eSteal",
|
||||||
"gXp", "gSpd",
|
"gXp", "gSpd",
|
||||||
"#ldiv",
|
|
||||||
"majorIds",
|
"majorIds",
|
||||||
|
"!spacer",
|
||||||
"slots",
|
"slots",
|
||||||
|
"!spacer",
|
||||||
"set",
|
"set",
|
||||||
"lore",
|
"lore",
|
||||||
"quest",
|
"quest",
|
||||||
"restrict"
|
"restrict"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let sq2_ing_display_order = [
|
||||||
|
"displayName", //tier will be displayed w/ name
|
||||||
|
"!spacer",
|
||||||
|
"ids",
|
||||||
|
"!spacer",
|
||||||
|
"posMods",
|
||||||
|
"itemIDs",
|
||||||
|
"consumableIDs",
|
||||||
|
"!spacer",
|
||||||
|
"lvl",
|
||||||
|
"skills",
|
||||||
|
]
|
||||||
|
|
||||||
|
let elem_colors = [
|
||||||
|
"#00AA00",
|
||||||
|
"#FFFF55",
|
||||||
|
"#55FFFF",
|
||||||
|
"#FF5555",
|
||||||
|
"#FFFFFF"
|
||||||
|
]
|
||||||
|
|
|
@ -213,14 +213,14 @@ function redraw(data) {
|
||||||
let tier_mod = tiers_mod.get(tier);
|
let tier_mod = tiers_mod.get(tier);
|
||||||
let y_max = baseline_y.map(x => 2.1*x*tier_mod*type_mod);
|
let y_max = baseline_y.map(x => 2.1*x*tier_mod*type_mod);
|
||||||
let y_min = baseline_y.map(x => 2.0*x*tier_mod*type_mod);
|
let y_min = baseline_y.map(x => 2.0*x*tier_mod*type_mod);
|
||||||
line_top.datum(zip(baseline_x, y_max))
|
line_top.datum(zip2(baseline_x, y_max))
|
||||||
.attr("fill", "none")
|
.attr("fill", "none")
|
||||||
.attr("stroke", d => colorMap.get(tier))
|
.attr("stroke", d => colorMap.get(tier))
|
||||||
.attr("d", d3.line()
|
.attr("d", d3.line()
|
||||||
.x(function(d) { return x(d[0]) })
|
.x(function(d) { return x(d[0]) })
|
||||||
.y(function(d) { return y(d[1]) })
|
.y(function(d) { return y(d[1]) })
|
||||||
)
|
)
|
||||||
line_bot.datum(zip(baseline_x, y_min))
|
line_bot.datum(zip2(baseline_x, y_min))
|
||||||
.attr("fill", "none")
|
.attr("fill", "none")
|
||||||
.attr("stroke", d => colorMap.get(tier))
|
.attr("stroke", d => colorMap.get(tier))
|
||||||
.attr("d", d3.line()
|
.attr("d", d3.line()
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
//which icons to use
|
//which icons to use
|
||||||
let window_storage = window.localStorage;
|
let window_storage = window.localStorage;
|
||||||
console.log(window_storage);
|
|
||||||
icon_state_stored = window_storage.getItem("newicons");
|
icon_state_stored = window_storage.getItem("newicons");
|
||||||
newIcons = true;
|
newIcons = true;
|
||||||
if (icon_state_stored === "false") {toggleIcons()}
|
if (icon_state_stored === "false") {toggleIcons()}
|
||||||
|
|
||||||
//REMOVE THIS IN THE REAL VERSION 7 OR SOMETHING
|
|
||||||
window_storage.removeItem("rick");
|
|
||||||
|
|
||||||
/** Toggle icons on the ENTIRE page.
|
/** Toggle icons on the ENTIRE page.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
21
js/item.js
|
@ -10,13 +10,6 @@ const item_url_tag = location.hash.slice(1);
|
||||||
|
|
||||||
const ITEM_BUILD_VERSION = "7.0.1";
|
const ITEM_BUILD_VERSION = "7.0.1";
|
||||||
|
|
||||||
function setTitle() {
|
|
||||||
let text = "WynnInfo version "+ITEM_BUILD_VERSION;
|
|
||||||
document.getElementById("header").classList.add("funnynumber");
|
|
||||||
document.getElementById("header").textContent = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle();
|
|
||||||
|
|
||||||
let item;
|
let item;
|
||||||
let amp_state = 0; //the level of corkian map used for ID purposes. Default 0.
|
let amp_state = 0; //the level of corkian map used for ID purposes. Default 0.
|
||||||
|
@ -27,16 +20,17 @@ function init_itempage() {
|
||||||
//displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view");
|
//displayExpandedItem(expandItem(itemMap.get(item_url_tag).statMap, []), "item-view");
|
||||||
try{
|
try{
|
||||||
item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []);
|
item = expandItem(itemMap.get(item_url_tag.replaceAll("%20"," ")), []);
|
||||||
displayExpandedItem(item, "item-view");
|
displaysq2ExpandedItem(item, "item-view");
|
||||||
displayAdditionalInfo("additional-info", item);
|
displaysq2AdditionalInfo("additional-info", item);
|
||||||
displayIDCosts("identification-costs", item);
|
displaysq2IDCosts("identification-costs", item);
|
||||||
if (item.get("set") && sets[item.get("set")]) {
|
if (item.get("set") && sets[item.get("set")]) {
|
||||||
displayAllSetBonuses("set-bonus-info",item.get("set"));
|
displaysq2AllSetBonuses("set-bonus-info",item.get("set"));
|
||||||
}
|
}
|
||||||
console.log(item);
|
console.log(item);
|
||||||
displayIDProbabilities("identification-probabilities", item, amp_state);
|
displaysq2IDProbabilities("identification-probabilities", item, amp_state);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
console.log(error.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +39,6 @@ function init_itempage() {
|
||||||
* @param {Number} button_id the ID of the button just pressed.
|
* @param {Number} button_id the ID of the button just pressed.
|
||||||
*/
|
*/
|
||||||
function toggleAmps(button_id) {
|
function toggleAmps(button_id) {
|
||||||
console.log(button_id);
|
|
||||||
amp_state = 0;
|
amp_state = 0;
|
||||||
if (button_id == 0) {return;}
|
if (button_id == 0) {return;}
|
||||||
else {
|
else {
|
||||||
|
@ -59,7 +52,7 @@ function toggleAmps(button_id) {
|
||||||
amp_state = button_id;
|
amp_state = button_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
displayIDProbabilities("identification-probabilities", item, amp_state);
|
displaysq2IDProbabilities("identification-probabilities", item, amp_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
29
js/items.js
|
@ -103,15 +103,17 @@ const special_mappings = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let itemFilters = document.getElementById("filter-items");
|
let itemFilters = document.getElementById("filter-items");
|
||||||
for (let x in translate_mappings) {
|
if (itemFilters) {
|
||||||
let el = document.createElement("option");
|
for (let x in translate_mappings) {
|
||||||
el.value = x;
|
let el = document.createElement("option");
|
||||||
itemFilters.appendChild(el);
|
el.value = x;
|
||||||
}
|
itemFilters.appendChild(el);
|
||||||
for (let x in special_mappings) {
|
}
|
||||||
let el = document.createElement("option");
|
for (let x in special_mappings) {
|
||||||
el.value = x;
|
let el = document.createElement("option");
|
||||||
itemFilters.appendChild(el);
|
el.value = x;
|
||||||
|
itemFilters.appendChild(el);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let itemCategories = [ "armor", "accessory", "weapon" ];
|
let itemCategories = [ "armor", "accessory", "weapon" ];
|
||||||
|
@ -137,9 +139,9 @@ let searchDb;
|
||||||
function doItemSearch() {
|
function doItemSearch() {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
let queries = [];
|
let queries = [];
|
||||||
queries.push('f:name?="'+document.getElementById("name-choice").value.trim()+'"');
|
queries.push('f:name?="'+document.getElementById("item-name-choice").value.trim()+'"');
|
||||||
|
|
||||||
let categoryOrType = document.getElementById("category-choice").value;
|
let categoryOrType = document.getElementById("item-category-choice").value;
|
||||||
if (itemTypes.includes(categoryOrType)) {
|
if (itemTypes.includes(categoryOrType)) {
|
||||||
queries.push('f:type="'+categoryOrType+'"');
|
queries.push('f:type="'+categoryOrType+'"');
|
||||||
}
|
}
|
||||||
|
@ -147,7 +149,7 @@ function doItemSearch() {
|
||||||
queries.push('f:cat="'+categoryOrType+'"');
|
queries.push('f:cat="'+categoryOrType+'"');
|
||||||
}
|
}
|
||||||
|
|
||||||
let rarity = document.getElementById("rarity-choice").value;
|
let rarity = document.getElementById("item-rarity-choice").value;
|
||||||
if (rarity) {
|
if (rarity) {
|
||||||
if (rarity === "ANY") {
|
if (rarity === "ANY") {
|
||||||
|
|
||||||
|
@ -160,7 +162,7 @@ function doItemSearch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let level_dat = document.getElementById("level-choice").value.split("-");
|
let level_dat = document.getElementById("item-level-choice").value.split("-");
|
||||||
queries.push('f:(lvl>='+parseInt(level_dat[0])+'&lvl<='+parseInt(level_dat[1])+')');
|
queries.push('f:(lvl>='+parseInt(level_dat[0])+'&lvl<='+parseInt(level_dat[1])+')');
|
||||||
|
|
||||||
for (let i = 1; i <= 4; ++i) {
|
for (let i = 1; i <= 4; ++i) {
|
||||||
|
@ -178,7 +180,6 @@ function doItemSearch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("main").textContent = "";
|
|
||||||
let filterQuery = "true";
|
let filterQuery = "true";
|
||||||
let sortQueries = [];
|
let sortQueries = [];
|
||||||
console.log(queries);
|
console.log(queries);
|
||||||
|
|
|
@ -239,17 +239,22 @@ function init_items2() {
|
||||||
const itemListFooter = document.getElementById('item-list-footer');
|
const itemListFooter = document.getElementById('item-list-footer');
|
||||||
|
|
||||||
// compile the search db from the item db
|
// compile the search db from the item db
|
||||||
const searchDb = items.filter(i => !i.remapID).map(i => [i, expandItem(i, [])]);
|
const searchDb = items.filter(i => !i.remapID).map(i => [i, expandItem(i)]);
|
||||||
|
|
||||||
// init item list elements
|
// init item list elements
|
||||||
const ITEM_LIST_SIZE = 64;
|
const ITEM_LIST_SIZE = 64;
|
||||||
const itemEntries = [];
|
const itemEntries = [];
|
||||||
for (let i = 0; i < ITEM_LIST_SIZE; i++) {
|
for (let i = 0; i < ITEM_LIST_SIZE; i++) {
|
||||||
const itemElem = document.createElement('div');
|
const itemElem = document.createElement('div');
|
||||||
itemElem.classList.add('item-entry', 'box');
|
itemElem.classList.add('col-lg-3', 'col-sm-auto', "p-2");
|
||||||
itemElem.setAttribute('id', `item-entry-${i}`);
|
// itemElem.setAttribute('id', `item-entry-${i}`);
|
||||||
itemList.append(itemElem);
|
itemList.append(itemElem);
|
||||||
itemEntries.push(itemElem);
|
itemEntries.push(itemElem);
|
||||||
|
|
||||||
|
const itemElemContained = document.createElement("div");
|
||||||
|
itemElemContained.classList.add("dark-7", "rounded", "px-2", "col-auto");
|
||||||
|
itemElemContained.setAttribute('id', `item-entry-${i}`);
|
||||||
|
itemElem.appendChild(itemElemContained);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the expression parser
|
// create the expression parser
|
||||||
|
@ -323,10 +328,10 @@ function init_items2() {
|
||||||
for (let i = 0; i < searchMax; i++) {
|
for (let i = 0; i < searchMax; i++) {
|
||||||
const result = searchResults[i];
|
const result = searchResults[i];
|
||||||
itemEntries[i].classList.add('visible');
|
itemEntries[i].classList.add('visible');
|
||||||
displayExpandedItem(result.itemExp, `item-entry-${i}`);
|
displaysq2ExpandedItem(result.itemExp, `item-entry-${i}`);
|
||||||
if (result.sortKeys.length > 0) {
|
if (result.sortKeys.length > 0) {
|
||||||
const sortKeyListContainer = document.createElement('div');
|
const sortKeyListContainer = document.createElement('div');
|
||||||
sortKeyListContainer.classList.add('itemleft');
|
sortKeyListContainer.classList.add('row');
|
||||||
const sortKeyList = document.createElement('ul');
|
const sortKeyList = document.createElement('ul');
|
||||||
sortKeyList.classList.add('item-entry-sort-key', 'itemp', 'T0');
|
sortKeyList.classList.add('item-entry-sort-key', 'itemp', 'T0');
|
||||||
sortKeyListContainer.append(sortKeyList);
|
sortKeyListContainer.append(sortKeyList);
|
||||||
|
|
265
js/load.js
|
@ -1,4 +1,4 @@
|
||||||
const DB_VERSION = 90;
|
const DB_VERSION = 93;
|
||||||
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
||||||
|
|
||||||
let db;
|
let db;
|
||||||
|
@ -6,7 +6,7 @@ let reload = false;
|
||||||
let load_complete = false;
|
let load_complete = false;
|
||||||
let load_in_progress = false;
|
let load_in_progress = false;
|
||||||
let items;
|
let items;
|
||||||
let sets;
|
let sets = new Map();
|
||||||
let itemMap;
|
let itemMap;
|
||||||
let idMap;
|
let idMap;
|
||||||
let redirectMap;
|
let redirectMap;
|
||||||
|
@ -14,42 +14,46 @@ let itemLists = new Map();
|
||||||
/*
|
/*
|
||||||
* Load item set from local DB. Calls init() on success.
|
* Load item set from local DB. Calls init() on success.
|
||||||
*/
|
*/
|
||||||
async function load_local(init_func) {
|
async function load_local() {
|
||||||
let get_tx = db.transaction(['item_db', 'set_db'], 'readonly');
|
return new Promise(function(resolve, reject) {
|
||||||
let sets_store = get_tx.objectStore('set_db');
|
let get_tx = db.transaction(['item_db', 'set_db'], 'readonly');
|
||||||
let get_store = get_tx.objectStore('item_db');
|
let sets_store = get_tx.objectStore('set_db');
|
||||||
let request = get_store.getAll();
|
let get_store = get_tx.objectStore('item_db');
|
||||||
request.onerror = function(event) {
|
let request = get_store.getAll();
|
||||||
console.log("Could not read local item db...");
|
request.onerror = function(event) {
|
||||||
}
|
reject("Could not read local item db...");
|
||||||
request.onsuccess = function(event) {
|
}
|
||||||
console.log("Successfully read local item db.");
|
request.onsuccess = function(event) {
|
||||||
items = request.result;
|
console.log("Successfully read local item db.");
|
||||||
//console.log(items);
|
|
||||||
let request2 = sets_store.openCursor();
|
|
||||||
|
|
||||||
sets = {};
|
|
||||||
request2.onerror = function(event) {
|
|
||||||
console.log("Could not read local set db...");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// key-value iteration (hpp don't break this again)
|
||||||
|
// https://stackoverflow.com/questions/47931595/indexeddb-getting-all-data-with-keys
|
||||||
|
let request2 = sets_store.openCursor();
|
||||||
|
request2.onerror = function(event) {
|
||||||
|
reject("Could not read local set db...");
|
||||||
|
}
|
||||||
request2.onsuccess = function(event) {
|
request2.onsuccess = function(event) {
|
||||||
let cursor = event.target.result;
|
let cursor = event.target.result;
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
sets[cursor.primaryKey] = cursor.value;
|
let key = cursor.primaryKey;
|
||||||
|
let value = cursor.value;
|
||||||
|
sets.set(key, value);
|
||||||
cursor.continue();
|
cursor.continue();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// no more results
|
||||||
console.log("Successfully read local set db.");
|
console.log("Successfully read local set db.");
|
||||||
//console.log(sets);
|
|
||||||
init_maps();
|
|
||||||
init_func();
|
|
||||||
load_complete = true;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
get_tx.oncomplete = function(event) {
|
||||||
|
items = request.result;
|
||||||
|
init_maps();
|
||||||
|
load_complete = true;
|
||||||
|
db.close();
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
await get_tx.complete;
|
|
||||||
db.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -91,7 +95,7 @@ function clean_item(item) {
|
||||||
/*
|
/*
|
||||||
* Load item set from remote DB (aka a big json file). Calls init() on success.
|
* Load item set from remote DB (aka a big json file). Calls init() on success.
|
||||||
*/
|
*/
|
||||||
async function load(init_func) {
|
async function load() {
|
||||||
|
|
||||||
let getUrl = window.location;
|
let getUrl = window.location;
|
||||||
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1];
|
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1];
|
||||||
|
@ -99,18 +103,8 @@ async function load(init_func) {
|
||||||
let url = baseUrl + "/compress.json?"+new Date();
|
let url = baseUrl + "/compress.json?"+new Date();
|
||||||
let result = await (await fetch(url)).json();
|
let result = await (await fetch(url)).json();
|
||||||
items = result.items;
|
items = result.items;
|
||||||
sets = result.sets;
|
let sets_ = result.sets;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// let clear_tx = db.transaction(['item_db', 'set_db'], 'readwrite');
|
|
||||||
// let clear_items = clear_tx.objectStore('item_db');
|
|
||||||
// let clear_sets = clear_tx.objectStore('item_db');
|
|
||||||
//
|
|
||||||
// await clear_items.clear();
|
|
||||||
// await clear_sets.clear();
|
|
||||||
// await clear_tx.complete;
|
|
||||||
|
|
||||||
let add_tx = db.transaction(['item_db', 'set_db'], 'readwrite');
|
let add_tx = db.transaction(['item_db', 'set_db'], 'readwrite');
|
||||||
add_tx.onabort = function(e) {
|
add_tx.onabort = function(e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
@ -127,78 +121,114 @@ async function load(init_func) {
|
||||||
add_promises.push(req);
|
add_promises.push(req);
|
||||||
}
|
}
|
||||||
let sets_store = add_tx.objectStore('set_db');
|
let sets_store = add_tx.objectStore('set_db');
|
||||||
for (const set in sets) {
|
for (const set in sets_) {
|
||||||
add_promises.push(sets_store.add(sets[set], set));
|
add_promises.push(sets_store.add(sets_[set], set));
|
||||||
|
sets.set(set, sets_[set]);
|
||||||
}
|
}
|
||||||
add_promises.push(add_tx.complete);
|
add_promises.push(add_tx.complete);
|
||||||
Promise.all(add_promises).then((values) => {
|
|
||||||
init_maps();
|
await Promise.all(add_promises);
|
||||||
init_func();
|
init_maps();
|
||||||
load_complete = true;
|
load_complete = true;
|
||||||
});
|
db.close();
|
||||||
// DB not closed? idfk man
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_init(init_func) {
|
async function load_init() {
|
||||||
if (load_complete) {
|
return new Promise((resolve, reject) => {
|
||||||
console.log("Item db already loaded, skipping load sequence");
|
let request = window.indexedDB.open('item_db', DB_VERSION);
|
||||||
init_func();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let request = window.indexedDB.open('item_db', DB_VERSION);
|
|
||||||
|
|
||||||
request.onerror = function() {
|
request.onerror = function() {
|
||||||
console.log("DB failed to open...");
|
reject("DB failed to open...");
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = function() {
|
request.onsuccess = async function() {
|
||||||
(async function() {
|
|
||||||
db = request.result;
|
db = request.result;
|
||||||
if (!reload) {
|
if (load_in_progress) {
|
||||||
console.log("Using stored data...")
|
while (!load_complete) {
|
||||||
load_local(init_func);
|
await sleep(100);
|
||||||
|
}
|
||||||
|
console.log("Skipping load...")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (load_in_progress) {
|
load_in_progress = true
|
||||||
while (!load_complete) {
|
if (reload) {
|
||||||
await sleep(100);
|
console.log("Using new data...")
|
||||||
}
|
await load();
|
||||||
console.log("Skipping load...")
|
|
||||||
init_func();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Not 100% safe... whatever!
|
console.log("Using stored data...")
|
||||||
load_in_progress = true
|
await load_local();
|
||||||
console.log("Using new data...")
|
|
||||||
load(init_func);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
resolve();
|
||||||
}
|
};
|
||||||
|
|
||||||
request.onupgradeneeded = function(e) {
|
request.onupgradeneeded = function(e) {
|
||||||
reload = true;
|
reload = true;
|
||||||
|
|
||||||
let db = e.target.result;
|
let db = e.target.result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.deleteObjectStore('item_db');
|
db.deleteObjectStore('item_db');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log("Could not delete item DB. This is probably fine");
|
console.log("Could not delete item DB. This is probably fine");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
db.deleteObjectStore('set_db');
|
db.deleteObjectStore('set_db');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log("Could not delete set DB. This is probably fine");
|
console.log("Could not delete set DB. This is probably fine");
|
||||||
}
|
}
|
||||||
|
|
||||||
db.createObjectStore('item_db');
|
db.createObjectStore('item_db');
|
||||||
db.createObjectStore('set_db');
|
db.createObjectStore('set_db');
|
||||||
|
|
||||||
console.log("DB setup complete...");
|
console.log("DB setup complete...");
|
||||||
}
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon.
|
||||||
|
for (const it of itemTypes) {
|
||||||
|
itemLists.set(it, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
let none_items = [
|
||||||
|
["armor", "helmet", "No Helmet"],
|
||||||
|
["armor", "chestplate", "No Chestplate"],
|
||||||
|
["armor", "leggings", "No Leggings"],
|
||||||
|
["armor", "boots", "No Boots"],
|
||||||
|
["accessory", "ring", "No Ring 1"],
|
||||||
|
["accessory", "ring", "No Ring 2"],
|
||||||
|
["accessory", "bracelet", "No Bracelet"],
|
||||||
|
["accessory", "necklace", "No Necklace"],
|
||||||
|
["weapon", "dagger", "No Weapon"],
|
||||||
|
];
|
||||||
|
for (let i = 0; i < none_items.length; i++) {
|
||||||
|
let item = Object();
|
||||||
|
item.slots = 0;
|
||||||
|
item.category = none_items[i][0];
|
||||||
|
item.type = none_items[i][1];
|
||||||
|
item.name = none_items[i][2];
|
||||||
|
item.displayName = item.name;
|
||||||
|
item.set = null;
|
||||||
|
item.quest = null;
|
||||||
|
item.skillpoints = [0, 0, 0, 0, 0];
|
||||||
|
item.has_negstat = false;
|
||||||
|
item.reqs = [0, 0, 0, 0, 0];
|
||||||
|
item.fixID = true;
|
||||||
|
item.tier = "Normal";
|
||||||
|
item.id = 10000 + i;
|
||||||
|
item.nDam = "0-0";
|
||||||
|
item.eDam = "0-0";
|
||||||
|
item.tDam = "0-0";
|
||||||
|
item.wDam = "0-0";
|
||||||
|
item.fDam = "0-0";
|
||||||
|
item.aDam = "0-0";
|
||||||
|
clean_item(item);
|
||||||
|
|
||||||
|
none_items[i] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
function init_maps() {
|
function init_maps() {
|
||||||
|
@ -207,56 +237,13 @@ function init_maps() {
|
||||||
/* Mapping from item names to set names. */
|
/* Mapping from item names to set names. */
|
||||||
idMap = new Map();
|
idMap = new Map();
|
||||||
redirectMap = new Map();
|
redirectMap = new Map();
|
||||||
for (const it of itemTypes) {
|
items = items.concat(none_items);
|
||||||
itemLists.set(it, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
let noneItems = [
|
|
||||||
["armor", "helmet", "No Helmet"],
|
|
||||||
["armor", "chestplate", "No Chestplate"],
|
|
||||||
["armor", "leggings", "No Leggings"],
|
|
||||||
["armor", "boots", "No Boots"],
|
|
||||||
["accessory", "ring", "No Ring 1"],
|
|
||||||
["accessory", "ring", "No Ring 2"],
|
|
||||||
["accessory", "bracelet", "No Bracelet"],
|
|
||||||
["accessory", "necklace", "No Necklace"],
|
|
||||||
["weapon", "dagger", "No Weapon"],
|
|
||||||
["tome", "weaponTome", "No Weapon Tome"],
|
|
||||||
["tome", "armorTome", "No Armour Tome"],
|
|
||||||
["tome", "guildTome", "No Guild Tome"]
|
|
||||||
];
|
|
||||||
for (let i = 0; i < 12; i++) {
|
|
||||||
let item = Object();
|
|
||||||
item.slots = 0;
|
|
||||||
item.category = noneItems[i][0];
|
|
||||||
item.type = noneItems[i][1];
|
|
||||||
item.name = noneItems[i][2];
|
|
||||||
item.displayName = item.name;
|
|
||||||
item.set = null;
|
|
||||||
item.quest = null;
|
|
||||||
item.skillpoints = [0, 0, 0, 0, 0];
|
|
||||||
item.has_negstat = false;
|
|
||||||
item.reqs = [0, 0, 0, 0, 0];
|
|
||||||
item.fixID = true;
|
|
||||||
item.tier = "Normal";//do not get rid of this @hpp
|
|
||||||
item.id = 10000 + i;
|
|
||||||
item.nDam = "0-0";
|
|
||||||
item.eDam = "0-0";
|
|
||||||
item.tDam = "0-0";
|
|
||||||
item.wDam = "0-0";
|
|
||||||
item.fDam = "0-0";
|
|
||||||
item.aDam = "0-0";
|
|
||||||
clean_item(item);
|
|
||||||
|
|
||||||
noneItems[i] = item;
|
|
||||||
}
|
|
||||||
items = items.concat(noneItems);
|
|
||||||
//console.log(items);
|
//console.log(items);
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.remapID === undefined) {
|
if (item.remapID === undefined) {
|
||||||
itemLists.get(item.type).push(item.displayName);
|
itemLists.get(item.type).push(item.displayName);
|
||||||
itemMap.set(item.displayName, item);
|
itemMap.set(item.displayName, item);
|
||||||
if (noneItems.includes(item)) {
|
if (none_items.includes(item)) {
|
||||||
idMap.set(item.id, "");
|
idMap.set(item.id, "");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
143
js/load_ing.js
|
@ -4,6 +4,7 @@ const ING_DB_VERSION = 13;
|
||||||
|
|
||||||
let idb;
|
let idb;
|
||||||
let ireload = false;
|
let ireload = false;
|
||||||
|
let iload_in_progress = false;
|
||||||
let iload_complete = false;
|
let iload_complete = false;
|
||||||
let ings;
|
let ings;
|
||||||
let recipes;
|
let recipes;
|
||||||
|
@ -20,32 +21,34 @@ let recipeIDMap;
|
||||||
/*
|
/*
|
||||||
* Load item set from local DB. Calls init() on success.
|
* Load item set from local DB. Calls init() on success.
|
||||||
*/
|
*/
|
||||||
async function ing_load_local(init_func) {
|
async function ing_load_local() {
|
||||||
console.log("IngMap is: \n " + ingMap);
|
return new Promise(function(resolve, reject) {
|
||||||
let get_tx = idb.transaction(['ing_db', 'recipe_db'], 'readonly');
|
let get_tx = idb.transaction(['ing_db', 'recipe_db'], 'readonly');
|
||||||
let ings_store = get_tx.objectStore('ing_db');
|
let ings_store = get_tx.objectStore('ing_db');
|
||||||
let recipes_store = get_tx.objectStore('recipe_db');
|
let recipes_store = get_tx.objectStore('recipe_db');
|
||||||
let request3 = ings_store.getAll();
|
let request3 = ings_store.getAll();
|
||||||
request3.onerror = function(event) {
|
request3.onerror = function(event) {
|
||||||
console.log("Could not read local ingredient db...");
|
reject("Could not read local ingredient db...");
|
||||||
}
|
}
|
||||||
request3.onsuccess = function(event) {
|
request3.onsuccess = function(event) {
|
||||||
console.log("Successfully read local ingredient db.");
|
console.log("Successfully read local ingredient db.");
|
||||||
ings = request3.result;
|
}
|
||||||
let request4 = recipes_store.getAll();
|
let request4 = recipes_store.getAll();
|
||||||
request4.onerror = function(event) {
|
request4.onerror = function(event) {
|
||||||
console.log("Could not read local recipe db...");
|
reject("Could not read local recipe db...");
|
||||||
}
|
}
|
||||||
request4.onsuccess = function(event) {
|
request4.onsuccess = function(event) {
|
||||||
console.log("Successfully read local recipe db.");
|
console.log("Successfully read local recipe db.");
|
||||||
|
}
|
||||||
|
get_tx.oncomplete = function(event) {
|
||||||
|
ings = request3.result;
|
||||||
recipes = request4.result;
|
recipes = request4.result;
|
||||||
init_ing_maps();
|
init_ing_maps();
|
||||||
init_func();
|
|
||||||
iload_complete = true;
|
iload_complete = true;
|
||||||
|
idb.close();
|
||||||
|
resolve()
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
await get_tx.complete;
|
|
||||||
idb.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clean_ing(ing) {
|
function clean_ing(ing) {
|
||||||
|
@ -59,11 +62,12 @@ function clean_ing(ing) {
|
||||||
/*
|
/*
|
||||||
* Load item set from remote DB (aka a big json file). Calls init() on success.
|
* Load item set from remote DB (aka a big json file). Calls init() on success.
|
||||||
*/
|
*/
|
||||||
async function load_ings(init_func) {
|
async function load_ings() {
|
||||||
|
|
||||||
let getUrl = window.location;
|
let getUrl = window.location;
|
||||||
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
|
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1];
|
||||||
let url = baseUrl + "/ingreds_compress.json";
|
// "Random" string to prevent caching!
|
||||||
|
let url = baseUrl + "/ingreds_compress.json?"+new Date();
|
||||||
url = url.replace(/\w+.html/, "") ;
|
url = url.replace(/\w+.html/, "") ;
|
||||||
let result = await (await fetch(url)).json();
|
let result = await (await fetch(url)).json();
|
||||||
|
|
||||||
|
@ -97,59 +101,65 @@ async function load_ings(init_func) {
|
||||||
}
|
}
|
||||||
add_promises.push(add_tx2.complete);
|
add_promises.push(add_tx2.complete);
|
||||||
add_promises.push(add_tx3.complete);
|
add_promises.push(add_tx3.complete);
|
||||||
Promise.all(add_promises).then((values) => {
|
|
||||||
init_ing_maps();
|
await Promise.all(add_promises);
|
||||||
init_func();
|
init_ing_maps();
|
||||||
iload_complete = true;
|
iload_complete = true;
|
||||||
});
|
idb.close();
|
||||||
// DB not closed? idfk man
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_ing_init(init_func) {
|
async function load_ing_init() {
|
||||||
if (iload_complete) {
|
return new Promise((resolve, reject) => {
|
||||||
console.log("Ingredient db already loaded, skipping load sequence");
|
let request = window.indexedDB.open("ing_db", ING_DB_VERSION)
|
||||||
init_func();
|
request.onerror = function() {
|
||||||
return;
|
reject("DB failed to open...");
|
||||||
}
|
}
|
||||||
let request = window.indexedDB.open("ing_db", ING_DB_VERSION)
|
|
||||||
request.onerror = function() {
|
|
||||||
console.log("DB failed to open...");
|
|
||||||
}
|
|
||||||
|
|
||||||
request.onsuccess = function() {
|
request.onsuccess = async function() {
|
||||||
idb = request.result;
|
idb = request.result;
|
||||||
if (!ireload) {
|
if (iload_in_progress) {
|
||||||
console.log("Using stored data...")
|
while (!iload_complete) {
|
||||||
ing_load_local(init_func);
|
await sleep(100);
|
||||||
|
}
|
||||||
|
console.log("Skipping load...")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
iload_in_progress = true
|
||||||
|
if (ireload) {
|
||||||
|
console.log("Using new data...")
|
||||||
|
await load_ings();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Using stored data...")
|
||||||
|
await ing_load_local();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
console.log("Using new data...")
|
|
||||||
load_ings(init_func);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.onupgradeneeded = function(e) {
|
request.onupgradeneeded = function(e) {
|
||||||
ireload = true;
|
ireload = true;
|
||||||
|
|
||||||
let idb = e.target.result;
|
let idb = e.target.result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
idb.deleteObjectStore('ing_db');
|
idb.deleteObjectStore('ing_db');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log("Could not delete ingredient DB. This is probably fine");
|
console.log("Could not delete ingredient DB. This is probably fine");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
idb.deleteObjectStore('recipe_db');
|
idb.deleteObjectStore('recipe_db');
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log("Could not delete recipe DB. This is probably fine");
|
console.log("Could not delete recipe DB. This is probably fine");
|
||||||
}
|
}
|
||||||
idb.createObjectStore('ing_db');
|
idb.createObjectStore('ing_db');
|
||||||
idb.createObjectStore('recipe_db');
|
idb.createObjectStore('recipe_db');
|
||||||
|
|
||||||
console.log("DB setup complete...");
|
console.log("DB setup complete...");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function init_ing_maps() {
|
function init_ing_maps() {
|
||||||
|
@ -222,4 +232,5 @@ function init_ing_maps() {
|
||||||
recipeList.push(recipe["name"]);
|
recipeList.push(recipe["name"]);
|
||||||
recipeIDMap.set(recipe["id"],recipe["name"]);
|
recipeIDMap.set(recipe["id"],recipe["name"]);
|
||||||
}
|
}
|
||||||
|
console.log(ingMap);
|
||||||
}
|
}
|
||||||
|
|
180
js/load_tome.js
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
const TOME_DB_VERSION = 3;
|
||||||
|
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
|
||||||
|
|
||||||
|
let tdb;
|
||||||
|
let treload = false;
|
||||||
|
let tload_complete = false;
|
||||||
|
let tload_in_progress = false;
|
||||||
|
let tomes;
|
||||||
|
let tomeMap;
|
||||||
|
let tometomeIDMap;
|
||||||
|
let tomeRedirectMap;
|
||||||
|
let tomeLists = new Map();
|
||||||
|
/*
|
||||||
|
* Load tome set from local DB. Calls init() on success.
|
||||||
|
*/
|
||||||
|
async function load_tome_local() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let get_tx = tdb.transaction(['tome_db'], 'readonly');
|
||||||
|
let get_store = get_tx.objectStore('tome_db');
|
||||||
|
let request = get_store.getAll();
|
||||||
|
request.onerror = function(event) {
|
||||||
|
reject("Could not read local tome db...");
|
||||||
|
}
|
||||||
|
request.onsuccess = function(event) {
|
||||||
|
console.log("Successfully read local tome db.");
|
||||||
|
}
|
||||||
|
get_tx.oncomplete = function(event) {
|
||||||
|
tomes = request.result;
|
||||||
|
init_tome_maps();
|
||||||
|
tload_complete = true;
|
||||||
|
tdb.close();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load tome set from remote DB (json). Calls init() on success.
|
||||||
|
*/
|
||||||
|
async function load_tome() {
|
||||||
|
|
||||||
|
let getUrl = window.location;
|
||||||
|
let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1];
|
||||||
|
// "Random" string to prevent caching!
|
||||||
|
let url = baseUrl + "/tomes.json?"+new Date();
|
||||||
|
let result = await (await fetch(url)).json();
|
||||||
|
tomes = result.tomes
|
||||||
|
|
||||||
|
let add_tx = tdb.transaction(['tome_db'], 'readwrite');
|
||||||
|
add_tx.onabort = function(e) {
|
||||||
|
console.log(e);
|
||||||
|
console.log("Not enough space...");
|
||||||
|
};
|
||||||
|
let tomes_store = add_tx.objectStore('tome_db');
|
||||||
|
let add_promises = [];
|
||||||
|
for (const tome of tomes) {
|
||||||
|
//dependency on clean_item in load.js
|
||||||
|
clean_item(tome);
|
||||||
|
let req = tomes_store.add(tome, tome.name);
|
||||||
|
req.onerror = function() {
|
||||||
|
console.log("ADD TOME ERROR? " + tome.name);
|
||||||
|
};
|
||||||
|
add_promises.push(req);
|
||||||
|
}
|
||||||
|
add_promises.push(add_tx.complete);
|
||||||
|
|
||||||
|
await Promise.all(add_promises);
|
||||||
|
init_tome_maps();
|
||||||
|
tload_complete = true;
|
||||||
|
tdb.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function load_tome_init() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let request = window.indexedDB.open('tome_db', TOME_DB_VERSION);
|
||||||
|
|
||||||
|
request.onerror = function() {
|
||||||
|
reject("DB failed to open...");
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onsuccess = async function() {
|
||||||
|
tdb = request.result;
|
||||||
|
if (tload_in_progress) {
|
||||||
|
while (!tload_complete) {
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
console.log("Skipping load...")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tload_in_progress = true
|
||||||
|
if (treload) {
|
||||||
|
console.log("Using new data...")
|
||||||
|
await load_tome();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Using stored data...")
|
||||||
|
await load_tome_local();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onupgradeneeded = function(e) {
|
||||||
|
treload = true;
|
||||||
|
|
||||||
|
let tdb = e.target.result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
tdb.deleteObjectStore('tome_db');
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log("Could not delete tome DB. This is probably fine");
|
||||||
|
}
|
||||||
|
|
||||||
|
tdb.createObjectStore('tome_db');
|
||||||
|
|
||||||
|
console.log("DB setup complete...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let none_tomes = [
|
||||||
|
["tome", "weaponTome", "No Weapon Tome"],
|
||||||
|
["tome", "armorTome", "No Armor Tome"],
|
||||||
|
["tome", "guildTome", "No Guild Tome"]
|
||||||
|
];
|
||||||
|
function init_tome_maps() {
|
||||||
|
//warp
|
||||||
|
tomeMap = new Map();
|
||||||
|
/* Mapping from item names to set names. */
|
||||||
|
tomeIDMap = new Map();
|
||||||
|
|
||||||
|
tomeRedirectMap = new Map();
|
||||||
|
for (const it of tome_types) {
|
||||||
|
tomeLists.set(it, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
let tome = Object();
|
||||||
|
tome.slots = 0;
|
||||||
|
tome.category = none_tomes[i][0];
|
||||||
|
tome.type = none_tomes[i][1];
|
||||||
|
tome.name = none_tomes[i][2];
|
||||||
|
tome.displayName = tome.name;
|
||||||
|
tome.set = null;
|
||||||
|
tome.quest = null;
|
||||||
|
tome.skillpoints = [0, 0, 0, 0, 0];
|
||||||
|
tome.has_negstat = false;
|
||||||
|
tome.reqs = [0, 0, 0, 0, 0];
|
||||||
|
tome.fixID = true;
|
||||||
|
tome.tier = "Normal";
|
||||||
|
tome.id = 61 + i; //special case!
|
||||||
|
tome.nDam = "0-0";
|
||||||
|
tome.eDam = "0-0";
|
||||||
|
tome.tDam = "0-0";
|
||||||
|
tome.wDam = "0-0";
|
||||||
|
tome.fDam = "0-0";
|
||||||
|
tome.aDam = "0-0";
|
||||||
|
//dependency - load.js
|
||||||
|
clean_item(tome);
|
||||||
|
|
||||||
|
none_tomes[i] = tome;
|
||||||
|
}
|
||||||
|
tomes = tomes.concat(none_tomes);
|
||||||
|
for (const tome of tomes) {
|
||||||
|
if (tome.remapID === undefined) {
|
||||||
|
tomeLists.get(tome.type).push(tome.displayName);
|
||||||
|
tomeMap.set(tome.displayName, tome);
|
||||||
|
if (none_tomes.includes(tome)) {
|
||||||
|
tomeIDMap.set(tome.id, "");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tomeIDMap.set(tome.id, tome.displayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tomeRedirectMap.set(tome.id, tome.remapID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
js/loadheader.js
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
<div class = "headerleft">
|
|
||||||
<a href = "./index.html" class = "nomarginp iconlink tooltip">
|
|
||||||
<img src = "/media/icons/new/builder.png" class = "left linkoptions headericon">
|
|
||||||
<div class = "tooltiptext center">WynnBuilder</div>
|
|
||||||
</img>
|
|
||||||
</a>
|
|
||||||
<a href = "./crafter.html" class = "nomarginp iconlink tooltip">
|
|
||||||
<img src = "/media/icons/new/crafter.png" class = "left linkoptions headericon">
|
|
||||||
<div class = "tooltiptext center">WynnCrafter</div>
|
|
||||||
</img>
|
|
||||||
</a>
|
|
||||||
<a href = "./items.html" class = "nomarginp iconlink tooltip">
|
|
||||||
<img src = "/media/icons/new/searcher.png" class = "left linkoptions headericon">
|
|
||||||
<div class = "tooltiptext center">WynnAtlas</div>
|
|
||||||
</img>
|
|
||||||
</a>
|
|
||||||
<a href = "./customizer.html" class = "nomarginp iconlink tooltip">
|
|
||||||
<img src = "/media/icons/new/custom.png" class = "left linkoptions headericon">
|
|
||||||
<div class = "tooltiptext center">WynnCustom</div>
|
|
||||||
</img>
|
|
||||||
</a> <a href = "./map.html" class = "nomarginp iconlink tooltip">
|
|
||||||
<img src = "/media/icons/new/compass.png" class = "left linkoptions headericon">
|
|
||||||
</img>
|
|
||||||
<div class = "tooltiptext center">WynnGPS</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class = "headerright">
|
|
||||||
<button class = "button" id = "toggle-icon-button" onclick="toggleIcons();">
|
|
||||||
Use Old Icons
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
*/
|
|
||||||
let header_icon_map_left = new Map([
|
|
||||||
["index",["builder","WynnBuilder"]],
|
|
||||||
["crafter",["crafter","WynnCrafter"]],
|
|
||||||
["items",["searcher","WynnAtlas"]],
|
|
||||||
["customizer",["custom","WynnCustom"]],
|
|
||||||
["map",["compass","WynnGPS"]],
|
|
||||||
["wynnfo/index",["book","Wynnfo"]]
|
|
||||||
]);
|
|
||||||
|
|
||||||
function setHeaders() {
|
|
||||||
let headerleft = document.getElementById("headerleft");
|
|
||||||
let headerright = document.getElementById("headerright");
|
|
||||||
|
|
||||||
for (const [name,data] of header_icon_map_left) {
|
|
||||||
let a_elem = document.createElement("a");
|
|
||||||
let img = document.createElement("img");
|
|
||||||
let div = document.createElement("div");
|
|
||||||
a_elem.classList.add("nomarginp");
|
|
||||||
a_elem.classList.add("iconlink");
|
|
||||||
a_elem.classList.add("tooltip");
|
|
||||||
a_elem.href = "../" + name + ".html";
|
|
||||||
img.classList.add("left");
|
|
||||||
img.classList.add("linkoptions");
|
|
||||||
img.classList.add("headericon");
|
|
||||||
img.src = "/media/icons/new/" + data[0] + ".png";
|
|
||||||
div.classList.add("tooltiptext");
|
|
||||||
div.classList.add("header-tooltip");
|
|
||||||
div.classList.add("center");
|
|
||||||
div.textContent = data[1];
|
|
||||||
a_elem.appendChild(img);
|
|
||||||
a_elem.appendChild(div);
|
|
||||||
headerleft.appendChild(a_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
let toggle_icon_button = document.createElement("button");
|
|
||||||
toggle_icon_button.classList.add("button");
|
|
||||||
toggle_icon_button.id = "toggle-icon-button";
|
|
||||||
toggle_icon_button.onclick = function() {toggleIcons()};
|
|
||||||
toggle_icon_button.textContent = "Use Old Icons";
|
|
||||||
headerright.appendChild(toggle_icon_button);
|
|
||||||
|
|
||||||
let reload_div = document.createElement("div");
|
|
||||||
let reload_button = document.createElement("button");
|
|
||||||
reload_button.classList.add("button");
|
|
||||||
reload_button.style.left = '0px';
|
|
||||||
reload_button.style.top = '0px';
|
|
||||||
reload_button.style.width = '48px';
|
|
||||||
reload_button.style.height = '48px';
|
|
||||||
reload_button.style.padding = '0px';
|
|
||||||
reload_button.onclick = hardReload;
|
|
||||||
let reload_img = document.createElement("img");
|
|
||||||
reload_img.src = "/media/icons/new/reload.png"
|
|
||||||
reload_img.style.width = "100%";
|
|
||||||
let reload_tooltip;
|
|
||||||
reload_tooltip = createTooltip(reload_tooltip, "p", "Reload", reload_button, ["center","reloadtooltip"]);
|
|
||||||
//reload_tooltip.style.position = "relative";
|
|
||||||
reload_tooltip.style.left = "-50%";
|
|
||||||
reload_tooltip.style.top = "70%";
|
|
||||||
|
|
||||||
reload_div.appendChild(reload_button);
|
|
||||||
reload_button.appendChild(reload_img);
|
|
||||||
headerright.appendChild(reload_div);
|
|
||||||
|
|
||||||
|
|
||||||
console.log("Set Header");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setHeaders();
|
|
28
js/map.js
|
@ -9,16 +9,6 @@ const map_url_tag = location.hash.slice(1);
|
||||||
// console.log(map_url_base);
|
// console.log(map_url_base);
|
||||||
// console.log(map_url_tag);
|
// console.log(map_url_tag);
|
||||||
|
|
||||||
const MAP_BUILD_VERSION = "7";
|
|
||||||
|
|
||||||
function setTitle() {
|
|
||||||
let text = "WynnGPS version "+MAP_BUILD_VERSION;
|
|
||||||
document.getElementById("header").classList.add("funnynumber");
|
|
||||||
document.getElementById("header").textContent = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +63,7 @@ function init_map(){ //async just in case we need async stuff
|
||||||
zoomControl: false,
|
zoomControl: false,
|
||||||
zoom: 1
|
zoom: 1
|
||||||
}).setView([0,0], 1);
|
}).setView([0,0], 1);
|
||||||
L.imageOverlay("/media/maps/world-map.png", bounds).addTo(map);
|
L.imageOverlay("../media/maps/world-map.png", bounds).addTo(map);
|
||||||
|
|
||||||
map.fitBounds(bounds);
|
map.fitBounds(bounds);
|
||||||
|
|
||||||
|
@ -144,10 +134,10 @@ function placeMarker(lat, lng) {
|
||||||
}
|
}
|
||||||
|
|
||||||
marker = L.marker([lat, lng], {icon: L.icon({
|
marker = L.marker([lat, lng], {icon: L.icon({
|
||||||
iconUrl: '/media/icons/' + (newIcons ? "new/" : "old/" ) + 'marker.png',
|
iconUrl: '../media/icons/' + (newIcons ? "new/" : "old/" ) + 'marker.png',
|
||||||
iconSize: [32, 32],
|
iconSize: [32, 32],
|
||||||
iconAnchor: [16, 32],
|
iconAnchor: [16, 32],
|
||||||
shadowUrl: '/media/icons/' + (newIcons ? "new/" : "old/" ) + 'shadow.png',
|
shadowUrl: '../media/icons/' + (newIcons ? "new/" : "old/" ) + 'shadow.png',
|
||||||
shadowSize: [1,1],
|
shadowSize: [1,1],
|
||||||
shadowAnchor: [16, 32],
|
shadowAnchor: [16, 32],
|
||||||
className: "marker"
|
className: "marker"
|
||||||
|
@ -243,7 +233,7 @@ function pullguilds() {
|
||||||
.then(res => {
|
.then(res => {
|
||||||
guildTags.set(guild, res.prefix);
|
guildTags.set(guild, res.prefix);
|
||||||
guildColors.set(guild, randomColorHSL([0,1],[0,1],[0.4,1]));
|
guildColors.set(guild, randomColorHSL([0,1],[0,1],[0.4,1]));
|
||||||
console.log("Succesfully pulled guild data for " + guild + ".");
|
// console.log("Succesfully pulled guild data for " + guild + ".");
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -286,7 +276,7 @@ function pullguilds() {
|
||||||
let li = document.createElement("li");
|
let li = document.createElement("li");
|
||||||
|
|
||||||
let i = document.createElement("img");
|
let i = document.createElement("img");
|
||||||
i.src = "./media/icons/locations/" + img;
|
i.src = "../media/icons/locations/" + img;
|
||||||
i.style.maxWidth = "32px";
|
i.style.maxWidth = "32px";
|
||||||
i.style.maxHeight = "32px";
|
i.style.maxHeight = "32px";
|
||||||
li.appendChild(i);
|
li.appendChild(i);
|
||||||
|
@ -467,10 +457,10 @@ function toggleResources() {
|
||||||
let imgBounds = [ [ TRcorner[0]-(16*n)-20-gap*n,TRcorner[1]+4], [ TRcorner[0]-(16*n)-4-gap*n,TRcorner[1]+20] ];
|
let imgBounds = [ [ TRcorner[0]-(16*n)-20-gap*n,TRcorner[1]+4], [ TRcorner[0]-(16*n)-4-gap*n,TRcorner[1]+20] ];
|
||||||
imgBounds = [xytolatlng(imgBounds[0][0],imgBounds[0][1]), xytolatlng(imgBounds[1][0],imgBounds[1][1])];
|
imgBounds = [xytolatlng(imgBounds[0][0],imgBounds[0][1]), xytolatlng(imgBounds[1][0],imgBounds[1][1])];
|
||||||
|
|
||||||
let resourceObj = L.imageOverlay("/media/icons/" + (newIcons ? "new/" : "old/" ) +resource+".png", imgBounds, {className: `${resource} resourceimg`}).addTo(map);
|
let resourceObj = L.imageOverlay("../media/icons/" + (newIcons ? "new/" : "old/" ) +resource+".png", imgBounds, {className: `${resource} resourceimg`}).addTo(map);
|
||||||
resourceObjs.push(resourceObj);
|
resourceObjs.push(resourceObj);
|
||||||
}
|
}
|
||||||
let gearObj = L.imageOverlay("/media/icons/" + (newIcons ? "new/" : "old/" ) + "Gears.png", [xytolatlng(TRcorner[0]-(16*terr_resources.length)-20-gap*terr_resources.length,TRcorner[1]+4), xytolatlng(TRcorner[0]-(16*terr_resources.length)-4-gap*terr_resources.length,TRcorner[1]+20)], {className: `Ore resourceimg`}).addTo(map);
|
let gearObj = L.imageOverlay("../media/icons/" + (newIcons ? "new/" : "old/" ) + "Gears.png", [xytolatlng(TRcorner[0]-(16*terr_resources.length)-20-gap*terr_resources.length,TRcorner[1]+4), xytolatlng(TRcorner[0]-(16*terr_resources.length)-4-gap*terr_resources.length,TRcorner[1]+20)], {className: `Ore resourceimg`}).addTo(map);
|
||||||
resourceObjs.push(gearObj);
|
resourceObjs.push(gearObj);
|
||||||
//draw resource storage
|
//draw resource storage
|
||||||
for (const n in terr_storage) {
|
for (const n in terr_storage) {
|
||||||
|
@ -479,10 +469,10 @@ function toggleResources() {
|
||||||
let imgBounds = [ [ DRcorner[0]-(16*n)-20-gap*n,DRcorner[1]-20], [ DRcorner[0]-(16*n)-4-gap*n,DRcorner[1]-4] ];
|
let imgBounds = [ [ DRcorner[0]-(16*n)-20-gap*n,DRcorner[1]-20], [ DRcorner[0]-(16*n)-4-gap*n,DRcorner[1]-4] ];
|
||||||
imgBounds = [xytolatlng(imgBounds[0][0],imgBounds[0][1]), xytolatlng(imgBounds[1][0],imgBounds[1][1])];
|
imgBounds = [xytolatlng(imgBounds[0][0],imgBounds[0][1]), xytolatlng(imgBounds[1][0],imgBounds[1][1])];
|
||||||
|
|
||||||
let resourceObj = L.imageOverlay("/media/icons/" + (newIcons ? "new/" : "old/" ) +storage+".png", imgBounds, {alt: `${storage}`, className: `${storage} resourceimg`}).addTo(map);
|
let resourceObj = L.imageOverlay("../media/icons/" + (newIcons ? "new/" : "old/" ) +storage+".png", imgBounds, {alt: `${storage}`, className: `${storage} resourceimg`}).addTo(map);
|
||||||
resourceObjs.push(resourceObj);
|
resourceObjs.push(resourceObj);
|
||||||
}
|
}
|
||||||
let chestObj = L.imageOverlay("/media/icons/" + (newIcons ? "new/" : "old/" ) + "Chest.png", [xytolatlng(DRcorner[0]-(16*terr_storage.length)-20-gap*terr_storage.length,DRcorner[1]-20), xytolatlng(DRcorner[0]-(16*terr_storage.length)-4-gap*terr_storage.length,DRcorner[1]-4)], {className: `Wood resourceimg`}).addTo(map);
|
let chestObj = L.imageOverlay("../media/icons/" + (newIcons ? "new/" : "old/" ) + "Chest.png", [xytolatlng(DRcorner[0]-(16*terr_storage.length)-20-gap*terr_storage.length,DRcorner[1]-20), xytolatlng(DRcorner[0]-(16*terr_storage.length)-4-gap*terr_storage.length,DRcorner[1]-4)], {className: `Wood resourceimg`}).addTo(map);
|
||||||
resourceObjs.push(chestObj);
|
resourceObjs.push(chestObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
100
js/optimize.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
function optimizeStrDex() {
|
||||||
|
if (!player_build) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const skillpoints = skp_inputs.map(x => x.value); // JANK
|
||||||
|
let total_assigned = 0;
|
||||||
|
const min_assigned = player_build.base_skillpoints;
|
||||||
|
const base_totals = player_build.total_skillpoints;
|
||||||
|
let base_skillpoints = [];
|
||||||
|
for (let i in skp_order){ //big bren
|
||||||
|
const assigned = skillpoints[i] - base_totals[i] + min_assigned[i]
|
||||||
|
base_skillpoints.push(assigned);
|
||||||
|
total_assigned += assigned;
|
||||||
|
}
|
||||||
|
|
||||||
|
const remaining = levelToSkillPoints(player_build.level) - total_assigned;
|
||||||
|
const max_str_boost = 100 - base_skillpoints[0];
|
||||||
|
const max_dex_boost = 100 - base_skillpoints[1];
|
||||||
|
if (Math.min(remaining, max_str_boost, max_dex_boost) < 0) return; // Unwearable
|
||||||
|
|
||||||
|
let str_bonus = remaining;
|
||||||
|
let dex_bonus = 0;
|
||||||
|
let best_skillpoints = skillpoints;
|
||||||
|
let best_damage = 0;
|
||||||
|
for (let i = 0; i <= remaining; ++i) {
|
||||||
|
let total_skillpoints = skillpoints.slice();
|
||||||
|
total_skillpoints[0] += Math.min(max_str_boost, str_bonus);
|
||||||
|
total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus);
|
||||||
|
|
||||||
|
// Calculate total 3rd spell damage
|
||||||
|
let spell = spell_table[player_build.weapon.statMap.get("type")][2];
|
||||||
|
const stats = player_build.statMap;
|
||||||
|
let critChance = skillPointsToPercentage(total_skillpoints[1]);
|
||||||
|
let save_damages = [];
|
||||||
|
let spell_parts;
|
||||||
|
if (spell.parts) {
|
||||||
|
spell_parts = spell.parts;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spell_parts = spell.variants.DEFAULT;
|
||||||
|
for (const majorID of stats.get("activeMajorIDs")) {
|
||||||
|
if (majorID in spell.variants) {
|
||||||
|
spell_parts = spell.variants[majorID];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let total_damage = 0;
|
||||||
|
for (const part of spell_parts) {
|
||||||
|
if (part.type === "damage") {
|
||||||
|
let tmp_conv = [];
|
||||||
|
for (let i in part.conversion) {
|
||||||
|
tmp_conv.push(part.conversion[i] * part.multiplier);
|
||||||
|
}
|
||||||
|
let _results = calculateSpellDamage(stats, player_build.weapon.statMap, tmp_conv, true);
|
||||||
|
let totalDamNormal = _results[0];
|
||||||
|
let totalDamCrit = _results[1];
|
||||||
|
let results = _results[2];
|
||||||
|
let tooltipinfo = _results[3];
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; ++i) {
|
||||||
|
for (let j in results[i]) {
|
||||||
|
results[i][j] = results[i][j].toFixed(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0;
|
||||||
|
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
|
||||||
|
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
|
||||||
|
|
||||||
|
save_damages.push(averageDamage);
|
||||||
|
if (part.summary == true) {
|
||||||
|
total_damage = averageDamage;
|
||||||
|
}
|
||||||
|
} else if (part.type === "total") {
|
||||||
|
total_damage = 0;
|
||||||
|
for (let i in part.factors) {
|
||||||
|
total_damage += save_damages[i] * part.factors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // END Calculate total 3rd spell damage (total_damage)
|
||||||
|
if (total_damage > best_damage) {
|
||||||
|
best_damage = total_damage;
|
||||||
|
best_skillpoints = total_skillpoints.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
str_bonus -= 1;
|
||||||
|
dex_bonus += 1;
|
||||||
|
}
|
||||||
|
console.log(best_skillpoints);
|
||||||
|
|
||||||
|
// TODO do not merge for performance reasons
|
||||||
|
for (let i in skp_order) {
|
||||||
|
skp_inputs[i].input_field.value = best_skillpoints[i];
|
||||||
|
skp_inputs[i].mark_dirty();
|
||||||
|
}
|
||||||
|
for (let i in skp_order) {
|
||||||
|
skp_inputs[i].update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
133
js/powders.js
|
@ -55,9 +55,140 @@ class PowderSpecial{
|
||||||
function _ps(a,b,c,d,e) { return new PowderSpecial(a,b,c,d,e); } //bruh moment
|
function _ps(a,b,c,d,e) { return new PowderSpecial(a,b,c,d,e); } //bruh moment
|
||||||
|
|
||||||
let powderSpecialStats = [
|
let powderSpecialStats = [
|
||||||
_ps("Quake",new Map([["Radius",[4.4,4.9,5.4,5.9,6.4]], ["Damage",[155,220,285,350,415]] ]),"Rage",new Map([ ["Damage", [0.3,0.4,0.5,0.7,1.0]],["Description", "% " + "\u2764" + " Missing"] ]),400), //e
|
_ps("Quake",new Map([["Radius",[4.4,4.9,5.4,5.9,6.4]], ["Damage",[155,220,285,350,415]] ]),"Rage",new Map([ ["Damage", [0.3,0.4,0.5,0.7,1.0]],["Description", "% " + "\u2764" + " Missing"] ]), 396), //e
|
||||||
_ps("Chain Lightning",new Map([ ["Chains", [5,6,7,8,9]], ["Damage", [200,225,250,275,300]] ]),"Kill Streak",new Map([ ["Damage", [3,4.5,6,7.5,9]],["Duration", [5,5,5,5,5]],["Description", "Mob Killed"] ]),200), //t
|
_ps("Chain Lightning",new Map([ ["Chains", [5,6,7,8,9]], ["Damage", [200,225,250,275,300]] ]),"Kill Streak",new Map([ ["Damage", [3,4.5,6,7.5,9]],["Duration", [5,5,5,5,5]],["Description", "Mob Killed"] ]),200), //t
|
||||||
_ps("Curse",new Map([ ["Duration", [7,7.5,8,8.5,9]],["Damage Boost", [90,120,150,180,210]] ]),"Concentration",new Map([ ["Damage", [1,2,3,4,5]],["Duration",[1,1,1,1,1]],["Description", "Mana Used"] ]),150), //w
|
_ps("Curse",new Map([ ["Duration", [7,7.5,8,8.5,9]],["Damage Boost", [90,120,150,180,210]] ]),"Concentration",new Map([ ["Damage", [1,2,3,4,5]],["Duration",[1,1,1,1,1]],["Description", "Mana Used"] ]),150), //w
|
||||||
_ps("Courage",new Map([ ["Duration", [6,6.5,7,7.5,8]],["Damage", [75,87.5,100,112.5,125]],["Damage Boost", [70,90,110,130,150]] ]),"Endurance",new Map([ ["Damage", [2,3,4,5,6]],["Duration", [8,8,8,8,8]],["Description", "Hit Taken"] ]),200), //f
|
_ps("Courage",new Map([ ["Duration", [6,6.5,7,7.5,8]],["Damage", [75,87.5,100,112.5,125]],["Damage Boost", [70,90,110,130,150]] ]),"Endurance",new Map([ ["Damage", [2,3,4,5,6]],["Duration", [8,8,8,8,8]],["Description", "Hit Taken"] ]),200), //f
|
||||||
_ps("Wind Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [400,450,500,550,600]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a
|
_ps("Wind Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [400,450,500,550,600]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply armor powders.
|
||||||
|
* Encoding shortcut assumes that all powders give +def to one element
|
||||||
|
* and -def to the element "behind" it in cycle ETWFA, which is true
|
||||||
|
* as of now and unlikely to change in the near future.
|
||||||
|
*/
|
||||||
|
function applyArmorPowders(expandedItem) {
|
||||||
|
const powders = expandedItem.get('powders');
|
||||||
|
for(const id of powders){
|
||||||
|
let powder = powderStats[id];
|
||||||
|
let name = powderNames.get(id).charAt(0);
|
||||||
|
let prevName = skp_elements[(skp_elements.indexOf(name) + 4 )% 5];
|
||||||
|
expandedItem.set(name+"Def", (expandedItem.get(name+"Def") || 0) + powder["defPlus"]);
|
||||||
|
expandedItem.set(prevName+"Def", (expandedItem.get(prevName+"Def") || 0) - powder["defMinus"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const damage_keys = [ "nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_" ];
|
||||||
|
const damage_present_key = 'damagePresent';
|
||||||
|
/**
|
||||||
|
* Apply weapon powders. MUTATES THE ITEM!
|
||||||
|
* Adds entries for `damage_keys` and `damage_present_key`
|
||||||
|
* For normal items, `damage_keys` is 6x2 list (elem: [min, max])
|
||||||
|
* For crafted items, `damage_keys` is 6x2x2 list (elem: [minroll: [min, max], maxroll: [min, max]])
|
||||||
|
*/
|
||||||
|
function apply_weapon_powders(item) {
|
||||||
|
let present;
|
||||||
|
if (item.get("tier") !== "Crafted") {
|
||||||
|
let weapon_result = calc_weapon_powder(item);
|
||||||
|
let damages = weapon_result[0];
|
||||||
|
present = weapon_result[1];
|
||||||
|
for (const i in damage_keys) {
|
||||||
|
item.set(damage_keys[i], damages[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let base_low = [item.get("nDamBaseLow"),item.get("eDamBaseLow"),item.get("tDamBaseLow"),item.get("wDamBaseLow"),item.get("fDamBaseLow"),item.get("aDamBaseLow")];
|
||||||
|
let results_low = calc_weapon_powder(item, base_low);
|
||||||
|
let damage_low = results_low[0];
|
||||||
|
let base_high = [item.get("nDamBaseHigh"),item.get("eDamBaseHigh"),item.get("tDamBaseHigh"),item.get("wDamBaseHigh"),item.get("fDamBaseHigh"),item.get("aDamBaseHigh")];
|
||||||
|
let results_high = calc_weapon_powder(item, base_high);
|
||||||
|
let damage_high = results_high[0];
|
||||||
|
present = results_high[1];
|
||||||
|
|
||||||
|
for (const i in damage_keys) {
|
||||||
|
item.set(damage_keys[i], [damage_low[i], damage_high[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.set(damage_present_key, present);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate weapon damage from powder.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* weapon: Weapon to apply powder to
|
||||||
|
* damageBases: used by crafted
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* [damages, damage_present]
|
||||||
|
*/
|
||||||
|
function calc_weapon_powder(weapon, damageBases) {
|
||||||
|
let powders = weapon.get("powders").slice();
|
||||||
|
|
||||||
|
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
|
||||||
|
let damages = [
|
||||||
|
weapon.get('nDam').split('-').map(Number),
|
||||||
|
weapon.get('eDam').split('-').map(Number),
|
||||||
|
weapon.get('tDam').split('-').map(Number),
|
||||||
|
weapon.get('wDam').split('-').map(Number),
|
||||||
|
weapon.get('fDam').split('-').map(Number),
|
||||||
|
weapon.get('aDam').split('-').map(Number)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Applying spell conversions
|
||||||
|
let neutralBase = damages[0].slice();
|
||||||
|
let neutralRemainingRaw = damages[0].slice();
|
||||||
|
|
||||||
|
//powder application for custom crafted weapons is inherently fucked because there is no base. Unsure what to do.
|
||||||
|
|
||||||
|
//Powder application for Crafted weapons - this implementation is RIGHT YEAAAAAAAAA
|
||||||
|
//1st round - apply each as ingred, 2nd round - apply as normal
|
||||||
|
if (weapon.get("tier") === "Crafted" && !weapon.get("custom")) {
|
||||||
|
for (const p of powders.concat(weapon.get("ingredPowders"))) {
|
||||||
|
let powder = powderStats[p]; //use min, max, and convert
|
||||||
|
let element = Math.floor((p+0.01)/6); //[0,4], the +0.01 attempts to prevent division error
|
||||||
|
let diff = Math.floor(damageBases[0] * powder.convert/100);
|
||||||
|
damageBases[0] -= diff;
|
||||||
|
damageBases[element+1] += diff + Math.floor( (powder.min + powder.max) / 2 );
|
||||||
|
}
|
||||||
|
//update all damages
|
||||||
|
for (let i = 0; i < damages.length; i++) {
|
||||||
|
damages[i] = [Math.floor(damageBases[i] * 0.9), Math.floor(damageBases[i] * 1.1)];
|
||||||
|
}
|
||||||
|
neutralRemainingRaw = damages[0].slice();
|
||||||
|
neutralBase = damages[0].slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
//apply powders to weapon
|
||||||
|
for (const powderID of powders) {
|
||||||
|
const powder = powderStats[powderID];
|
||||||
|
// Bitwise to force conversion to integer (integer division).
|
||||||
|
const element = (powderID/6) | 0;
|
||||||
|
let conversionRatio = powder.convert/100;
|
||||||
|
if (neutralRemainingRaw[1] > 0) {
|
||||||
|
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
|
||||||
|
let max_diff = Math.min(neutralRemainingRaw[1], conversionRatio * neutralBase[1]);
|
||||||
|
|
||||||
|
damages[element+1][0] = Math.floor(round_near(damages[element+1][0] + min_diff));
|
||||||
|
damages[element+1][1] = Math.floor(round_near(damages[element+1][1] + max_diff));
|
||||||
|
neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
|
||||||
|
neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
|
||||||
|
//damages[element+1][0] += min_diff;
|
||||||
|
//damages[element+1][1] += max_diff;
|
||||||
|
//neutralRemainingRaw[0] -= min_diff;
|
||||||
|
//neutralRemainingRaw[1] -= max_diff;
|
||||||
|
}
|
||||||
|
damages[element+1][0] += powder.min;
|
||||||
|
damages[element+1][1] += powder.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ordering of these two blocks decides whether neutral is present when converted away or not.
|
||||||
|
let present_elements = []
|
||||||
|
for (const damage of damages) {
|
||||||
|
present_elements.push(damage[1] > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ordering of these two blocks decides whether neutral is present when converted away or not.
|
||||||
|
damages[0] = neutralRemainingRaw;
|
||||||
|
return [damages, present_elements];
|
||||||
|
}
|
||||||
|
|
205
js/render_compute_graph.js
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
// Set-up the export button
|
||||||
|
function set_export_button(svg, button_id, output_id) {
|
||||||
|
d3.select('#'+button_id).on('click', function(){
|
||||||
|
//get svg source.
|
||||||
|
var serializer = new XMLSerializer();
|
||||||
|
var source = serializer.serializeToString(svg.node());
|
||||||
|
console.log(source);
|
||||||
|
|
||||||
|
source = source.replace(/^<g/, '<svg');
|
||||||
|
source = source.replace(/<\/g>$/, '</svg>');
|
||||||
|
//add name spaces.
|
||||||
|
if(!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)){
|
||||||
|
source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
|
||||||
|
}
|
||||||
|
if(!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)){
|
||||||
|
source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
|
||||||
|
}
|
||||||
|
|
||||||
|
//add xml declaration
|
||||||
|
source = '<?xml version="1.0" standalone="no"?>\r\n' + source;
|
||||||
|
|
||||||
|
//convert svg source to URI data scheme.
|
||||||
|
var url = "data:image/svg+xml;charset=utf-8,"+encodeURIComponent(source);
|
||||||
|
|
||||||
|
//set url value to a element's href attribute.
|
||||||
|
document.getElementById(output_id).href = url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
d3.select("#graph_body")
|
||||||
|
.append("div")
|
||||||
|
.attr("style", "width: 100%; height: 100%; min-height: 0px; flex-grow: 1")
|
||||||
|
.append("svg")
|
||||||
|
.attr("preserveAspectRatio", "xMinYMin meet")
|
||||||
|
.classed("svg-content-responsive", true);
|
||||||
|
let graph = d3.select("svg");
|
||||||
|
let svg = graph.append('g');
|
||||||
|
let margin = {top: 20, right: 20, bottom: 35, left: 40};
|
||||||
|
|
||||||
|
function bbox() {
|
||||||
|
let ret = graph.node().parentNode.getBoundingClientRect();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
let _bbox = bbox();
|
||||||
|
|
||||||
|
const colors = ['aqua', 'yellow', 'fuchsia', 'white', 'teal', 'olive', 'purple', 'gray', 'blue', 'lime', 'red', 'silver', 'navy', 'green', 'maroon'];
|
||||||
|
const n_colors = colors.length;
|
||||||
|
|
||||||
|
const view = svg.append("rect")
|
||||||
|
.attr("class", "view")
|
||||||
|
.attr("x", 0)
|
||||||
|
.attr("y", 0);
|
||||||
|
|
||||||
|
|
||||||
|
function convert_data(nodes_raw) {
|
||||||
|
let edges = [];
|
||||||
|
let node_id = new Map();
|
||||||
|
nodes = [];
|
||||||
|
for (let i in nodes_raw) {
|
||||||
|
node_id.set(nodes_raw[i], i);
|
||||||
|
nodes.push({id: i, color: 0, data: nodes_raw[i]});
|
||||||
|
}
|
||||||
|
for (const node of nodes_raw) {
|
||||||
|
const to = node_id.get(node);
|
||||||
|
for (const input of node.inputs) {
|
||||||
|
const from = node_id.get(input);
|
||||||
|
let name = input.name;
|
||||||
|
let link_name = node.input_translation.get(name);
|
||||||
|
edges.push({
|
||||||
|
source: from,
|
||||||
|
target: to,
|
||||||
|
name: link_name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
nodes: nodes,
|
||||||
|
links: edges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_svg(data, redraw_func) {
|
||||||
|
// Initialize the links
|
||||||
|
var link = svg
|
||||||
|
.selectAll("line")
|
||||||
|
.data(data.links)
|
||||||
|
.enter()
|
||||||
|
.append("line")
|
||||||
|
.style("stroke", "#aaa")
|
||||||
|
|
||||||
|
// Initialize the nodes
|
||||||
|
let node = svg
|
||||||
|
.selectAll("g")
|
||||||
|
.data(data.nodes);
|
||||||
|
|
||||||
|
let node_enter = node.enter()
|
||||||
|
.append('g')
|
||||||
|
|
||||||
|
let circles = node_enter.append("circle")
|
||||||
|
.attr("r", 20)
|
||||||
|
.style("fill", ({id, color, data}) => colors[color])
|
||||||
|
|
||||||
|
node_enter.append('text')
|
||||||
|
.attr("dx", -20)
|
||||||
|
.attr("dy", -22)
|
||||||
|
.style('fill', 'white')
|
||||||
|
.text(({id, color, data}) => data.name);
|
||||||
|
|
||||||
|
// Let's list the force we wanna apply on the network
|
||||||
|
var simulation = d3.forceSimulation(data.nodes) // Force algorithm is applied to data.nodes
|
||||||
|
.force("link", d3.forceLink().strength(0.1) // This force provides links between nodes
|
||||||
|
.id(function(d) { return d.id; }) // This provide the id of a node
|
||||||
|
.links(data.links) // and this the list of links
|
||||||
|
)
|
||||||
|
.force("charge", d3.forceManyBody().strength(-400)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength
|
||||||
|
//.force("center", d3.forceCenter(_bbox.width / 2, _bbox.height / 2).strength(0.1)) // This force attracts nodes to the center of the svg area
|
||||||
|
.on("tick", ticked);
|
||||||
|
// This function is run at each iteration of the force algorithm, updating the nodes position.
|
||||||
|
let scale_transform = {k: 1, x: 0, y: 0}
|
||||||
|
function ticked() {
|
||||||
|
link
|
||||||
|
.attr("x1", function(d) { return d.source.x; })
|
||||||
|
.attr("y1", function(d) { return d.source.y; })
|
||||||
|
.attr("x2", function(d) { return d.target.x; })
|
||||||
|
.attr("y2", function(d) { return d.target.y; });
|
||||||
|
|
||||||
|
node_enter.attr("transform", function (d) { return 'translate('+scale_transform.x+','+scale_transform.y+') scale('+scale_transform.k+') translate('+d.x+','+d.y+')' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const drag = d3.drag()
|
||||||
|
.on("start", dragstart)
|
||||||
|
.on("drag", dragged);
|
||||||
|
|
||||||
|
node_enter.call(drag).on('click', click);
|
||||||
|
function click(event, d) {
|
||||||
|
if (event.ctrlKey) {
|
||||||
|
// Color cycle.
|
||||||
|
d.color = (d.color + 1) % n_colors;
|
||||||
|
d3.select(this).selectAll('circle').style("fill", ({id, color, data}) => colors[color])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete d.fx;
|
||||||
|
delete d.fy;
|
||||||
|
d3.select(this).classed("fixed", false);
|
||||||
|
simulation.alpha(0.5).restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragstart() {
|
||||||
|
d3.select(this).classed("fixed", true);
|
||||||
|
}
|
||||||
|
function dragged(event, d) {
|
||||||
|
d.fx = event.x;
|
||||||
|
d.fy = event.y;
|
||||||
|
simulation.alpha(0.5).restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
const zoom = d3.zoom()
|
||||||
|
.scaleExtent([0.01, 10])
|
||||||
|
.translateExtent([[-10000, -10000], [10000, 10000]])
|
||||||
|
.filter(filter)
|
||||||
|
.on("zoom", zoomed);
|
||||||
|
view.call(zoom);
|
||||||
|
|
||||||
|
function zoomed({ transform }) {
|
||||||
|
link.attr('transform', transform);
|
||||||
|
scale_transform = transform;
|
||||||
|
node_enter.attr("transform", function (d) { return 'translate('+scale_transform.x+','+scale_transform.y+') scale('+scale_transform.k+') translate('+d.x+','+d.y+')' })
|
||||||
|
redraw_func();
|
||||||
|
}
|
||||||
|
// prevent scrolling then apply the default filter
|
||||||
|
function filter(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_export_button(svg, 'saveButton', 'saveLink');
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
|
||||||
|
// JANKY
|
||||||
|
while (edit_id_output === undefined) {
|
||||||
|
await sleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function redraw() {
|
||||||
|
_bbox = bbox();
|
||||||
|
graph.attr("viewBox", [0, 0, _bbox.width, _bbox.height]);
|
||||||
|
view.attr("width", _bbox.width - 1)
|
||||||
|
.attr("height", _bbox.height - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
d3.select(window)
|
||||||
|
.on("resize", function() {
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
redraw();
|
||||||
|
|
||||||
|
const data = convert_data(all_nodes);
|
||||||
|
create_svg(data, redraw);
|
||||||
|
|
||||||
|
console.log("render");
|
||||||
|
|
||||||
|
})();
|
|
@ -1,7 +1,6 @@
|
||||||
function calculate_skillpoints(equipment, weapon) {
|
function calculate_skillpoints(equipment, weapon) {
|
||||||
// Calculate equipment equipping order and required skillpoints.
|
// Calculate equipment equipping order and required skillpoints.
|
||||||
// Return value: [equip_order, best_skillpoints, final_skillpoints, best_total];
|
// Return value: [equip_order, best_skillpoints, final_skillpoints, best_total];
|
||||||
|
|
||||||
let fixed = [];
|
let fixed = [];
|
||||||
let consider = [];
|
let consider = [];
|
||||||
let noboost = [];
|
let noboost = [];
|
||||||
|
@ -32,14 +31,14 @@ function calculate_skillpoints(equipment, weapon) {
|
||||||
let setCount = activeSetCounts.get(setName);
|
let setCount = activeSetCounts.get(setName);
|
||||||
let old_bonus = {};
|
let old_bonus = {};
|
||||||
if (setCount) {
|
if (setCount) {
|
||||||
old_bonus = sets[setName].bonuses[setCount-1];
|
old_bonus = sets.get(setName).bonuses[setCount-1];
|
||||||
activeSetCounts.set(setName, setCount + 1);
|
activeSetCounts.set(setName, setCount + 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setCount = 0;
|
setCount = 0;
|
||||||
activeSetCounts.set(setName, 1);
|
activeSetCounts.set(setName, 1);
|
||||||
}
|
}
|
||||||
const new_bonus = sets[setName].bonuses[setCount];
|
const new_bonus = sets.get(setName).bonuses[setCount];
|
||||||
//let skp_order = ["str","dex","int","def","agi"];
|
//let skp_order = ["str","dex","int","def","agi"];
|
||||||
for (const i in skp_order) {
|
for (const i in skp_order) {
|
||||||
const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
||||||
|
@ -75,8 +74,8 @@ function calculate_skillpoints(equipment, weapon) {
|
||||||
if (setName) { // undefined/null means no set.
|
if (setName) { // undefined/null means no set.
|
||||||
const setCount = activeSetCounts.get(setName);
|
const setCount = activeSetCounts.get(setName);
|
||||||
if (setCount) {
|
if (setCount) {
|
||||||
const old_bonus = sets[setName].bonuses[setCount-1];
|
const old_bonus = sets.get(setName).bonuses[setCount-1];
|
||||||
const new_bonus = sets[setName].bonuses[setCount];
|
const new_bonus = sets.get(setName).bonuses[setCount];
|
||||||
//let skp_order = ["str","dex","int","def","agi"];
|
//let skp_order = ["str","dex","int","def","agi"];
|
||||||
for (const i in skp_order) {
|
for (const i in skp_order) {
|
||||||
const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
const set_delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
|
||||||
|
|
34
js/sq2icons.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//which icons to use
|
||||||
|
let window_storage = window.localStorage;
|
||||||
|
console.log(window_storage);
|
||||||
|
icon_state_stored = window_storage.getItem("newicons");
|
||||||
|
newIcons = true;
|
||||||
|
if (icon_state_stored === "false") {toggleIcons()}
|
||||||
|
|
||||||
|
|
||||||
|
/** Toggle icons on the ENTIRE page.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function toggleIcons() {
|
||||||
|
newIcons = !newIcons;
|
||||||
|
let imgs = document.getElementsByTagName("IMG");
|
||||||
|
let favicon = document.querySelector("link[rel~='icon']");
|
||||||
|
|
||||||
|
if (newIcons) { //switch to new
|
||||||
|
favicon.href = favicon.href.replace("media/icons/old","media/icons/new");
|
||||||
|
for (const img of imgs) {
|
||||||
|
if (img.src.includes("media/icons/old")) {img.src = img.src.replace("media/icons/old","media/icons/new");}
|
||||||
|
if (img.src.includes("media/items/old")) {img.src = img.src.replace("media/items/old","media/items/new");}
|
||||||
|
}
|
||||||
|
//toggleiconbutton.textContent = "Use Old Icons";
|
||||||
|
window_storage.setItem("newicons","true");
|
||||||
|
} else { //switch to old
|
||||||
|
favicon.href = favicon.href.replace("media/icons/new","media/icons/old");
|
||||||
|
for (const img of imgs) {
|
||||||
|
if (img.src.includes("media/icons/new")) {img.src = img.src.replace("media/icons/new","media/icons/old");}
|
||||||
|
if (img.src.includes("media/items/new")) {img.src = img.src.replace("media/items/new","media/items/old");}
|
||||||
|
}
|
||||||
|
//toggleiconbutton.textContent = "Use New Icons";
|
||||||
|
window_storage.setItem("newicons","false");
|
||||||
|
}
|
||||||
|
}
|
256
js/sq2items.js
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
let filterInputs = new Map([["item-category", ["ALL", "armor", "helmet", "chestplate", "leggings", "boots", "accessory", "ring", "bracelet", "necklace", "weapon", "wand", "spear", "bow", "dagger", "relik"]],
|
||||||
|
["item-rarity", ["ANY", "Normal", "Unique", "Set", "Rare", "Legendary", "Fabled", "Mythic", "Sane"]],
|
||||||
|
["filter1", sq2ItemFilters],
|
||||||
|
["filter2", sq2ItemFilters],
|
||||||
|
["filter3", sq2ItemFilters],
|
||||||
|
["filter4", sq2ItemFilters]]);
|
||||||
|
for (const [field, data] of filterInputs) {
|
||||||
|
let field_choice = document.getElementById(field+"-choice");
|
||||||
|
// show dropdown on click
|
||||||
|
field_choice.onclick = function() {field_choice.dispatchEvent(new Event('input', {bubbles:true}));};
|
||||||
|
filterInputs.set(field, new autoComplete({
|
||||||
|
data: {
|
||||||
|
src: data,
|
||||||
|
},
|
||||||
|
threshold: 0,
|
||||||
|
selector: "#"+ field +"-choice",
|
||||||
|
wrapper: false,
|
||||||
|
resultsList: {
|
||||||
|
maxResults: 100,
|
||||||
|
tabSelect: true,
|
||||||
|
noResults: true,
|
||||||
|
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
|
||||||
|
element: (list, data) => {
|
||||||
|
let position = document.getElementById(field+'-choice').getBoundingClientRect();
|
||||||
|
list.style.top = position.bottom + window.scrollY +"px";
|
||||||
|
list.style.left = position.x+"px";
|
||||||
|
list.style.width = position.width+"px";
|
||||||
|
list.style.maxHeight = position.height * 4 +"px";
|
||||||
|
|
||||||
|
if (!data.results.length) {
|
||||||
|
message = document.createElement('li');
|
||||||
|
message.classList.add('scaled-font');
|
||||||
|
message.textContent = "No results found!";
|
||||||
|
list.prepend(message);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resultItem: {
|
||||||
|
class: "scaled-font search-item",
|
||||||
|
selected: "dark-5",
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
input: {
|
||||||
|
selection: (event) => {
|
||||||
|
if (event.detail.selection.value) {
|
||||||
|
event.target.value = event.detail.selection.value;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const sq2_translate_mappings = {
|
||||||
|
//"Name": "name",
|
||||||
|
//"Display Name": "displayName",
|
||||||
|
//"tier"Tier": ",
|
||||||
|
//"Set": "set",
|
||||||
|
"Powder Slots": "slots",
|
||||||
|
//"Type": "type",
|
||||||
|
//"armorType", (deleted)
|
||||||
|
//"color", (deleted)
|
||||||
|
//"lore", (deleted)
|
||||||
|
//"material", (deleted)
|
||||||
|
"Drop type": "drop",
|
||||||
|
"Quest requirement": "quest",
|
||||||
|
"Restriction": "restrict",
|
||||||
|
//"Base Neutral Damage": "nDam",
|
||||||
|
//"Base Fire Damage": "fDam",
|
||||||
|
//"Base Water Damage": "wDam",
|
||||||
|
//"Base Air Damage": "aDam",
|
||||||
|
//"Base Thunder Damage": "tDam",
|
||||||
|
//"Base Earth Damage": "eDam",
|
||||||
|
//"Base Attack Speed": "atkSpd",
|
||||||
|
"Health": "hp",
|
||||||
|
"Raw Fire Defense": "fDef",
|
||||||
|
"Raw Water Defense": "wDef",
|
||||||
|
"Raw Air Defense": "aDef",
|
||||||
|
"Raw Thunder Defense": "tDef",
|
||||||
|
"Raw Earth Defense": "eDef",
|
||||||
|
"Combat Level": "lvl",
|
||||||
|
//"Class Requirement": "classReq",
|
||||||
|
"Req Strength": "strReq",
|
||||||
|
"Req Dexterity": "dexReq",
|
||||||
|
"Req Intelligence": "intReq",
|
||||||
|
"Req Agility": "agiReq",
|
||||||
|
"Req Defense": "defReq",
|
||||||
|
"% Health Regen": "hprPct",
|
||||||
|
"Mana Regen": "mr",
|
||||||
|
"% Spell Damage": "sdPct",
|
||||||
|
"% Melee Damage": "mdPct",
|
||||||
|
"Life Steal": "ls",
|
||||||
|
"Mana Steal": "ms",
|
||||||
|
"XP Bonus": "xpb",
|
||||||
|
"Loot Bonus": "lb",
|
||||||
|
"Reflection": "ref",
|
||||||
|
"Strength": "str",
|
||||||
|
"Dexterity": "dex",
|
||||||
|
"Intelligence": "int",
|
||||||
|
"Agility": "agi",
|
||||||
|
"Defense": "def",
|
||||||
|
"Thorns": "thorns",
|
||||||
|
"Exploding": "expd",
|
||||||
|
"Walk Speed": "spd",
|
||||||
|
"Attack Speed Bonus": "atkTier",
|
||||||
|
"Poison": "poison",
|
||||||
|
"Health Bonus": "hpBonus",
|
||||||
|
"Soul Point Regen": "spRegen",
|
||||||
|
"Stealing": "eSteal",
|
||||||
|
"Raw Health Regen": "hprRaw",
|
||||||
|
"Raw Spell": "sdRaw",
|
||||||
|
"Raw Melee": "mdRaw",
|
||||||
|
"% Fire Damage": "fDamPct",
|
||||||
|
"% Water Damage": "wDamPct",
|
||||||
|
"% Air Damage": "aDamPct",
|
||||||
|
"% Thunder Damage": "tDamPct",
|
||||||
|
"% Earth Damage": "eDamPct",
|
||||||
|
"% Fire Defense": "fDefPct",
|
||||||
|
"% Water Defense": "wDefPct",
|
||||||
|
"% Air Defense": "aDefPct",
|
||||||
|
"% Thunder Defense": "tDefPct",
|
||||||
|
"% Earth Defense": "eDefPct",
|
||||||
|
"Fixed IDs": "fixID",
|
||||||
|
"Custom Skin": "skin",
|
||||||
|
//"Item Category": "category",
|
||||||
|
|
||||||
|
"1st Spell Cost %": "spPct1",
|
||||||
|
"1st Spell Cost Raw": "spRaw1",
|
||||||
|
"2nd Spell Cost %": "spPct2",
|
||||||
|
"2nd Spell Cost Raw": "spRaw2",
|
||||||
|
"3rd Spell Cost %": "spPct3",
|
||||||
|
"3rd Spell Cost Raw": "spRaw3",
|
||||||
|
"4th Spell Cost %": "spPct4",
|
||||||
|
"4th Spell Cost Raw": "spRaw4",
|
||||||
|
|
||||||
|
"Rainbow Spell Damage": "rainbowRaw",
|
||||||
|
"Sprint": "sprint",
|
||||||
|
"Sprint Regen": "sprintReg",
|
||||||
|
"Jump Height": "jh",
|
||||||
|
"Loot Quality": "lq",
|
||||||
|
|
||||||
|
"Gather XP Bonus": "gXp",
|
||||||
|
"Gather Speed Bonus": "gSpd",
|
||||||
|
};
|
||||||
|
|
||||||
|
const sq2_special_mappings = {
|
||||||
|
"Sum (skill points)": new SumQuery(["str", "dex", "int", "def", "agi"]),
|
||||||
|
"Sum (Mana Sustain)": new SumQuery(["mr", "ms"]),
|
||||||
|
"Sum (Life Sustain)": new SumQuery(["hpr", "ls"]),
|
||||||
|
"Sum (Health + Health Bonus)": new SumQuery(["hp", "hpBonus"]),
|
||||||
|
"No Strength Req": new NegateQuery("strReq"),
|
||||||
|
"No Dexterity Req": new NegateQuery("dexReq"),
|
||||||
|
"No Intelligence Req": new NegateQuery("intReq"),
|
||||||
|
"No Agility Req": new NegateQuery("agiReq"),
|
||||||
|
"No Defense Req": new NegateQuery("defReq"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let sq2ItemFilters = []
|
||||||
|
for (let x in sq2_translate_mappings) {
|
||||||
|
sq2ItemFilters.push(x);
|
||||||
|
}
|
||||||
|
for (let x in sq2_special_mappings) {
|
||||||
|
sq2ItemFilters.push(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyQuery(items, query) {
|
||||||
|
return items.filter(query.filter, query).sort(query.compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayItems(items_copy) {
|
||||||
|
let items_parent = document.getElementById("search-results");
|
||||||
|
for (let i in items_copy) {
|
||||||
|
if (i > 200) {break;}
|
||||||
|
let item = items_copy[i];
|
||||||
|
let box = document.createElement("div");
|
||||||
|
box.classList.add("col-lg-3", "col-sm-6", "p-2");
|
||||||
|
box.id = "item"+i;
|
||||||
|
box.addEventListener("dblclick", function() {set_item(item);});
|
||||||
|
|
||||||
|
let bckgrdbox = document.createElement("div");
|
||||||
|
bckgrdbox.classList.add("dark-7", "rounded", "px-2", "col-auto");
|
||||||
|
box.appendChild(bckgrdbox);
|
||||||
|
bckgrdbox.id = "item"+i+"b";
|
||||||
|
items_parent.appendChild(box);
|
||||||
|
displaysq2ExpandedItem(item, bckgrdbox.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let items_expanded;
|
||||||
|
|
||||||
|
function doItemSearch() {
|
||||||
|
// window.scrollTo(0, 0);
|
||||||
|
let queries = [];
|
||||||
|
queries.push(new NameQuery(document.getElementById("item-name-choice").value.trim()));
|
||||||
|
|
||||||
|
let categoryOrType = document.getElementById("item-category-choice").value;
|
||||||
|
if (itemTypes.includes(categoryOrType)) {
|
||||||
|
queries.push(new IdMatchQuery("type", categoryOrType));
|
||||||
|
}
|
||||||
|
else if (itemCategories.includes(categoryOrType)) {
|
||||||
|
queries.push(new IdMatchQuery("category", categoryOrType));
|
||||||
|
}
|
||||||
|
|
||||||
|
let rarity = document.getElementById("item-rarity-choice").value;
|
||||||
|
if (rarity) {
|
||||||
|
if (rarity === "ANY") {
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
queries.push(new IdMatchQuery("tier", rarity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let level_dat = document.getElementById("item-level-choice").value ? document.getElementById("item-level-choice").value.split("-") : [1, 106];
|
||||||
|
queries.push(new LevelRangeQuery(parseInt(level_dat[0]), parseInt(level_dat[1])));
|
||||||
|
|
||||||
|
for (let i = 1; i <= 4; ++i) {
|
||||||
|
let raw_dat = document.getElementById("filter"+i+"-choice").value;
|
||||||
|
let filter_dat = translate_mappings[raw_dat];
|
||||||
|
if (filter_dat !== undefined) {
|
||||||
|
queries.push(new IdQuery(filter_dat));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
filter_dat = special_mappings[raw_dat];
|
||||||
|
if (filter_dat !== undefined) {
|
||||||
|
queries.push(filter_dat);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let items_copy = items_expanded.slice();
|
||||||
|
document.getElementById("search-results").textContent = "";
|
||||||
|
for (const query of queries) {
|
||||||
|
console.log(items_copy.length);
|
||||||
|
console.log(query, query.filter);
|
||||||
|
items_copy = applyQuery(items_copy, query);
|
||||||
|
console.log(items_copy.length);
|
||||||
|
}
|
||||||
|
document.getElementById("summary").textContent = items_copy.length + " results:"
|
||||||
|
displayItems(items_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetItemSearch() {
|
||||||
|
resetFields = ["item-name-choice", "item-category-choice", "item-rarity-choice", "item-level-choice", "filter1-choice", "filter2-choice", "filter3-choice", "filter4-choice"]
|
||||||
|
for (const field of resetFields) {
|
||||||
|
document.getElementById(field).value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init_items() {
|
||||||
|
items_expanded = items.filter( (i) => !("remapID" in i) ).map( (i) => expandItem(i) );
|
||||||
|
}
|
||||||
|
|
||||||
|
load_init(init_items);
|
418
js/utils.js
|
@ -1,32 +1,8 @@
|
||||||
let getUrl = window.location;
|
let getUrl = window.location;
|
||||||
const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
|
const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
|
||||||
|
|
||||||
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
const zip2 = (a, b) => a.map((k, i) => [k, b[i]]);
|
||||||
|
const zip3 = (a, b, c) => a.map((k, i) => [k, b[i], c[i]]);
|
||||||
//updates all the OGP tags for a webpage. Should be called when build changes
|
|
||||||
function updateOGP() {
|
|
||||||
//update the embed URL
|
|
||||||
let url_elem = document.getElementById("ogp-url");
|
|
||||||
if (url_elem) {
|
|
||||||
url_elem.content = url_base+location.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
//update the embed text content
|
|
||||||
let build_elem = document.getElementById("ogp-build-list");
|
|
||||||
if (build_elem && player_build) {
|
|
||||||
let text = "WynnBuilder build:\n"+
|
|
||||||
"> "+player_build.helmet.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.chestplate.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.leggings.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.boots.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.ring1.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.ring2.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.bracelet.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.necklace.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.weapon.get("displayName")+" ["+player_build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]";
|
|
||||||
build_elem.content = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clamp(num, low, high){
|
function clamp(num, low, high){
|
||||||
return Math.min(Math.max(num, low), high);
|
return Math.min(Math.max(num, low), high);
|
||||||
|
@ -98,8 +74,10 @@ function log(b, n) {
|
||||||
// https://stackoverflow.com/a/27696695
|
// https://stackoverflow.com/a/27696695
|
||||||
// Modified for fixed precision
|
// Modified for fixed precision
|
||||||
|
|
||||||
|
// Base64.fromInt(-2147483648); // gives "200000"
|
||||||
|
// Base64.toInt("200000"); // gives -2147483648
|
||||||
Base64 = (function () {
|
Base64 = (function () {
|
||||||
var digitsStr =
|
var digitsStr =
|
||||||
// 0 8 16 24 32 40 48 56 63
|
// 0 8 16 24 32 40 48 56 63
|
||||||
// v v v v v v v v v
|
// v v v v v v v v v
|
||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
|
||||||
|
@ -149,8 +127,246 @@ Base64 = (function () {
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Base64.fromInt(-2147483648); // gives "200000"
|
|
||||||
// Base64.toInt("200000"); // gives -2147483648
|
/** A class used to represent an arbitrary length bit vector. Very useful for encoding and decoding.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class BitVector {
|
||||||
|
|
||||||
|
/** Constructs an arbitrary-length bit vector.
|
||||||
|
* @class
|
||||||
|
* @param {String | Number} data - The data to append.
|
||||||
|
* @param {Number} length - A set length for the data. Ignored if data is a string.
|
||||||
|
*
|
||||||
|
* The structure of the Uint32Array should be [[last, ..., first], ..., [last, ..., first], [empty space, last, ..., first]]
|
||||||
|
*/
|
||||||
|
constructor(data, length) {
|
||||||
|
let bit_vec = [];
|
||||||
|
|
||||||
|
if (typeof data === "string") {
|
||||||
|
let int = 0;
|
||||||
|
let bv_idx = 0;
|
||||||
|
length = data.length * 6;
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let char = Base64.toInt(data[i]);
|
||||||
|
let pre_pos = bv_idx % 32;
|
||||||
|
int |= (char << bv_idx);
|
||||||
|
bv_idx += 6;
|
||||||
|
let post_pos = bv_idx % 32;
|
||||||
|
if (post_pos < pre_pos) { //we have to have filled up the integer
|
||||||
|
bit_vec.push(int);
|
||||||
|
int = (char >>> (6 - post_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == data.length - 1 && post_pos != 0) {
|
||||||
|
bit_vec.push(int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof data === "number") {
|
||||||
|
if (typeof length === "undefined")
|
||||||
|
if (length < 0) {
|
||||||
|
throw new RangeError("BitVector must have nonnegative length.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert to int just in case
|
||||||
|
data = Math.round(data);
|
||||||
|
|
||||||
|
//range of numbers that won't fit in a uint32
|
||||||
|
if (data > 2**32 - 1 || data < -(2 ** 32 - 1)) {
|
||||||
|
throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector.");
|
||||||
|
}
|
||||||
|
bit_vec.push(data);
|
||||||
|
} else {
|
||||||
|
throw new TypeError("BitVector must be instantiated with a Number or a B64 String");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.length = length;
|
||||||
|
this.bits = new Uint32Array(bit_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return value of bit at index idx.
|
||||||
|
*
|
||||||
|
* @param {Number} idx - The index to read
|
||||||
|
*
|
||||||
|
* @returns The bit value at position idx
|
||||||
|
*/
|
||||||
|
read_bit(idx) {
|
||||||
|
if (idx < 0 || idx >= this.length) {
|
||||||
|
throw new RangeError("Cannot read bit outside the range of the BitVector. ("+idx+" > "+this.length+")");
|
||||||
|
}
|
||||||
|
return ((this.bits[Math.floor(idx / 32)] & (1 << idx)) == 0 ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an integer value (if possible) made from the range of bits [start, end). Undefined behavior if the range to read is too big.
|
||||||
|
*
|
||||||
|
* @param {Number} start - The index to start slicing from. Inclusive.
|
||||||
|
* @param {Number} end - The index to end slicing at. Exclusive.
|
||||||
|
*
|
||||||
|
* @returns An integer representation of the sliced bits.
|
||||||
|
*/
|
||||||
|
slice(start, end) {
|
||||||
|
//TO NOTE: JS shifting is ALWAYS in mod 32. a << b will do a << (b mod 32) implicitly.
|
||||||
|
|
||||||
|
if (end < start) {
|
||||||
|
throw new RangeError("Cannot slice a range where the end is before the start.");
|
||||||
|
} else if (end == start) {
|
||||||
|
return 0;
|
||||||
|
} else if (end - start > 32) {
|
||||||
|
//requesting a slice of longer than 32 bits (safe integer "length")
|
||||||
|
throw new RangeError("Cannot slice a range of longer than 32 bits (unsafe to store in an integer).");
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = 0;
|
||||||
|
if (Math.floor((end - 1) / 32) == Math.floor(start / 32)) {
|
||||||
|
//the range is within 1 uint32 section - do some relatively fast bit twiddling
|
||||||
|
res = (this.bits[Math.floor(start / 32)] & ~((((~0) << ((end - 1))) << 1) | ~((~0) << (start)))) >>> (start % 32);
|
||||||
|
} else {
|
||||||
|
//the number of bits in the uint32s
|
||||||
|
let start_pos = (start % 32);
|
||||||
|
let int_idx = Math.floor(start/32);
|
||||||
|
res = (this.bits[int_idx] & ((~0) << (start))) >>> (start_pos);
|
||||||
|
res |= (this.bits[int_idx + 1] & ~((~0) << (end))) << (32 - start_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// General code - slow
|
||||||
|
// for (let i = start; i < end; i++) {
|
||||||
|
// res |= (get_bit(i) << (i - start));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assign bit at index idx to 1.
|
||||||
|
*
|
||||||
|
* @param {Number} idx - The index to set.
|
||||||
|
*/
|
||||||
|
set_bit(idx) {
|
||||||
|
if (idx < 0 || idx >= this.length) {
|
||||||
|
throw new RangeError("Cannot set bit outside the range of the BitVector.");
|
||||||
|
}
|
||||||
|
this.bits[Math.floor(idx / 32)] |= (1 << idx % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assign bit at index idx to 0.
|
||||||
|
*
|
||||||
|
* @param {Number} idx - The index to clear.
|
||||||
|
*/
|
||||||
|
clear_bit(idx) {
|
||||||
|
if (idx < 0 || idx >= this.length) {
|
||||||
|
throw new RangeError("Cannot clear bit outside the range of the BitVector.");
|
||||||
|
}
|
||||||
|
this.bits[Math.floor(idx / 32)] &= ~(1 << idx % 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a string version of the bit vector in B64. Does not keep the order of elements a sensible human readable format.
|
||||||
|
*
|
||||||
|
* @returns A b64 string representation of the BitVector.
|
||||||
|
*/
|
||||||
|
toB64() {
|
||||||
|
if (this.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
let b64_str = "";
|
||||||
|
let i = 0;
|
||||||
|
while (i < this.length) {
|
||||||
|
b64_str += Base64.fromIntV(this.slice(i, i + 6), 1);
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b64_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a BitVector in bitstring format. Probably only useful for dev debugging.
|
||||||
|
*
|
||||||
|
* @returns A bit string representation of the BitVector. Goes from higher-indexed bits to lower-indexed bits. (n ... 0)
|
||||||
|
*/
|
||||||
|
toString() {
|
||||||
|
let ret_str = "";
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
ret_str = (this.read_bit(i) == 0 ? "0": "1") + ret_str;
|
||||||
|
}
|
||||||
|
return ret_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a BitVector in bitstring format. Probably only useful for dev debugging.
|
||||||
|
*
|
||||||
|
* @returns A bit string representation of the BitVector. Goes from lower-indexed bits to higher-indexed bits. (0 ... n)
|
||||||
|
*/
|
||||||
|
toStringR() {
|
||||||
|
let ret_str = "";
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
ret_str += (this.read_bit(i) == 0 ? "0": "1");
|
||||||
|
}
|
||||||
|
return ret_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Appends data to the BitVector.
|
||||||
|
*
|
||||||
|
* @param {Number | String} data - The data to append.
|
||||||
|
* @param {Number} length - The length, in bits, of the new data. This is ignored if data is a string.
|
||||||
|
*/
|
||||||
|
append(data, length) {
|
||||||
|
if (length < 0) {
|
||||||
|
throw new RangeError("BitVector length must increase by a nonnegative number.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let bit_vec = [];
|
||||||
|
for (const uint of this.bits) {
|
||||||
|
bit_vec.push(uint);
|
||||||
|
}
|
||||||
|
if (typeof data === "string") {
|
||||||
|
let int = bit_vec[bit_vec.length - 1];
|
||||||
|
let bv_idx = this.length;
|
||||||
|
length = data.length * 6;
|
||||||
|
let updated_curr = false;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let char = Base64.toInt(data[i]);
|
||||||
|
let pre_pos = bv_idx % 32;
|
||||||
|
int |= (char << bv_idx);
|
||||||
|
bv_idx += 6;
|
||||||
|
let post_pos = bv_idx % 32;
|
||||||
|
if (post_pos < pre_pos) { //we have to have filled up the integer
|
||||||
|
if (bit_vec.length == this.bits.length && !updated_curr) {
|
||||||
|
bit_vec[bit_vec.length - 1] = int;
|
||||||
|
updated_curr = true;
|
||||||
|
} else {
|
||||||
|
bit_vec.push(int);
|
||||||
|
}
|
||||||
|
int = (char >>> (6 - post_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == data.length - 1) {
|
||||||
|
if (bit_vec.length == this.bits.length && !updated_curr) {
|
||||||
|
bit_vec[bit_vec.length - 1] = int;
|
||||||
|
} else if (post_pos != 0) {
|
||||||
|
bit_vec.push(int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (typeof data === "number") {
|
||||||
|
//convert to int just in case
|
||||||
|
let int = Math.round(data);
|
||||||
|
|
||||||
|
//range of numbers that "could" fit in a uint32 -> [0, 2^32) U [-2^31, 2^31)
|
||||||
|
if (data > 2**32 - 1 || data < -(2 ** 31)) {
|
||||||
|
throw new RangeError("Numerical data has to fit within a 32-bit integer range to instantiate a BitVector.");
|
||||||
|
}
|
||||||
|
//could be split between multiple new ints
|
||||||
|
//reminder that shifts implicitly mod 32
|
||||||
|
bit_vec[bit_vec.length - 1] |= ((int & ~((~0) << length)) << (this.length));
|
||||||
|
if (((this.length - 1) % 32 + 1) + length > 32) {
|
||||||
|
bit_vec.push(int >>> (32 - this.length));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new TypeError("BitVector must be appended with a Number or a B64 String");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bits = new Uint32Array(bit_vec);
|
||||||
|
this.length += length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative.
|
Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative.
|
||||||
|
@ -262,7 +478,7 @@ function randomColor() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random color, but lightning must be relatively high (>0.5).
|
* Generates a random color, but lightning must be relatively high (>0.5).
|
||||||
*
|
*
|
||||||
* @returns a random color in RGB 6-bit form.
|
* @returns a random color in RGB 6-bit form.
|
||||||
*/
|
*/
|
||||||
function randomColorLight() {
|
function randomColorLight() {
|
||||||
|
@ -270,7 +486,7 @@ function randomColorLight() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generates a random color given HSL restrictions.
|
/** Generates a random color given HSL restrictions.
|
||||||
*
|
*
|
||||||
* @returns a random color in RGB 6-bit form.
|
* @returns a random color in RGB 6-bit form.
|
||||||
*/
|
*/
|
||||||
function randomColorHSL(h,s,l) {
|
function randomColorHSL(h,s,l) {
|
||||||
|
@ -322,8 +538,8 @@ function randomColorHSL(h,s,l) {
|
||||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a tooltip.
|
/** Creates a tooltip.
|
||||||
*
|
*
|
||||||
* @param {DOM Element} elem - the element to make a tooltip
|
* @param {DOM Element} elem - the element to make a tooltip
|
||||||
* @param {String} element_type - the HTML element type that the tooltiptext should be.
|
* @param {String} element_type - the HTML element type that the tooltiptext should be.
|
||||||
* @param {String} tooltiptext - the text to display in the tooltip.
|
* @param {String} tooltiptext - the text to display in the tooltip.
|
||||||
|
@ -354,7 +570,7 @@ function createTooltip(elem, element_type, tooltiptext, parent, classList) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A generic function that toggles the on and off state of a button.
|
/** A generic function that toggles the on and off state of a button.
|
||||||
*
|
*
|
||||||
* @param {String} button_id - the id name of the button.
|
* @param {String} button_id - the id name of the button.
|
||||||
*/
|
*/
|
||||||
function toggleButton(button_id) {
|
function toggleButton(button_id) {
|
||||||
|
@ -398,14 +614,142 @@ function addClasses(elem, classes) {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A utility function that reloads the page forcefully.
|
/** A utility function that reloads the page forcefully.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
async function hardReload() {
|
async function hardReload() {
|
||||||
//https://gist.github.com/rmehner/b9a41d9f659c9b1c3340
|
//https://gist.github.com/rmehner/b9a41d9f659c9b1c3340
|
||||||
const dbs = await window.indexedDB.databases();
|
const dbs = await window.indexedDB.databases();
|
||||||
await dbs.forEach(db => { window.indexedDB.deleteDatabase(db.name) });
|
await dbs.forEach(db => { window.indexedDB.deleteDatabase(db.name) });
|
||||||
|
|
||||||
location.reload();
|
location.reload(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function capitalizeFirst(str) {
|
||||||
|
return str[0].toUpperCase() + str.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** https://stackoverflow.com/questions/16839698/jquery-getscript-alternative-in-native-javascript
|
||||||
|
* If we ever want to write something that needs to import other js files
|
||||||
|
*/
|
||||||
|
const getScript = url => new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = url;
|
||||||
|
script.async = true;
|
||||||
|
|
||||||
|
script.onerror = reject;
|
||||||
|
|
||||||
|
script.onload = script.onreadystatechange = function () {
|
||||||
|
const loadState = this.readyState;
|
||||||
|
|
||||||
|
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
|
||||||
|
|
||||||
|
script.onload = script.onreadystatechange = null;
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.head.appendChild(script);
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
GENERIC TEST FUNCTIONS
|
||||||
|
*/
|
||||||
|
/** The generic assert function. Fails on all "false-y" values. Useful for non-object equality checks, boolean value checks, and existence checks.
|
||||||
|
*
|
||||||
|
* @param {*} arg - argument to assert.
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert(arg, msg) {
|
||||||
|
if (!arg) {
|
||||||
|
throw new Error(msg ? msg : "Assert failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts object equality of the 2 parameters. For loose and strict asserts, use assert().
|
||||||
|
*
|
||||||
|
* @param {*} arg1 - first argument to compare.
|
||||||
|
* @param {*} arg2 - second argument to compare.
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert_equals(arg1, arg2, msg) {
|
||||||
|
if (!Object.is(arg1, arg2)) {
|
||||||
|
throw new Error(msg ? msg : "Assert Equals failed. " + arg1 + " is not " + arg2 + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts object inequality of the 2 parameters. For loose and strict asserts, use assert().
|
||||||
|
*
|
||||||
|
* @param {*} arg1 - first argument to compare.
|
||||||
|
* @param {*} arg2 - second argument to compare.
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert_not_equals(arg1, arg2, msg) {
|
||||||
|
if (Object.is(arg1, arg2)) {
|
||||||
|
throw new Error(msg ? msg : "Assert Not Equals failed. " + arg1 + " is " + arg2 + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts proximity between 2 arguments. Should be used for any floating point datatype.
|
||||||
|
*
|
||||||
|
* @param {*} arg1 - first argument to compare.
|
||||||
|
* @param {*} arg2 - second argument to compare.
|
||||||
|
* @param {Number} epsilon - the margin of error (<= del difference is ok). Defaults to -1E5.
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert_near(arg1, arg2, epsilon = 1E-5, msg) {
|
||||||
|
if (Math.abs(arg1 - arg2) > epsilon) {
|
||||||
|
throw new Error(msg ? msg : "Assert Near failed. " + arg1 + " is not within " + epsilon + " of " + arg2 + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that the input argument is null.
|
||||||
|
*
|
||||||
|
* @param {*} arg - the argument to test for null.
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert_null(arg, msg) {
|
||||||
|
if (arg !== null) {
|
||||||
|
throw new Error(msg ? msg : "Assert Near failed. " + arg + " is not null.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that the input argument is undefined.
|
||||||
|
*
|
||||||
|
* @param {*} arg - the argument to test for undefined.
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert_undefined(arg, msg) {
|
||||||
|
if (arg !== undefined) {
|
||||||
|
throw new Error(msg ? msg : "Assert Near failed. " + arg + " is not undefined.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asserts that there is an error when a callback function is run.
|
||||||
|
*
|
||||||
|
* @param {Function} func_binding - a function binding to run. Can be passed in with func.bind(null, arg1, ..., argn)
|
||||||
|
* @param {String} msg - the error message to throw.
|
||||||
|
*/
|
||||||
|
function assert_error(func_binding, msg) {
|
||||||
|
try {
|
||||||
|
func_binding();
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(msg ? msg : "Function didn't throw an error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deep copy object/array of basic types.
|
||||||
|
*/
|
||||||
|
function deepcopy(obj) {
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
132
map.html
|
@ -1,132 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html scroll-behavior="smooth">
|
|
||||||
<head>
|
|
||||||
<meta name="HandheldFriendly" content="true" />
|
|
||||||
<meta name="MobileOptimized" content="320" />
|
|
||||||
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
|
|
||||||
<!-- nunito font, copying wynndata -->
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/styles.css">
|
|
||||||
<link rel="stylesheet" media="screen and (min-width: 900px)" href="/css/map-wide.css"/>
|
|
||||||
<link rel="stylesheet" media="screen and (max-width: 899px)" href="/css/map-narrow.css"/>
|
|
||||||
<link rel="icon" href="./media/icons/new/compass2.png">
|
|
||||||
<link rel="manifest" href="manifest.json">
|
|
||||||
|
|
||||||
<!--Leaflet for map-->
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
|
|
||||||
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
|
|
||||||
crossorigin="anonymous"/>
|
|
||||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
|
|
||||||
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
|
|
||||||
crossorigin="anonymous"></script>
|
|
||||||
|
|
||||||
<title>WynnGPS</title>
|
|
||||||
</head>
|
|
||||||
<body class="all">
|
|
||||||
<div class="center">
|
|
||||||
<header class = "header nomarginp">
|
|
||||||
<div class = "headerleft" id = "headerleft">
|
|
||||||
</div>
|
|
||||||
<div class = "headercenter" id = "headercenter">
|
|
||||||
<div>
|
|
||||||
<p class = "itemp" id = "header">WynnGPS</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class = "headerright" id = "headerright">
|
|
||||||
<p class = "center">Right click to place marker.</p>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div class = "overall-container" display = "grid">
|
|
||||||
<div id = "mapdiv" class ="mapdiv container" display = "grid-item-1">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id = "mapoptions-container" class = "container" display = "grid-item-2">
|
|
||||||
<div id = "coord-container" class = "center coord-container" display = "grid">
|
|
||||||
<p class = "nomargin"></p>
|
|
||||||
<p class = "title center nomargin">Options</p>
|
|
||||||
<p class = "nomargin"></p>
|
|
||||||
<p class = "nomargin" display = "grid-item-1">X</p>
|
|
||||||
<p class = "nomargin" display = "grid-item-2"></p>
|
|
||||||
<p class = "nomargin" display = "grid-item-3">Z</p>
|
|
||||||
<p class = "nomargin" id = "coord-x" display = "grid-item-4"></p>
|
|
||||||
<p class = "nomargin" id = "coord-img" display = "grid-item-5">
|
|
||||||
<img src = "/media/icons/new/compass2.png" alt style = "max-width:32px; max-height:32px"/>
|
|
||||||
</p>
|
|
||||||
<p class = "nomargin" id = "coord-z" display = "grid-item-6"></p>
|
|
||||||
<p class = "nomargin" id = "marker-coord-x" display = "none"></p>
|
|
||||||
<p class = "nomargin" id = "marker-coord-img" display = "none">
|
|
||||||
<img src = "/media/icons/new/marker.png" alt style = "max-width:32px; max-height:32px"/>
|
|
||||||
</p>
|
|
||||||
<p class = "nomargin" id = "marker-coord-z" display = "none"></p>
|
|
||||||
</div>
|
|
||||||
<div id = "button-choices container">
|
|
||||||
<button class = "left" id = "territories-button" onclick = "toggleButton('territories-button'); toggleTerritories()">Show Territories</button>
|
|
||||||
<button class = "left" id = "claims-button" onclick = "toggleButton('claims-button'); toggleClaims()">Show Claims</button>
|
|
||||||
<button class = "left" id = "routes-button" onclick = "toggleButton('routes-button'); toggleRoutes()">Show Routes</button>
|
|
||||||
<button class = "left" id = "resources-button" onclick = "toggleButton('resources-button'); toggleResources()">Show Resources</button>
|
|
||||||
<button class = "left" id = "locations-button" onclick = "toggleButton('locations-button'); toggleLocations()">Show Locations</button>
|
|
||||||
<button class = "left" id = "pull-button" onclick = "refreshData()">Refresh Data</button>
|
|
||||||
<p class = "left" style = "color:red">Do NOT refresh too often.</p>
|
|
||||||
</div>
|
|
||||||
<div id ="territory-stats">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id = "key-container" class = "container">
|
|
||||||
<div id = "key-title" class = "center">
|
|
||||||
<p class = "center title"> All Keys </p>
|
|
||||||
</div>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div id = "guild-key" style = "display:none">
|
|
||||||
<p class = "left">Guild Key:</p>
|
|
||||||
<ul id = "guildkeylist">
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div id = "resources-key" style = "display:none">
|
|
||||||
<p class = "left">Resource Key:</p>
|
|
||||||
<ul id = "resourcelist">
|
|
||||||
<li><img src= "media/icons/new/Emeralds.png" style ="max-width:16px;max-height:16px" class = "Emeralds"/> Emeralds</li>
|
|
||||||
<li><img src= "media/icons/new/Ore.png" style ="max-width:16px;max-height:16px" class = "Ore"/> Ore</li>
|
|
||||||
<li><img src= "media/icons/new/Wood.png" style ="max-width:16px;max-height:16px" class = "Wood"/> Wood</li>
|
|
||||||
<li><img src= "media/icons/new/Crops.png" style ="max-width:16px;max-height:16px" class = "Crops"/> Crops</li>
|
|
||||||
<li><img src= "media/icons/new/Fish.png" style ="max-width:16px;max-height:16px" class = "Fish"/> Fish</li>
|
|
||||||
<li><img src= "media/icons/new/Chest.png" style ="max-width:16px;max-height:16px" class = "Wood"/> Storage</li>
|
|
||||||
<li><img src= "media/icons/new/Gears.png" style ="max-width:16px;max-height:16px" class = "Ore"/> Production</li>
|
|
||||||
<li>Double image means double generation</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div id = "locations-key" style = "display:none">
|
|
||||||
<p class = "left">Locations Key:</p>
|
|
||||||
<ul id = "locationlist">
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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/load_map.js"></script>
|
|
||||||
<script type="text/javascript" src="/js/map.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
164
map/index.html
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html scroll-behavior="smooth">
|
||||||
|
<head>
|
||||||
|
<meta name="HandheldFriendly" content="true" />
|
||||||
|
<meta name="MobileOptimized" content="320" />
|
||||||
|
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width, user-scalable=no" />
|
||||||
|
<!-- nunito font, copying wynndata -->
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.6/dist/css/autoComplete.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" media="screen and (min-width: 900px)" href="/css/map-wide.css"/>
|
||||||
|
<link rel="stylesheet" media="screen and (max-width: 899px)" href="/css/map-narrow.css"/>
|
||||||
|
<link rel="stylesheet" href="../css/sq2bs.css">
|
||||||
|
<link rel="stylesheet" href="../css/sidebar.css">
|
||||||
|
<link rel="stylesheet" href="../css/wynnstyles.css">
|
||||||
|
<link rel="icon" href="../media/icons/new/compass2.png">
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
|
||||||
|
<!--Leaflet for map-->
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
|
||||||
|
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
|
||||||
|
crossorigin="anonymous"/>
|
||||||
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
|
||||||
|
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<title>WynnGPS</title>
|
||||||
|
</head>
|
||||||
|
<body class = "text-light d-flex justify-content-center" id = "body">
|
||||||
|
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
|
||||||
|
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
|
||||||
|
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
|
||||||
|
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
|
||||||
|
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
|
||||||
|
<a href = ""><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 = "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 row py-5 vh-100 mx-0 mx-lg-auto scaled-font">
|
||||||
|
<div id = "mapdiv" class = "col-lg-8 col-sm-12 rounded border border-light border-3">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id = "mapoptions-container" class = "col-lg-3 col-sm-12 rounded border border-light border-3 mx-1">
|
||||||
|
<div id = "coord-container" class = "row justify-content-center text-center">
|
||||||
|
<div class = "row big-title justify-content-center">Options</div>
|
||||||
|
<div class = "row justify-content-center">Right click to place marker.</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col-4">X</div>
|
||||||
|
<div class = "col-4"></div>
|
||||||
|
<div class = "col-4">Z</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row mb-1">
|
||||||
|
<div class = "col-4" id = "coord-x"></div>
|
||||||
|
<div class = "col-4">
|
||||||
|
<p class = "nomargin" id = "coord-img">
|
||||||
|
<img src = "../media/icons/new/compass2.png" alt style = "max-width:32px; max-height:32px"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-4" id = "coord-z"></div>
|
||||||
|
</div>
|
||||||
|
<div class = "row mb-1">
|
||||||
|
<div class = "col-4" id = "marker-coord-x"></div>
|
||||||
|
<div class = "col-4">
|
||||||
|
<p class = "nomargin" id = "marker-coord-img" display = "none">
|
||||||
|
<img src = "../media/icons/new/marker.png" alt style = "max-width:32px; max-height:32px"/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class = "col-4" id = "marker-coord-z"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id = "button-choices row">
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col mb-1">
|
||||||
|
<button class = "w-100 button rounded scaled-font fw-bold text-light dark-5" id = "territories-button" onclick = "toggleButton('territories-button'); toggleTerritories()">Show Territories</button>
|
||||||
|
</div>
|
||||||
|
<div class = "col mb-1">
|
||||||
|
<button class = "w-100 button rounded scaled-font fw-bold text-light dark-5" id = "claims-button" onclick = "toggleButton('claims-button'); toggleClaims()">Show Claims</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col mb-1">
|
||||||
|
<button class = "w-100 button rounded scaled-font fw-bold text-light dark-5" id = "routes-button" onclick = "toggleButton('routes-button'); toggleRoutes()">Show Routes</button>
|
||||||
|
</div>
|
||||||
|
<div class = "col mb-1">
|
||||||
|
<button class = "w-100 button rounded scaled-font fw-bold text-light dark-5" id = "resources-button" onclick = "toggleButton('resources-button'); toggleResources()">Show Resources</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<div class = "col mb-1">
|
||||||
|
<button class = "w-100 button rounded scaled-font fw-bold text-light dark-5" id = "locations-button" onclick = "toggleButton('locations-button'); toggleLocations()">Show Locations</button>
|
||||||
|
</div>
|
||||||
|
<div class = "col mb-1">
|
||||||
|
<button class = "w-100 button rounded scaled-font fw-bold text-light dark-5" id = "pull-button" onclick = "refreshData()">Refresh Data</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class = "row">
|
||||||
|
<p class = "warning" style = "color:red">Do NOT refresh too often.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id ="territory-stats">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id = "key-container" class = "row rounded border border-light border-3 my-1">
|
||||||
|
<div id = "key-title" class = "row">
|
||||||
|
<p class = "item-title"> All Keys </p>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div id = "guild-key" style = "display:none">
|
||||||
|
<p class = "left">Guild Key:</p>
|
||||||
|
<ul id = "guildkeylist">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div id = "resources-key" style = "display:none">
|
||||||
|
<p class = "left">Resource Key:</p>
|
||||||
|
<ul id = "resourcelist">
|
||||||
|
<li><img src= "../media/icons/new/Emeralds.png" style ="max-width:16px;max-height:16px" class = "Emeralds"/> Emeralds</li>
|
||||||
|
<li><img src= "../media/icons/new/Ore.png" style ="max-width:16px;max-height:16px" class = "Ore"/> Ore</li>
|
||||||
|
<li><img src= "../media/icons/new/Wood.png" style ="max-width:16px;max-height:16px" class = "Wood"/> Wood</li>
|
||||||
|
<li><img src= "../media/icons/new/Crops.png" style ="max-width:16px;max-height:16px" class = "Crops"/> Crops</li>
|
||||||
|
<li><img src= "../media/icons/new/Fish.png" style ="max-width:16px;max-height:16px" class = "Fish"/> Fish</li>
|
||||||
|
<li><img src= "../media/icons/new/Chest.png" style ="max-width:16px;max-height:16px" class = "Wood"/> Storage</li>
|
||||||
|
<li><img src= "../media/icons/new/Gears.png" style ="max-width:16px;max-height:16px" class = "Ore"/> Production</li>
|
||||||
|
<li>Double image means double generation</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div id = "locations-key" style = "display:none">
|
||||||
|
<p class = "left">Locations Key:</p>
|
||||||
|
<ul id = "locationlist">
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript" src="../js/utils.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/icons.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/load_map.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/map.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
media/atree/connect_angle.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
media/atree/connect_c.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
media/atree/connect_line.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
media/atree/connect_t.png
Normal file
After Width: | Height: | Size: 962 B |
BIN
media/atree/highlight_angle.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
media/atree/highlight_c.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
media/atree/highlight_c_2_a.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
media/atree/highlight_c_2_l.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
media/atree/highlight_c_3.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
media/atree/highlight_line.png
Normal file
After Width: | Height: | Size: 974 B |
BIN
media/atree/highlight_t_2_a.png
Normal file
After Width: | Height: | Size: 708 B |
BIN
media/atree/highlight_t_2_l.png
Normal file
After Width: | Height: | Size: 666 B |
BIN
media/atree/highlight_t_3.png
Normal file
After Width: | Height: | Size: 654 B |
BIN
media/atree/node-blocked.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
media/atree/node-selected.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
media/atree/node.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
media/atree/node_0.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
media/atree/node_0_blocked.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
media/atree/node_1.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
media/atree/node_1_blocked.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
media/atree/node_2.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
media/atree/node_2_blocked.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
media/atree/node_3.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
media/atree/node_3_blocked.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
media/atree/node_4.png
Normal file
After Width: | Height: | Size: 5.2 KiB |