Merge branch 'dev' of github.com:reschan/reschan.github.io into UI_test

This commit is contained in:
hppeng 2022-01-05 16:03:50 -08:00
commit f292b3d4a9
16 changed files with 7658 additions and 0 deletions

3
.gitignore vendored
View file

@ -4,3 +4,6 @@ sets/
.idea/
*.iml
node_modules/
package.json
package-lock.json

BIN
media/icons/new/copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

BIN
media/icons/new/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

349
normalize.css vendored Normal file
View file

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

440
sq2.css Normal file
View file

@ -0,0 +1,440 @@
html {
height: 100%;
}
* {
font-family: 'Nunito', sans-serif;
}
input::-webkit-calendar-picker-indicator {
display: none !important;
}
input {
-webkit-appearance : none;
border-radius : 0;
}
textarea, input { outline: none; }
body {
height: 100%;
overflow: hidden; /* makes the body non-scrollable (we will add scrolling to the sidebar and main content containers) */
margin: 0; /* removes default style */
display: flex; /* enables flex content for its children */
box-sizing: border-box;
font-family: 'Nunito', sans-serif;
}
/* Works on Firefox */
* {
scrollbar-width: thin;
scrollbar-color: rgb(30, 30, 30) rgb(45, 45, 45);
}
/* Works on Chrome, Edge, and Safari */
*::-webkit-scrollbar {
width: 5px;
}
*::-webkit-scrollbar-track {
background: rgb(45, 45, 45);
}
*::-webkit-scrollbar-thumb {
background-color: rgb(30, 30, 30);
}
.column {
height: 100%; /* allows both columns to span the full height of the browser window */
display: flex;
flex-direction: column; /* places the left and right headers above the bottom content */
}
#left { /* makes sure that content is not cut off in a smaller browser window */
flex-shrink: 1;
background-color: rgb(30, 30, 30);
}
#right {
flex-grow: 1;
background-color: rgb(40, 40, 40);
color: rgb(240, 240, 240);
padding: 2%;
}
ul {
list-style: none;
padding: 0;
}
img.item-icon {
padding: 0;
margin: 0;
border: none;
}
.bottom {
flex-grow: 1; /* ensures that the container will take up the full height of the parent container */
overflow-y: auto;
overflow-x: auto;
}
table {
vertical-align: top;
}
.full-border, .box {
border: .3vw solid rgb(45, 45, 45);
}
.se-border {
border-left: .3vw solid rgb(45, 45, 45);
border-right: .3vw solid rgb(45, 45, 45);
}
td {
padding: 0;
margin: 0;
white-space: nowrap;
font-size: .8vw;
}
p {
padding: 0;
margin: 0;
padding-top: 1px;
padding-bottom: 1px;
}
input {
background-color: rgb(40, 40, 40);
border-top: .15vw solid rgb(25, 25, 25) !important;
border-left: .15vw solid rgb(25, 25, 25) !important;
border-bottom: .15vw solid rgb(45, 45, 45) !important;
border-right: .15vw solid rgb(45, 45, 45) !important;
box-sizing: border-box !important;
color: rgb(240, 240, 240);
min-width: 0;
min-height: 0;
font-size: .8vw;
box-sizing: none;
}
input.item-name {
text-align: center;
font-weight: bold;
width: 10vw;
height: 1.2vw;
}
input.search-field {
text-align: center;
font-weight: bold;
width: 9em;
}
.equipment-container {
background-color: rgb(30, 30, 30);
flex-direction: column;
display: inline-flex;
}
.all-equipment {
display: inline-flex;
flex-direction: column;
}
.weapon-container {
display: inline-flex;
background-color: rgb(30, 30, 30);
}
.skp-container {
width: 38vw;
height: 10vw;
display: inline-flex;
flex-direction: column;
background-color: rgb(30, 30, 30);
}
td.mono-font {
font-family: 'Courier New', Courier, monospace;
}
td.damage-size {
width: 5vw;
user-select: none;
}
.potency {
width: 36em;
user-select: none;
}
.skp-text {
width: 7.75em;
}
.skp-input {
width: 6vw;
height: 1.2vw;
text-align: center;
}
.center-screen {
display: flex;
justify-content: center;
align-items: center;
}
.powder-input {
text-align: center;
font-family: 'Courier New', Courier, monospace;
font-weight: bold;
width: 10vw;
height: 1.2vw;
}
.skp-tooltip {
font-size: .6vw;
}
.spell-container {
display: inline-flex;
vertical-align: top;
background-color: rgb(30, 30, 30);
width: 18vw;
height: 24.5vw;
}
.draggable {
position: absolute !important;
z-index: 99;
}
.draggable-header {
cursor: move;
z-index: 10;
display: inline-flex;
flex-direction: row;
text-align: center;
}
p.Damage {
color: rgb(255, 198, 85)
}
.Set {
display: inline;
color: #5f5;
}
.Mana { color: #5ff;}
.Mana:after { content: "\273A"}
.left {
text-align: left;
}
.right {
text-align: right;
}
.center {
text-align: center;
}
.f-w {
width: 100%;
}
.warning {
color: red;
}
.shaded-table {
background-color: rgb(30, 30, 30);
padding-top: 2px;
padding-bottom: 2px;
border-bottom: 1px solid rgb(45, 45, 45)
}
.spacer-table {
padding: 8px;
}
.minimal-stats-container {
display: inline-flex;
flex-direction: column;
vertical-align: top;
background-color: rgb(30, 30, 30);
width: 18vw;
height: 24.5vw;
}
.nDam {
color: #FFAA00;
}
.eDam, .Earth, .Earth_powder {
color: #00AA00;
}
.Earth:before, .Earth_powder:before { content: "\2724" ' '; }
.tDam, .Thunder, .Thunder_powder {
color: #FFFF55;
}
.Thunder:before, .Thunder_powder:before { content: "\2726" ' '; }
.wDam, .Water, .Water_powder {
color: #55FFFF
}
.Water:before, .Water_powder:before { content: "\2749" ' '; }
.fDam, .Fire, .Fire_powder {
color: #FF5555;
}
.Fire:before, .Fire_powder:before { content: "\2739" ' '; }
.aDam, .Air, .Air_powder {
color: #FFFFFF
}
.Air:before, .Air_powder:before { content: "\274b" ' '; }
.Neutral { color: #fa0; }
.Neutral:before { content: "\2724" ' '; }
.Damage { color: rgb(255, 198, 85)}
.Health {
color: #AA0000
}
.Health:before {
content: "\2764" ' ';
}
.Normal {
color: #FFFFFF;
}
.Unique {
color: #FFFF55;
}
.Rare {
color: #FF55FF;
}
.Legendary {
color: #55FFFF;
}
.Fabled {
color: #FF5555;
}
.Mythic {
color: #AA00AA
}
p.no-newline {
display: inline;
}
.clickable {
cursor: pointer;
}
.small-text {
font-size: 12px;
}
.lvl {
color: #d4d4d4
}
.lvl:before {
content: "Lv. "
}
button {
background-color: rgb(30, 30, 30);
color: white;
border: none;
cursor: pointer;
}
.positive {
color: #5f5;
/*text-shadow: 2px 2px 0 #153f15;*/
}
.negative {
color: #f55;
/*text-shadow: 2px 2px 0 #1f1515;*/
}
.item-margin {
margin-top: .8vw;
margin-bottom: .8vw;
}
.item-tooltip {
width: 15rem;
background-color: rgb(30, 30, 30);
color: white;
font-size: 0.8rem;
}
.window-container {
display: inline-flex;
flex-direction: column;
background-color: rgb(30, 30, 30);
color: white;
}
.window-header {
display: flex;
cursor: move;
background-color: rgb(45, 45, 45);
font-size: .8vw;
}
.search-result-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
overflow-y: auto;
overflow-x: hidden;
flex-basis: 30vw;
max-width: 14.4vw;
}
.button-boost {
background-color: rgb(45, 45, 45);
width: 10rem;
border: 5px solid rgb(50, 50, 50)
}
button.toggleOn{
background-color:#0a0;
border: 3px solid rgb(0, 70, 0);
}
.damageSubtitle {
text-align: center;
}
.itemp {
font-size: .8vw;
}
/*
div:not(.item-tooltip) {
font-size: .8vw;
}*/

1155
sq2.html Normal file

File diff suppressed because it is too large Load diff

347
sq2.js Normal file
View file

@ -0,0 +1,347 @@
let equipment_keys = ['weapon', 'helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace'];
$(document).ready(function(){
// inits
$("#overall-window").toggle();
$("#search-container").toggle();
$("#boost-container").toggle();
// pot/base damage switch for weap display
/*
$(".damage-size").click(function(){
$(".damage-size").hide();
$(".potency").show();
});
$(".potency").click(function(){
$(".potency").hide();
$(".damage-size").show();
});*/
// windows
$("#overall-window").draggable({
handle: '#overall-window-header',
}).resizable({
alsoResize: "#all-stats",
handles: 'n, e, s ,w'
});
$("#search-container").draggable({
handle: '#search-container-header',
});
$("#boost-container").draggable({
handle: '#boost-container-header',
});
// window priority
$("#overall-window").mousedown(function() {
$(".window-container").css("z-index", 10);
$(this).css("z-index", 11);
});
$("#search-container").mousedown(function() {
$(".window-container").css("z-index", 10);
$(this).css("z-index", 11);
});
$("#boost-container").mousedown(function() {
$(".window-container").css("z-index", 10);
$(this).css("z-index", 11);
});
// update builds
jQuery(document).on("input", '.skp-input', function(event){
updateStatSchedule();
});
jQuery(document).on("input", '.search-field', function(event){
doSearchSchedule();
});
// set listeners/checks
$("#weapon-choice").on('input', function(){
set_input_style('weapon');
calcBuildSchedule();
update_powder_count('weapon');
});
$("#weapon-powder").on('input', function(){
calcBuildSchedule();
});
$("#helmet-choice").on('input', function(){
set_input_style('helmet');
calcBuildSchedule();
update_powder_count('helmet', '|example: t6t6');
});
$("#helmet-powder").on('input', function(){
calcBuildSchedule();
});
$("#chestplate-choice").on('input', function(){
set_input_style('chestplate');
calcBuildSchedule();
update_powder_count('chestplate');
});
$("#chestplate-powder").on('input', function(){
calcBuildSchedule();
});
$("#leggings-choice").on('input', function(){
set_input_style('leggings');
calcBuildSchedule();
update_powder_count('leggings');
});
$("#leggings-powder").on('input', function(){
calcBuildSchedule();
});
$("#boots-choice").on('input', function(){
set_input_style('boots');
calcBuildSchedule();
update_powder_count('boots');
});
$("#boots-powder").on('input', function(){
calcBuildSchedule();
});
$("#ring1-choice").on('input', function(){
set_input_style('ring1');
calcBuildSchedule();
});
$("#ring2-choice").on('input', function(){
set_input_style('ring2');
calcBuildSchedule();
});
$("#bracelet-choice").on('input', function(){
set_input_style('bracelet');
calcBuildSchedule();
});
$("#necklace-choice").on('input', function(){
set_input_style('necklace');
calcBuildSchedule();
});
// control vars
let basic_stats_ctrl = true;
let off_stats_ctrl = false;
let def_stats_ctrl = false;
$("#basic-stats-btn").click(function(){
basic_stats_ctrl = true;
off_stats_ctrl = false;
def_stats_ctrl = false;
$("#minimal-stats").show();
$("#minimal-offensive-stats").hide();
$("#minimal-defensive-stats").hide();
$("#off-stats-btn").css("background-color", "rgb(45, 45, 45)");
$("#def-stats-btn").css("background-color", "rgb(45, 45, 45)");
});
$("#basic-stats-btn").hover(
function(){
$("#basic-stats-btn").css("background-color", "rgb(40, 40, 40)");
},function(){
if (basic_stats_ctrl) {
$("#basic-stats-btn").css("background-color", "rgb(30, 30, 30)");
} else {
$("#basic-stats-btn").css("background-color", "rgb(45, 45, 45)");
}
});
$("#off-stats-btn").click(function(){
basic_stats_ctrl = false;
off_stats_ctrl = true;
def_stats_ctrl = false;
$("#minimal-stats").hide();
$("#minimal-offensive-stats").show();
$("#minimal-defensive-stats").hide();
$("#basic-stats-btn").css("background-color", "rgb(45, 45, 45)");
$("#def-stats-btn").css("background-color", "rgb(45, 45, 45)");
});
$("#off-stats-btn").hover(
function(){
$("#off-stats-btn").css("background-color", "rgb(40, 40, 40)");
},function(){
if (off_stats_ctrl) {
$("#off-stats-btn").css("background-color", "rgb(30, 30, 30)");
} else {
$("#off-stats-btn").css("background-color", "rgb(45, 45, 45)");
}
});
$("#def-stats-btn").click(function(){
basic_stats_ctrl = false;
off_stats_ctrl = false;
def_stats_ctrl = true;
$("#minimal-stats").hide();
$("#minimal-offensive-stats").hide();
$("#minimal-defensive-stats").show();
$("#off-stats-btn").css("background-color", "rgb(45, 45, 45)");
$("#basic-stats-btn").css("background-color", "rgb(45, 45, 45)");
});
$("#def-stats-btn").hover(
function(){
$("#def-stats-btn").css("background-color", "rgb(40, 40, 40)");
},function(){
if (def_stats_ctrl) {
$("#def-stats-btn").css("background-color", "rgb(30, 30, 30)");
} else {
$("#def-stats-btn").css("background-color", "rgb(45, 45, 45)");
}
});
// item tooltip
$("#weapon-img-loc").hover(function(event){
$("#weapon-tooltip").show();
init_tooltip_loc('weapon');
}, function(){
$("#weapon-tooltip").hide();
});
$("#helmet-img-loc").hover(function(event){
$("#helmet-tooltip").show();
init_tooltip_loc('helmet');
}, function(){
$("#helmet-tooltip").hide();
});
$("#chestplate-img-loc").hover(function(event){
$("#chestplate-tooltip").show();
init_tooltip_loc('chestplate');
}, function(){
$("#chestplate-tooltip").hide();
});
$("#leggings-img-loc").hover(function(event){
$("#leggings-tooltip").show();
init_tooltip_loc('leggings');
}, function(){
$("#leggings-tooltip").hide();
});
$("#boots-img-loc").hover(function(event){
$("#boots-tooltip").show();
init_tooltip_loc('boots');
}, function(){
$("#boots-tooltip").hide();
});
$("#ring1-img-loc").hover(function(event){
$("#ring1-tooltip").show();
init_tooltip_loc('ring1');
}, function(){
$("#ring1-tooltip").hide();
});
$("#ring2-img-loc").hover(function(event){
$("#ring2-tooltip").show();
init_tooltip_loc('ring2');
}, function(){
$("#ring2-tooltip").hide();
});
$("#bracelet-img-loc").hover(function(event){
$("#bracelet-tooltip").show();
init_tooltip_loc('bracelet');
}, function(){
$("#bracelet-tooltip").hide();
});
$("#necklace-img-loc").hover(function(event){
$("#necklace-tooltip").show();
init_tooltip_loc('necklace');
}, function(){
$("#necklace-tooltip").hide();
});
});
function set_input_style(type) {
let item = itemMap.get($("#"+type+"-choice").val());
if (item) {
$("#"+type+"-choice").addClass(item.tier);
if (type == 'weapon') {
$("#"+type+"-img").attr('src', 'media/items/new/generic-'+item.type+'.png');
}
} else {
$("#"+type+"-choice").attr('class', 'item-name');
}
}
function init_tooltip_loc(equipment){
let ImgLoc = document.getElementById(equipment+'-img-loc').getBoundingClientRect();
let tooltipRect = document.getElementById(equipment+"-tooltip").getBoundingClientRect();
let windowHeight = $(window).height()
$("#"+equipment+"-tooltip").css('top', Math.min(ImgLoc.top, windowHeight - (tooltipRect.bottom - tooltipRect.top)));
$("#"+equipment+"-tooltip").css('left', ImgLoc.right);
}
function update_powder_count(type, alt="") {
let item = itemMap.get($("#"+type+"-choice").val());
if (item) {
$("#"+type+"-powder").attr("placeholder", item["slots"]+" slots"+alt);
}
}
function init_equipUI() {
for (const i in equipment_keys) {
set_input_style(equipment_keys[i]);
}
update_powder_count('weapon');
update_powder_count('helmet', '|example: t6t6');
update_powder_count('chestplate');
update_powder_count('leggings');
update_powder_count('boots');
}
// phanta method of handling input <3
let calcBuildTask = null;
let updateStatTask = null;
let doSearchTask = null;
function calcBuildSchedule(){
if (calcBuildTask !== null) {
clearTimeout(calcBuildTask);
}
calcBuildTask = setTimeout(function(){
calcBuildTask = null;
calculateBuild();
}, 500);
}
function updateStatSchedule(){
if (updateStatTask !== null) {
clearTimeout(updateStatTask);
}
updateStatTask = setTimeout(function(){
updateStatTask = null;
updateStats();
}, 500);
}
function doSearchSchedule(){
if (doSearchTask !== null) {
clearTimeout(doSearchTask);
}
doSearchTask = setTimeout(function(){
doSearchTask = null;
doItemSearch();
}, 500);
}

437
sq2bs.css Normal file
View file

@ -0,0 +1,437 @@
* {
font-family: 'Nunito', sans-serif;
}
/* sidebar stuff */
.sidebar {
height: 100%; /* 100% Full-height */
width: 3.5vw; /* 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 */
}
.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: .8vw;
}
.sidebar a:hover {
background-color: hsl(0, 0%, 8%);
color: rgb(210, 210, 210);
}
@media screen and (max-width: 992px) {
.sidebar {display: none;}
}
/* builder containers */
/* wynn-related css(es) */
.positive {
color: #5f5;
}
.negative {
color: #f55;
}
.Health {
color: #AA0000
}
.Health:before {
content: "\2764" ' ';
}
.lvl:before {
content: 'Lv. '
}
.Damage {
color: rgb(255, 198, 85)
}
.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;
}
.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" ' '; }
.nDam, .Neutral {
color: #FFAA00;
}
.nDam:before, .Neutral:before {
content: "\2724" ' ';
}
.Mana {
color: #5ff;
}
.Mana:after {
content: "\273A"
}
/* 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;
}
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);
}
.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;
}
.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;
}
.scaled-font {
font-size: 2.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) {
.scaled-font {
font-size: .8rem;
}
.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) {
.scaled-font {
font-size: 1rem;
}
.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 {
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-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%);
}
.dark-shadow {
box-shadow: 0rem 0rem 1.25rem 0.1875rem black;
}
.dark-shadow-sm {
box-shadow: 0rem 0rem 0.625rem 0.125rem black;
}
.border-dark-7 {
border-color:hsl(0, 0%, 14%) !important;
}

1229
sq2bs.html Normal file

File diff suppressed because it is too large Load diff

385
sq2bs.js Normal file
View file

@ -0,0 +1,385 @@
let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'];
let weapon_keys = ['dagger', 'wand', 'bow', 'relik', 'spear'];
let skp_keys = ['str', 'dex', 'int', 'def', 'agi'];
let spell_disp = ['spell0-info', 'spell1-info', 'spell2-info', 'spell3-info'];
let other_disp = ['build-order', 'set-info', 'int-info'];
document.addEventListener('DOMContentLoaded', function() {
for (const eq of equipment_keys) {
document.querySelector("#"+eq+"-choice").setAttribute("oninput", "update_field('"+ eq +"'); calcBuildSchedule();");
document.querySelector("#"+eq+"-powder").setAttribute("oninput", "calcBuildSchedule();");
document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip')");
}
for (const i of spell_disp) {
document.querySelector("#"+i+"Avg").setAttribute("onclick", "toggle_spell_tab('"+i+"')");
}
document.querySelector("#level-choice").setAttribute("oninput", "calcBuildSchedule()")
let skp_fields = document.getElementsByClassName("skp-update");
for (i = 0; i < skp_fields.length; i++) {
skp_fields[i].setAttribute("oninput", "updateStatSchedule()");
}
let masonry = Macy({
container: "#masonry-container",
columns: 1,
mobileFirst: true,
breakAt: {
1200: 4,
},
margin: {
x: 20,
y: 20,
}
});
let search_masonry = Macy({
container: "#search-results",
columns: 1,
mobileFirst: true,
breakAt: {
1200: 4,
},
margin: {
x: 20,
y: 20,
}
});
document.querySelector("#search-container").addEventListener("keyup", function(event) {
if (event.key === "Escape") {
document.querySelector("#search-container").style.display = "none";
};
});
});
// phanta scheduler
let calcBuildTask = null;
let updateStatTask = null;
let doSearchTask = null;
function calcBuildSchedule(){
if (calcBuildTask !== null) {
clearTimeout(calcBuildTask);
}
calcBuildTask = setTimeout(function(){
calcBuildTask = null;
calculateBuild();
}, 500);
}
function updateStatSchedule(){
if (updateStatTask !== null) {
clearTimeout(updateStatTask);
}
updateStatTask = setTimeout(function(){
updateStatTask = null;
updateStats();
}, 500);
}
function doSearchSchedule(){
if (doSearchTask !== null) {
clearTimeout(doSearchTask);
}
doSearchTask = setTimeout(function(){
doSearchTask = null;
doItemSearch();
window.dispatchEvent(new Event('resize'));
}, 500);
}
// equipment field dynamic styling
function update_field(field) {
// built on the assumption of no one will type in CI/CR letter by letter
// resets
document.querySelector("#"+field+"-choice").classList.remove("text-light", "is-invalid", 'Normal', 'Unique', 'Rare', 'Legendary', 'Fabled', 'Mythic', 'Set');
item = document.querySelector("#"+field+"-choice").value
let powder_slots;
let tier;
let category;
let type;
// get item info
if (item.slice(0, 3) == "CI-") {
item = getCustomFromHash(item);
powder_slots = item.statMap.get("slots");
tier = item.statMap.get("tier");
category = item.statMap.get("category");
type = item.statMap.get("type");
}
else if (item.slice(0, 3) == "CR-") {
item = getCraftFromHash(item);
powder_slots = item.statMap.get("slots");
tier = item.statMap.get("tier");
category = item.statMap.get("category");
type = item.statMap.get("type");
}
else if (itemMap.get(item)) {
item = itemMap.get(item);
if (!item) {return false;}
powder_slots = item.slots;
tier = item.tier;
category = item.category;
type = item.type;
}
else {
// item not found
document.querySelector("#"+field+"-choice").classList.add("text-light");
if (item) { document.querySelector("#"+field+"-choice").classList.add("is-invalid"); }
document.querySelector("#"+equipment_keys[i]+"-powder").disabled = true;
return false;
}
if ((type != field.replace(/[0-9]/g, '')) && (category != field.replace(/[0-9]/g, ''))) {
document.querySelector("#"+field+"-choice").classList.add("text-light");
if (item) { document.querySelector("#"+field+"-choice").classList.add("is-invalid"); }
document.querySelector("#"+equipment_keys[i]+"-powder").disabled = true;
return false;
}
// set item color
document.querySelector("#"+field+"-choice").classList.add(tier);
// set powder slots
document.querySelector("#"+field+"-powder").setAttribute("placeholder", powder_slots+" slots");
if (powder_slots == 0) {
document.querySelector("#"+field+"-powder").disabled = true;
} else {
document.querySelector("#"+field+"-powder").disabled = false;
}
// set weapon img
if (category == 'weapon') {
document.querySelector("#weapon-img").setAttribute('src', 'media/items/new/generic-'+type+'.png');
}
// call calc build
}
/* tabulars | man i hate this code but too lazy to fix /shrug */
let tabs = ['all-stats', 'minimal-offensive-stats', 'minimal-defensive-stats'];
function show_tab(tab) {
console.log(itemFilters)
for (const i in tabs) {
document.querySelector("#"+tabs[i]).style.display = "none";
}
document.querySelector("#"+tab).style.display = "";
}
function toggle_spell_tab(tab) {
if (document.querySelector("#"+tab).style.display == "none") {
document.querySelector("#"+tab).style.display = "";
} else {
document.querySelector("#"+tab).style.display = "none";
}
}
function toggle_boost_tab(tab) {
for (const i of skp_keys) {
document.querySelector("#"+i+"-boost").style.display = "none";
}
document.querySelector("#"+tab+"-boost").style.display = "";
}
// toggle tab
function toggle_tab(tab) {
if (document.querySelector("#"+tab).style.display == "none") {
document.querySelector("#"+tab).style.display = "";
} else {
document.querySelector("#"+tab).style.display = "none";
}
}
function collapse_element(elmnt) {
elem_list = document.querySelector(elmnt).children;
for (elem of elem_list) {
if (elem.classList.contains("no-collapse")) { continue; }
if (elem.style.display == "none") {
elem.style.display = "";
} else {
elem.style.display = "none";
}
}
// macy quirk
window.dispatchEvent(new Event('resize'));
// weird bug where display: none overrides??
document.querySelector(elmnt).style.display = "";
}
// search misc
function set_item(item) {
document.querySelector("#search-container").style.display = "none";
let type;
// if (!player_build) {return false;}
if (item.get("category") === "weapon") {
type = "weapon";
} else if (item.get("type") === "ring") {
if (!document.querySelector("#ring1-choice").value) {
type = "ring1";
} else {
type = "ring2";
}
} else {
type = item.get("type");
}
document.querySelector("#"+type+"-choice").value = item.get("displayName");
calcBuildSchedule();
update_field(type);
}
// disable boosts
function reset_powder_specials() {
let specials = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"]
for (const special of specials) {
for (i = 1; i < 6; i++) {
if (document.querySelector("#"+special+"-"+i).classList.contains("toggleOn")) {
document.querySelector("#"+special+"-"+i).classList.remove("toggleOn");
}
}
}
}
// autocomplete initialize
function init_autocomplete() {
let dropdowns = new Map()
for (const eq of equipment_keys) {
// build dropdown
console.log('init dropdown for '+ eq)
let item_arr = [];
if (eq == 'weapon') {
for (const weaponType of weapon_keys) {
for (const weapon of itemLists.get(weaponType)) {
let item_obj = itemMap.get(weapon);
if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") {
continue;
}
if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) {
continue;
}
item_arr.push(weapon);
}
}
} else {
for (const item of itemLists.get(eq.replace(/[0-9]/g, ''))) {
let item_obj = itemMap.get(item);
if (item_obj["restrict"] && item_obj["restrict"] === "DEPRECATED") {
continue;
}
if (item_obj["name"] == 'No '+ eq.charAt(0).toUpperCase() + eq.slice(1)) {
continue;
}
item_arr.push(item)
}
}
// create dropdown
dropdowns.set(eq, new autoComplete({
data: {
src: item_arr
},
selector: "#"+ eq +"-choice",
wrapper: false,
resultsList: {
tabSelect: true,
noResults: true,
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
element: (list, data) => {
// dynamic result loc
let position = document.getElementById(eq+'-dropdown').getBoundingClientRect();
list.style.top = position.bottom + window.scrollY +"px";
list.style.left = position.x+"px";
list.style.width = position.width+"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",
element: (item, data) => {
item.classList.add(itemMap.get(data.value).tier);
},
},
events: {
input: {
selection: (event) => {
if (event.detail.selection.value) {
event.target.value = event.detail.selection.value;
}
update_field(eq);
calcBuildSchedule();
},
},
}
}));
}
let filter_loc = ["filter1", "filter2", "filter3", "filter4"];
for (const i of filter_loc) {
console.log(i);
console.log('init dropdown for '+i+"-choice" )
dropdowns.set(i+"-choice", new autoComplete({
data: {
src: itemFilters,
},
selector: "#"+i+"-choice",
wrapper: false,
resultsList: {
tabSelect: true,
noResults: true,
class: "search-box dark-7 rounded-bottom px-2 fw-bold dark-shadow-sm",
element: (list, data) => {
// dynamic result loc
console.log(i);
list.style.zIndex = "100";
let position = document.getElementById(i+"-dropdown").getBoundingClientRect();
window_pos = document.getElementById("search-container").getBoundingClientRect();
list.style.top = position.bottom - window_pos.top + 5 +"px";
list.style.left = position.x - window_pos.x +"px";
list.style.width = position.width+"px";
if (!data.results.length) {
message = document.createElement('li');
message.classList.add('scaled-font');
message.textContent = "No filters found!";
list.prepend(message);
}
},
},
resultItem: {
class: "scaled-font search-item",
selected: "dark-5",
},
events: {
input: {
selection: (event) => {
if (event.detail.selection.value) {
event.target.value = event.detail.selection.value;
}
doSearchSchedule();
},
},
}
}));
}
}

551
sq2build.js Normal file
View file

@ -0,0 +1,551 @@
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20], ["sword", 1.10]]);
/**
* @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 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.chesplate, 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]);
}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];
// How many skillpoints the player had to assign (5 number)
this.base_skillpoints = result[1];
// How many skillpoints the build ended up with (5 number)
this.total_skillpoints = result[2];
// How many skillpoints assigned (1 number, sum of base_skillpoints)
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 */
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.
@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", "str", "dex", "int", "def", "agi"];
//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")) {
if (staticIDs.includes(id)) {
continue;
}
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 major_id of item.get("majorIds")) {
major_ids.add(major_id);
}
}
}
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")));
}
}

985
sq2builder.js Normal file
View file

@ -0,0 +1,985 @@
const url_tag = location.hash.slice(1);
// console.log(url_base);
// console.log(url_tag);
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 equipment_names = [
"Helmet",
"Chestplate",
"Leggings",
"Boots",
"Ring 1",
"Ring 2",
"Bracelet",
"Necklace",
"Weapon"
];
let equipmentInputs = equipment_fields.map(x => x + "-choice");
let buildFields = equipment_fields.map(x => x+"-tooltip");
let powderInputs = [
"helmet-powder",
"chestplate-powder",
"leggings-powder",
"boots-powder",
"weapon-powder",
];
function init() {
console.log("builder.js init");
init_autocomplete();
for (const i of equipment_keys) {
update_field(i);
}
decodeBuild(url_tag);
}
function getItemNameFromID(id) {
if (redirectMap.has(id)) {
return getItemNameFromID(redirectMap.get(id));
}
return idMap.get(id);
}
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);
console.log(block);
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;
}
/*
* Populate fields based on url, and calculate build.
*/
function decodeBuild(url_tag) {
if (url_tag) {
let equipment = [null, null, 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;
if (version === "0" || version === "1" || version === "2" || version === "3") {
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);
}
if (version === "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);
}
if (version === "5") {
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);
}
if (version === "1") {
let powder_info = info[1];
powdering = parsePowdering(powder_info);
} else if (version === "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);
powdering = parsePowdering(powder_info);
} else if (version === "3" || version === "4" || version === "5"){
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);
powdering = parsePowdering(powder_info);
}
for (let i in powderInputs) {
setValue(powderInputs[i], powdering[i]);
}
for (let i in equipment) {
setValue(equipmentInputs[i], equipment[i]);
}
calculateBuild(save_skp, skillpoints);
}
}
/* Stores the entire build in a string using B64 encryption and adds it to the URL.
*/
function encodeBuild() {
if (player_build) {
let build_string;
if (player_build.customItems.length > 0) { //v5 encoding
build_string = "5_";
let crafted_idx = 0;
let custom_idx = 0;
for (const item of player_build.items) {
if (item.get("custom")) {
let custom = "CI-"+encodeCustom(player_build.customItems[custom_idx],true);
build_string += Base64.fromIntN(custom.length, 3) + custom;
custom_idx += 1;
} else if (item.get("crafted")) {
build_string += "CR-"+encodeCraft(player_build.craftedItems[crafted_idx]);
crafted_idx += 1;
} else {
build_string += Base64.fromIntN(item.get("id"), 3);
}
}
for (const skp of skp_order) {
build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048
}
build_string += Base64.fromIntN(player_build.level, 2);
for (const _powderset of player_build.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);
}
}
} else { //v4 encoding
build_string = "4_";
let crafted_idx = 0;
for (const item of player_build.items) {
if (item.get("crafted")) {
build_string += "-"+encodeCraft(player_build.craftedItems[crafted_idx]);
crafted_idx += 1;
} else {
build_string += Base64.fromIntN(item.get("id"), 3);
}
}
for (const skp of skp_order) {
build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048
}
build_string += Base64.fromIntN(player_build.level, 2);
for (const _powderset of player_build.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);
}
}
}
return build_string;
}
// this.equipment = [ this.helmet, this.chestplate, this.leggings, this.boots, this.ring1, this.ring2, this.bracelet, this.necklace ];
// let build_string = "3_" + Base64.fromIntN(player_build.helmet.get("id"), 3) +
// Base64.fromIntN(player_build.chestplate.get("id"), 3) +
// Base64.fromIntN(player_build.leggings.get("id"), 3) +
// Base64.fromIntN(player_build.boots.get("id"), 3) +
// Base64.fromIntN(player_build.ring1.get("id"), 3) +
// Base64.fromIntN(player_build.ring2.get("id"), 3) +
// Base64.fromIntN(player_build.bracelet.get("id"), 3) +
// Base64.fromIntN(player_build.necklace.get("id"), 3) +
// Base64.fromIntN(player_build.weapon.get("id"), 3);
return "";
}
function calculateBuild(save_skp, skp){
try {
/*
let specialNames = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"];
for (const sName of specialNames) {
for (let i = 1; i < 6; i++) {
let elem = document.getElementById(sName + "-" + i);
let name = sName.replace("_", " ");
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
elem.classList.remove("toggleOn");
}
}
}
*/
if(player_build){
reset_powder_specials();
updateBoosts("skip", false);
updatePowderSpecials("skip", false);
}
let weaponName = getValue(equipmentInputs[8]);
if (weaponName.startsWith("Morph-")) {
let equipment = [ "Morph-Stardust", "Morph-Steel", "Morph-Iron", "Morph-Gold", "Morph-Topaz", "Morph-Emerald", "Morph-Amethyst", "Morph-Ruby", weaponName.substring(6) ];
for (let i in equipment) {
setValue(equipmentInputs[i], equipment[i]);
}
}
//updatePowderSpecials("skip"); //jank pt 1
save_skp = (typeof save_skp !== 'undefined') ? save_skp : false;
/* TODO: implement level changing
Make this entire function prettier
*/
let equipment = [ null, null, null, null, null, null, null, null, null ];
for (let i in equipment) {
let equip = getValue(equipmentInputs[i]).trim();
if (equip === "") {
equip = "No " + equipment_names[i]
}
else {
setValue(equipmentInputs[i], equip);
}
equipment[i] = equip;
}
let powderings = [];
let errors = [];
for (const i in powderInputs) {
// read in two characters at a time.
// TODO: make this more robust.
let input = getValue(powderInputs[i]).trim();
let powdering = [];
let errorederrors = [];
while (input) {
let first = input.slice(0, 2);
let powder = powderIDs.get(first);
console.log(powder);
if (powder === undefined) {
errorederrors.push(first);
} else {
powdering.push(powder);
}
input = input.slice(2);
}
if (errorederrors.length > 0) {
if (errorederrors.length > 1)
errors.push(new IncorrectInput(errorederrors.join(""), "t6w6", powderInputs[i]));
else
errors.push(new IncorrectInput(errorederrors[0], "t6 or e3", powderInputs[i]));
}
//console.log("POWDERING: " + powdering);
powderings.push(powdering);
}
let level = document.getElementById("level-choice").value;
player_build = new Build(level, equipment, powderings, new Map(), errors);
console.log(player_build);
for (let i of document.getElementsByClassName("hide-container-block")) {
i.style.display = "block";
}
for (let i of document.getElementsByClassName("hide-container-grid")) {
i.style.display = "grid";
}
console.log(player_build.toString());
displaysq2EquipOrder(document.getElementById("build-order"),player_build.equip_order);
const assigned = player_build.base_skillpoints;
const skillpoints = player_build.total_skillpoints;
for (let i in skp_order){ //big bren
setText(skp_order[i] + "-skp-base", "Original: " + skillpoints[i]);
}
if (save_skp) {
// TODO: reduce duplicated code, @updateStats
let skillpoints = player_build.total_skillpoints;
let delta_total = 0;
for (let i in skp_order) {
let manual_assigned = skp[i];
let delta = manual_assigned - skillpoints[i];
skillpoints[i] = manual_assigned;
player_build.base_skillpoints[i] += delta;
delta_total += delta;
}
player_build.assigned_skillpoints += delta_total;
}
calculateBuildStats();
// setTitle();
if (player_build.errored)
throw new ListError(player_build.errors);
}
catch (error) {
console.log(error);
}
}
function handleBuilderError(error) {
if (error instanceof ListError) {
for (let i of error.errors) {
if (i instanceof ItemNotFound) {
i.element.textContent = i.message;
} else if (i instanceof IncorrectInput) {
if (document.getElementById(i.id) !== null) {
document.getElementById(i.id).parentElement.querySelectorAll("p.error")[0].textContent = i.message;
}
} else {
let msg = i.stack;
let lines = msg.split("\n");
let header = document.getElementById("header");
header.textContent = "";
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);
}
}
} else {
let msg = error.stack;
let lines = msg.split("\n");
let header = document.getElementById("header");
header.textContent = "";
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);
}
}
/* Updates all build statistics based on (for now) the skillpoint input fields and then calculates build stats.
*/
function updateStats() {
let specialNames = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"];
for (const sName of specialNames) {
for (let i = 1; i < 6; i++) {
let elem = document.getElementById(sName + "-" + i);
let name = sName.replace("_", " ");
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
elem.classList.remove("toggleOn");
let special = powderSpecialStats[specialNames.indexOf(sName)];
console.log(special);
if (special["weaponSpecialEffects"].has("Damage Boost")) {
if (name === "Courage" || name === "Curse") { //courage is universal damage boost
//player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100;
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
} else if (name === "Wind Prison") {
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1];
}
}
}
}
}
let skillpoints = player_build.total_skillpoints;
let delta_total = 0;
for (let i in skp_order) {
let value = document.getElementById(skp_order[i] + "-skp").value;
if (value === ""){value = 0; setValue(skp_order[i] + "-skp", value)}
let manual_assigned = 0;
if (value.includes("+")) {
let skp = value.split("+");
for (const s of skp) {
manual_assigned += parseInt(s,10);
}
} else {
manual_assigned = parseInt(value,10);
}
let delta = manual_assigned - skillpoints[i];
skillpoints[i] = manual_assigned;
player_build.base_skillpoints[i] += delta;
delta_total += delta;
}
player_build.assigned_skillpoints += delta_total;
if(player_build){
updatePowderSpecials("skip", false);
updateBoosts("skip", false);
}
/*
for (let id of editable_item_fields) {
player_build.statMap.set(id, parseInt(getValue(id)));
console.log(player_build.statMap.get(id));
}
}*/
player_build.aggregateStats();
console.log(player_build.statMap);
calculateBuildStats();
}
/* Updates all spell boosts
*/
function updateBoosts(buttonId, recalcStats) {
let elem = document.getElementById(buttonId);
let name = buttonId.split("-")[0];
if(buttonId !== "skip") {
if (elem.classList.contains("toggleOn")) {
player_build.damageMultiplier -= damageMultipliers.get(name);
if (name === "warscream") {
player_build.defenseMultiplier -= .20;
}
if (name === "vanish") {
player_build.defenseMultiplier -= .15;
}
elem.classList.remove("toggleOn");
}else{
player_build.damageMultiplier += damageMultipliers.get(name);
if (name === "warscream") {
player_build.defenseMultiplier += .20;
}
if (name === "vanish") {
player_build.defenseMultiplier += .15;
}
elem.classList.add("toggleOn");
}
updatePowderSpecials("skip", false); //jank pt 1
} else {
for (const [key, value] of damageMultipliers) {
let elem = document.getElementById(key + "-boost")
if (elem.classList.contains("toggleOn")) {
elem.classList.remove("toggleOn");
player_build.damageMultiplier -= value;
if (key === "warscream") { player_build.defenseMultiplier -= .20 }
if (key === "vanish") { player_build.defenseMultiplier -= .15 }
}
}
}
if (recalcStats) {
calculateBuildStats();
}
}
/* Updates all powder special boosts
*/
function updatePowderSpecials(buttonId, recalcStats) {
//console.log(player_build.statMap);
let name = (buttonId).split("-")[0];
let power = (buttonId).split("-")[1]; // [1, 5]
let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"];
let powderSpecials = []; // [ [special, power], [special, power]]
if(name !== "skip"){
let elem = document.getElementById(buttonId);
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
elem.classList.remove("toggleOn");
let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))];
if (special.weaponSpecialEffects.has("Damage Boost")) {
name = name.replace("_", " ");
if (name === "Courage" || name === "Curse") { //courage and curse are universal damage boost
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
//poison?
} else if (name === "Wind Prison") {
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[power-1];
}
}
} else {
for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off
//name is same, power is i
if(document.getElementById(name.replace(" ", "_") + "-" + i).classList.contains("toggleOn")) {
document.getElementById(name.replace(" ", "_") + "-" + i).classList.remove("toggleOn");
let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))];
if (special.weaponSpecialEffects.has("Damage Boost")) {
name = name.replace("_", " "); //might be redundant
if (name === "Courage" || name === "Curse") { //courage is universal damage boost
//player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100;
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
} else if (name === "Wind Prison") {
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1];
}
}
}
}
//toggle the pressed button on
elem.classList.add("toggleOn");
}
}
for (const sName of specialNames) {
for (let i = 1;i < 6; i++) {
if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) {
let powderSpecial = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))];
powderSpecials.push([powderSpecial, i]);
break;
}
}
}
if (name !== "skip") {
let elem = document.getElementById(buttonId);
if (elem.classList.contains("toggleOn")) {
let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))];
if (special["weaponSpecialEffects"].has("Damage Boost")) {
let name = special["weaponSpecialName"];
if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
} else if (name === "Wind Prison") {
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
player_build.externalStats.get("damageBonus")[4] += special.weaponSpecialEffects.get("Damage Boost")[power-1];
}
}
}
}
if (recalcStats) {
calculateBuildStats();
}
displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true);
}
/* Calculates all build statistics and updates the entire display.
*/
function calculateBuildStats() {
const assigned = player_build.base_skillpoints;
const skillpoints = player_build.total_skillpoints;
let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."];
for (let i in skp_order){ //big bren
setText(skp_order[i] + "-skp-assign", "Assign: " + assigned[i]);
setValue(skp_order[i] + "-skp", skillpoints[i]);
let linebreak = document.createElement("br");
linebreak.classList.add("itemp");
document.getElementById(skp_order[i] + "-skp-label");
setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i]));
document.getElementById(skp_order[i]+"-warnings").textContent = ''
if (assigned[i] > 100) {
let skp_warning = document.createElement("p");
skp_warning.classList.add("warning");
skp_warning.classList.add("small-text")
skp_warning.textContent += "Cannot assign " + assigned[i] + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually.";
document.getElementById(skp_order[i]+"-warnings").textContent = ''
document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning);
}
}
let summarybox = document.getElementById("summary-box");
summarybox.textContent = "";
let skpRow = document.createElement("p");
// let td = document.createElement("p");
let remainingSkp = document.createElement("p");
remainingSkp.classList.add("scaled-font");
let remainingSkpTitle = document.createElement("b");
remainingSkpTitle.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Remaining skillpoints: ";
let remainingSkpContent = document.createElement("b");
remainingSkpContent.textContent = "" + (levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints);
remainingSkpContent.classList.add(levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints < 0 ? "negative" : "positive");
remainingSkp.appendChild(remainingSkpTitle);
remainingSkp.appendChild(remainingSkpContent);
summarybox.append(skpRow);
summarybox.append(remainingSkp);
if(player_build.assigned_skillpoints > levelToSkillPoints(player_build.level)){
let skpWarning = document.createElement("span");
//skpWarning.classList.add("itemp");
skpWarning.classList.add("warning");
// skpWarning.classList.add("skp-tooltip");
skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!";
let skpCount = document.createElement("p");
skpCount.classList.add("warning");
// skpCount.classList.add("skp-tooltip");
skpCount.textContent = "For level " + (player_build.level>101 ? "101+" : player_build.level) + ", there are only " + levelToSkillPoints(player_build.level) + " skill points available.";
summarybox.append(skpWarning);
summarybox.append(skpCount);
}
let lvlWarning;
for (const item of player_build.items) {
let item_lvl;
if (item.get("crafted")) {
//item_lvl = item.get("lvlLow") + "-" + item.get("lvl");
item_lvl = item.get("lvlLow");
}
else {
item_lvl = item.get("lvl");
}
if (player_build.level < item_lvl) {
if (!lvlWarning) {
lvlWarning = document.createElement("p");
lvlWarning.classList.add("itemp");
lvlWarning.classList.add("warning");
lvlWarning.textContent = "WARNING: A level " + player_build.level + " player cannot use some piece(s) of this build."
}
let baditem = document.createElement("p");
baditem.classList.add("nocolor");
baditem.classList.add("itemp");
baditem.textContent = item.get("displayName") + " requires level " + item.get("lvl") + " to use.";
lvlWarning.appendChild(baditem);
}
}
if(lvlWarning){
summarybox.append(lvlWarning);
}
for (const [setName, count] of player_build.activeSetCounts) {
const bonus = sets[setName].bonuses[count-1];
// console.log(setName);
if (bonus["illegal"]) {
let setWarning = document.createElement("p");
setWarning.classList.add("itemp");
setWarning.classList.add("warning");
setWarning.textContent = "WARNING: illegal item combination: " + setName
summarybox.append(setWarning);
}
}
for (let i in player_build.items) {
if (player_build.items[i].get("id") > 9999) {
continue;
}
displaysq2ExpandedItem(player_build.items[i], buildFields[i]);
collapse_element("#"+equipment_keys[i]+"-tooltip");
}
displaysq2ArmorStats(player_build);
displaysq2BuildStats("all-stats", player_build, build_all_display_commands);
displaysq2BuildStats("minimal-offensive-stats",player_build, build_offensive_display_commands);
displaysq2SetBonuses("set-info",player_build);
let meleeStats = player_build.getMeleeStats();
displaysq2MeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats);
displaysq2DefenseStats(document.getElementById("minimal-defensive-stats"),player_build);
displaysq2PoisonDamage(document.getElementById("build-poison-stats"),player_build);
let spells = spell_table[player_build.weapon.get("type")];
for (let i = 0; i < 4; ++i) {
let parent_elem = document.getElementById("spell"+i+"-info");
let overallparent_elem = document.getElementById("spell"+i+"-infoAvg");
displaysq2SpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1);
}
location.hash = encodeBuild();
clear_highlights();
}
function copyBuild() {
if (player_build) {
copyTextToClipboard(url_base+location.hash);
document.getElementById("copy-button").textContent = "Copied!";
}
}
function shareBuild() {
if (player_build) {
let text = url_base+location.hash+"\n"+
"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("")+"]";
copyTextToClipboard(text);
document.getElementById("share-button").textContent = "Copied!";
}
}
function populateBuildList() {
const buildList = document.getElementById("build-choice");
const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds"));
for (const buildName of Object.keys(savedBuilds).sort()) {
const buildOption = document.createElement("option");
buildOption.setAttribute("value", buildName);
buildList.appendChild(buildOption);
}
}
function saveBuild() {
if (player_build) {
const savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds"));
const saveName = document.getElementById("build-name").value;
const encodedBuild = encodeBuild();
if ((!Object.keys(savedBuilds).includes(saveName)
|| document.getElementById("saved-error").textContent !== "") && encodedBuild !== "") {
savedBuilds[saveName] = encodedBuild.replace("#", "");
window.localStorage.setItem("builds", JSON.stringify(savedBuilds));
document.getElementById("saved-error").textContent = "";
document.getElementById("saved-build").textContent = "Build saved locally";
const buildList = document.getElementById("build-choice");
const buildOption = document.createElement("option");
buildOption.setAttribute("value", saveName);
buildList.appendChild(buildOption);
} else {
document.getElementById("saved-build").textContent = "";
if (encodedBuild === "") {
document.getElementById("saved-error").textContent = "Empty build";
}
else {
document.getElementById("saved-error").textContent = "Exists. Overwrite?";
}
}
}
}
function loadBuild() {
let savedBuilds = window.localStorage.getItem("builds") === null ? {} : JSON.parse(window.localStorage.getItem("builds"));
let saveName = document.getElementById("build-name").value;
if (Object.keys(savedBuilds).includes(saveName)) {
decodeBuild(savedBuilds[saveName])
document.getElementById("loaded-error").textContent = "";
document.getElementById("loaded-build").textContent = "Build loaded";
} else {
document.getElementById("loaded-build").textContent = "";
document.getElementById("loaded-error").textContent = "Build doesn't exist";
}
}
function resetFields(){
for (let i in powderInputs) {
setValue(powderInputs[i], "");
}
for (let i in equipmentInputs) {
setValue(equipmentInputs[i], "");
}
setValue("str-skp", "0");
setValue("dex-skp", "0");
setValue("int-skp", "0");
setValue("def-skp", "0");
setValue("agi-skp", "0");
setValue("level-choice", "106");
location.hash = "";
calculateBuild();
}
function toggleID() {
let button = document.getElementById("show-id-button");
let targetDiv = document.getElementById("id-edit");
if (button.classList.contains("toggleOn")) { //toggle the pressed button off
targetDiv.style.display = "none";
button.classList.remove("toggleOn");
}
else {
targetDiv.style.display = "block";
button.classList.add("toggleOn");
}
}
function optimizeStrDex() {
const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints;
const base_skillpoints = player_build.base_skillpoints;
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
const base_total_skillpoints = player_build.total_skillpoints;
let str_bonus = remaining;
let dex_bonus = 0;
let best_skillpoints = player_build.total_skillpoints;
let best_damage = 0;
for (let i = 0; i <= remaining; ++i) {
let total_skillpoints = base_total_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.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 _results = calculateSpellDamage(stats, part.conversion,
stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"),
part.multiplier / 100, player_build.weapon, total_skillpoints,
player_build.damageMultiplier, player_build.externalStats);
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;
}
// TODO: reduce duplicated code, @calculateBuild
let skillpoints = player_build.total_skillpoints;
let delta_total = 0;
for (let i in skp_order) {
let manual_assigned = best_skillpoints[i];
let delta = manual_assigned - skillpoints[i];
skillpoints[i] = manual_assigned;
player_build.base_skillpoints[i] += delta;
delta_total += delta;
}
player_build.assigned_skillpoints += delta_total;
try {
calculateBuildStats();
setTitle();
if (player_build.errored)
throw new ListError(player_build.errors);
}
catch (error) {
handleBuilderError(error);
}
}
// TODO: Learn and use await
function init2() {
load_ing_init(init);
}
load_init(init2);

1468
sq2display.js Normal file

File diff suppressed because it is too large Load diff

108
sq2display_constants.js Normal file
View file

@ -0,0 +1,108 @@
/*
* Display commands
*/
let build_all_display_commands = [
"#defense-stats",
"str", "dex", "int", "def", "agi",
"mr", "ms",
"hprRaw", "hprPct",
"sdRaw", "sdPct",
"mdRaw", "mdPct",
"ref", "thorns",
"ls",
"poison",
"expd",
"spd",
"atkTier",
"!elemental",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"!elemental",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"rainbowRaw",
"sprint", "sprintReg",
"jh",
"xpb", "lb", "lq",
"spRegen",
"eSteal",
"gXp", "gSpd",
];
let build_offensive_display_commands = [
"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",
"atkSpd",
"!elemental",
"hp",
"nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_",
"!spacer",
"fDef", "wDef", "aDef", "tDef", "eDef",
"!elemental",
"classReq",
"lvl",
"strReq", "dexReq", "intReq", "defReq","agiReq",
"!spacer",
"str", "dex", "int", "def", "agi",
"hpBonus",
"hprRaw", "hprPct",
"sdRaw", "sdPct",
"mdRaw", "mdPct",
"mr", "ms",
"ref", "thorns",
"ls",
"poison",
"expd",
"spd",
"atkTier",
"!elemental",
"fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct",
"fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct",
"!elemental",
"spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4",
"rainbowRaw",
"sprint", "sprintReg",
"jh",
"xpb", "lb", "lq",
"spRegen",
"eSteal",
"gXp", "gSpd",
"majorIds",
"!spacer",
"slots",
"!spacer",
"set",
"lore",
"quest",
"restrict"
];

6
sq2icons.js Normal file
View file

@ -0,0 +1,6 @@
//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()}

195
sq2items.js Normal file
View file

@ -0,0 +1,195 @@
const 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 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 itemFilters = []
for (let x in translate_mappings) {
itemFilters.push(x);
}
for (let x in special_mappings) {
itemFilters.push(x);
}
let itemCategories = [ "armor", "accessory", "weapon" ];
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-auto", "dark-7", "dark-shadow");
box.style.position = "absolute";
box.id = "item"+i;
box.addEventListener("dblclick", function() {set_item(item);});
items_parent.appendChild(box);
displaysq2ExpandedItem(item, box.id);
}
}
let items_expanded;
function doItemSearch() {
// window.scrollTo(0, 0);
let queries = [];
queries.push(new NameQuery(document.getElementById("name-choice").value.trim()));
let categoryOrType = document.getElementById("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("rarity-choice").value;
if (rarity) {
if (rarity === "ANY") {
}
else {
queries.push(new IdMatchQuery("tier", rarity));
}
}
let level_dat = document.getElementById("search-level-choice").value.split("-");
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);
console.log(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 init_items() {
items_expanded = items.filter( (i) => !("remapID" in i) ).map( (i) => expandItem(i, []) );
}
load_init(init_items);