This commit is contained in:
fin444 2022-08-03 11:12:28 -07:00
commit df0ab92bef
18 changed files with 323 additions and 181 deletions

File diff suppressed because one or more lines are too long

View file

@ -414,9 +414,6 @@
Active boosts Active boosts
</div> </div>
<div class="col"> <div class="col">
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="vanish-boost" onclick="update_boosts('vanish-boost')">
Vanish (+80%)
</button>
<button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')"> <button class="button-boost m-1 border-0 text-white dark-8u dark-shadow-sm" id="warscream-boost" onclick="update_boosts('warscream-boost')">
War Scream War Scream
</button> </button>

View file

@ -198,6 +198,60 @@ input.equipment-input:not(.is-invalid) {
max-width: 95%; max-width: 95%;
} }
@media screen and (orientation: landscape) and (max-width: 1199px) {
:root {
--scaled-fontsize: 1rem;
}
.scaled-font {
font-size: 1rem;
}
.box-title {
font-size: 1rem;
}
.item-title {
font-size: 1.2rem;
}
.big-title {
font-size: 1.5rem;
}
.skp-tooltip {
font-size: 1rem;
}
.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: 1200px) and (max-width: 1400px) { @media screen and (min-width: 1200px) and (max-width: 1400px) {
:root { :root {
--scaled-fontsize: 1rem; --scaled-fontsize: 1rem;

View file

@ -1,3 +1,8 @@
/**
* This file defines computation graph nodes and display code relevant to the ability tree.
* TODO: possibly split it up into compute and render... but its a bit complicated :/
*/
/** /**
ATreeNode spec: ATreeNode spec:
@ -74,6 +79,7 @@ stat_bonus: {
stat_scaling: { stat_scaling: {
"type": "stat_scaling", "type": "stat_scaling",
"slider": bool, "slider": bool,
positive: bool // True to keep stat above 0. False to ignore floor. Default: True for normal, False for scaling
"slider_name": Optional[str], "slider_name": Optional[str],
"slider_step": Optional[float], "slider_step": Optional[float],
round: Optional[bool] // Control floor behavior. True for stats and false for slider by default round: Optional[bool] // Control floor behavior. True for stats and false for slider by default
@ -754,10 +760,13 @@ const atree_scaling = new (class extends ComputeNode {
continue; continue;
case 'stat_scaling': case 'stat_scaling':
let total = 0; let total = 0;
const {round = true, slider = false, scaling = [0]} = effect; const {slider = false, scaling = [0]} = effect;
let { positive = true, round = true } = effect;
if (slider) { if (slider) {
const slider_val = slider_map.get(effect.slider_name).slider.value; const slider_val = slider_map.get(effect.slider_name).slider.value;
total = parseInt(slider_val) * scaling[0]; total = parseInt(slider_val) * scaling[0];
round = false;
positive = false;
} }
else { else {
// TODO: type: prop? // TODO: type: prop?
@ -768,7 +777,7 @@ const atree_scaling = new (class extends ComputeNode {
if ('output' in effect) { // sometimes nodes will modify slider without having effect. if ('output' in effect) { // sometimes nodes will modify slider without having effect.
if (round) { total = Math.floor(round_near(total)); } if (round) { total = Math.floor(round_near(total)); }
if (total < 0) { total = 0; } // Normal stat scaling will not go negative. if (positive && total < 0) { total = 0; } // Normal stat scaling will not go negative.
if ('max' in effect && total > effect.max) { total = effect.max; } if ('max' in effect && total > effect.max) { total = effect.max; }
if (Array.isArray(effect.output)) { if (Array.isArray(effect.output)) {
for (const output of effect.output) { for (const output of effect.output) {

View file

@ -5242,7 +5242,9 @@ const atrees = {
"Teleport" "Teleport"
], ],
"dependencies": [], "dependencies": [],
"blockers": [], "blockers": [
"Wisdom"
],
"cost": 1, "cost": 1,
"display": { "display": {
"row": 6, "row": 6,
@ -5387,20 +5389,6 @@ const atrees = {
0, 0,
0 0
] ]
},
{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Lightning Damage",
"behavior": "modify",
"multipliers": [
30,
90,
0,
0,
0,
0
]
} }
] ]
}, },
@ -5608,7 +5596,11 @@ const atrees = {
"Burning Sigil" "Burning Sigil"
], ],
"dependencies": [], "dependencies": [],
"blockers": [], "blockers": [
"Sunshower",
"Larger Heal",
"Orphion's Pulse"
],
"cost": 2, "cost": 2,
"display": { "display": {
"row": 15, "row": 15,
@ -7675,6 +7667,17 @@ const atrees = {
] ]
} }
] ]
},
{
"type": "raw_stat",
"toggle": "Activate Backstab",
"bonuses": [
{
"type": "stat",
"name": "damMult.Backstab:3.Backstab Damage",
"value": 100
}
]
} }
] ]
}, },

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,9 @@
/**
* This file defines a class representing the player Build.
*
* Keeps track of equipment list, equip order, skillpoint assignment (initial),
* Aggregates item stats into a statMap to be used in damage calculation.
*/
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.0], ["sword", 1.10]]); const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.0], ["sword", 1.10]]);
@ -10,15 +15,9 @@ class Build{
/** /**
* @description Construct a build. * @description Construct a build.
* @param {Number} level : Level of the player. * @param {Number} level : Level of the player.
* @param {String[]} equipment : List of equipment names that make up the build. * @param {String[]} items: List of equipment names that make up the build.
* In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon. * In order: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Tomes [x7].
* @param {Number[]} powders : Powder application. List of lists of integers (powder IDs). * @param {Item} weapon: Weapon that this build is using.
* In order: boots, Chestplate, Leggings, Boots, Weapon.
* @param {Object[]} inputerrors : List of instances of error-like classes.
*
* @param {Object[]} tomes: List of tomes.
* In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome.
* 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building)
*/ */
constructor(level, items, weapon){ constructor(level, items, weapon){
@ -39,11 +38,16 @@ class Build{
this.equipment = items; this.equipment = items;
this.weapon = weapon; this.weapon = weapon;
this.items = this.equipment.concat([this.weapon]); this.items = this.equipment.concat([this.weapon]);
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
// calc skillpoints requires statmaps only // calc skillpoints requires statmaps only
let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap); let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap);
this.equip_order = result[0]; this.equip_order = result[0].slice();
for (let i = 0; i < this.equip_order.length; i++) {
if (this.equip_order[i].get("category") === "tome" || this.equip_order[i].get("fixID") === true) {
this.equip_order.splice(i, 1);
i--;
}
}
// How many skillpoints the player had to assign (5 number) // How many skillpoints the player had to assign (5 number)
this.base_skillpoints = result[1]; this.base_skillpoints = result[1];
// How many skillpoints the build ended up with (5 number) // How many skillpoints the build ended up with (5 number)
@ -61,7 +65,6 @@ class Build{
return [this.equipment,this.weapon].flat(); return [this.equipment,this.weapon].flat();
} }
/* Get all stats for this build. Stores in this.statMap. /* Get all stats for this build. Stores in this.statMap.
@pre The build itself should be valid. No checking of validity of pieces is done here. @pre The build itself should be valid. No checking of validity of pieces is done here.
*/ */

View file

@ -1,3 +1,7 @@
/**
* File containing utility functions that are useful for the builder page.
*/
/*Turns the input amount of skill points into a float precision percentage. /*Turns the input amount of skill points into a float precision percentage.
* @param skp - the integer skillpoint count to be converted * @param skp - the integer skillpoint count to be converted
*/ */

View file

@ -1,3 +1,6 @@
/**
* File containing utility functions relevant to the builder page, as well as the setup code (at the very bottom).
*/
function populateBuildList() { function populateBuildList() {
const buildList = document.getElementById("build-choice"); const buildList = document.getElementById("build-choice");
@ -60,6 +63,9 @@ function resetFields(){
for (const i of equipment_inputs) { for (const i of equipment_inputs) {
setValue(i, ""); setValue(i, "");
} }
for (const i of tomeInputs) {
setValue(i, "");
}
setValue("str-skp", "0"); setValue("str-skp", "0");
setValue("dex-skp", "0"); setValue("dex-skp", "0");
setValue("int-skp", "0"); setValue("int-skp", "0");
@ -231,8 +237,9 @@ function init_autocomplete() {
for (const eq of tome_keys) { for (const eq of tome_keys) {
// build dropdown // build dropdown
let tome_arr = []; let tome_arr = [];
for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) { let tome_aliases = new Map();
let tome_obj = tomeMap.get(tome); for (const tome_name of tomeLists.get(eq.replace(/[0-9]/g, ''))) {
let tome_obj = tomeMap.get(tome_name);
if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") { if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") {
continue; continue;
} }
@ -240,8 +247,10 @@ function init_autocomplete() {
if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) { if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) {
continue; continue;
} }
let tome_name = tome; let tome_alias = tome_obj['alias'];
tome_arr.push(tome_name); tome_arr.push(tome_name);
tome_arr.push(tome_alias);
tome_aliases.set(tome_alias, tome_name);
} }
// create dropdown // create dropdown
@ -276,14 +285,18 @@ function init_autocomplete() {
class: "scaled-font search-item", class: "scaled-font search-item",
selected: "dark-5", selected: "dark-5",
element: (tome, data) => { element: (tome, data) => {
tome.classList.add(tomeMap.get(data.value).tier); let val = data.value;
if (tome_aliases.has(val)) { val = tome_aliases.get(val); }
tome.classList.add(tomeMap.get(val).tier);
}, },
}, },
events: { events: {
input: { input: {
selection: (event) => { selection: (event) => {
if (event.detail.selection.value) { if (event.detail.selection.value) {
event.target.value = event.detail.selection.value; let val = event.detail.selection.value;
if (tome_aliases.has(val)) { val = tome_aliases.get(val); }
event.target.value = val;
} }
event.target.dispatchEvent(new Event('change')); event.target.dispatchEvent(new Event('change'));
}, },

View file

@ -1,3 +1,7 @@
/**
* File containing compute graph structure of the builder page.
*/
let armor_powder_node = new (class extends ComputeNode { let armor_powder_node = new (class extends ComputeNode {
constructor() { super('builder-armor-powder-input'); } constructor() { super('builder-armor-powder-input'); }

View file

@ -31,16 +31,6 @@ let player_craft;
function init_crafter() { function init_crafter() {
//no ing
console.log("all ingredients");
console.log(ingMap);
console.log("all recipes");
console.log(recipeMap);
/*console.log(ingList);
console.log(recipeList);
console.log(ingIDMap);
console.log(recipeIDMap);*/
try { try {
document.getElementById("recipe-choice").addEventListener("change", (event) => { document.getElementById("recipe-choice").addEventListener("change", (event) => {
updateMaterials(); updateMaterials();
@ -56,17 +46,16 @@ function init_crafter() {
}); });
for (let i = 1; i < 4; ++i) { for (let i = 1; i < 4; ++i) {
document.getElementById("mat-1-"+i).setAttribute("onclick", document.getElementById("mat-1-"+i).getAttribute("onclick") + "; calculateCraftSchedule();"); document.getElementById("mat-1-"+i).addEventListener("click", (e) => calculateCraftSchedule());
document.getElementById("mat-2-"+i).setAttribute("onclick", document.getElementById("mat-2-"+i).getAttribute("onclick") + "; calculateCraftSchedule();"); document.getElementById("mat-2-"+i).addEventListener("click", (e) => calculateCraftSchedule());
} }
for (let i = 1; i < 7; ++i) { for (let i = 1; i < 7; ++i) {
document.getElementById("ing-choice-" + i ).setAttribute("oninput", "calculateCraftSchedule();"); document.getElementById("ing-choice-" + i ).addEventListener("oninput", (e) => calculateCraftSchedule());
} }
for (const str of ["slow", "normal", "fast"]) { for (const str of ["slow", "normal", "fast"]) {
document.getElementById(str + "-atk-button").setAttribute("onclick", document.getElementById(str + "-atk-button").getAttribute("onclick") + "; calculateCraftSchedule();"); document.getElementById(str + "-atk-button").addEventListener("onclick", (e) => calculateCraftSchedule());
} }
populateFields(); populateFields();
decodeCraft(ing_url_tag); decodeCraft(ing_url_tag);
} catch (error) { } catch (error) {
@ -259,12 +248,10 @@ function populateFields() {
ing_list.appendChild(el); ing_list.appendChild(el);
} }
} }
} }
/* /*
Copies the CR Hash (CR-blahblahblah) * Copies the CR Hash (CR-blahblahblah)
*/ */
function copyRecipeHash() { function copyRecipeHash() {
if (player_craft) { if (player_craft) {
copyTextToClipboard("CR-"+location.hash.slice(1)); copyTextToClipboard("CR-"+location.hash.slice(1));

View file

@ -1,4 +1,8 @@
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.00], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]); /**
* File implementing core damage calculation logic.
*/
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["warscream", 0.00], ["ragnarokkr", 0.30], ["fortitude", 0.60] ]);
function get_base_dps(item) { function get_base_dps(item) {
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))]; const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];

View file

@ -1,3 +1,8 @@
/**
* File containing generic display code, ex. for displaying items and spell damage.
* TODO: split this file into separate parts for each "component".
*/
const itemBGPositions = {"bow": "0 0", "spear": "9.090909090909088% 0", "wand": "18.181818181818183% 0", "dagger": "27.27272727272727% 0", "relik": "36.36363636363637% 0", const itemBGPositions = {"bow": "0 0", "spear": "9.090909090909088% 0", "wand": "18.181818181818183% 0", "dagger": "27.27272727272727% 0", "relik": "36.36363636363637% 0",
"helmet": "45.45454545454546% 0", "chestplate": "54.54545454545454% 0", "leggings": "63.63636363636363% 0", "boots": "72.72727272727272% 0", "helmet": "45.45454545454546% 0", "chestplate": "54.54545454545454% 0", "leggings": "63.63636363636363% 0", "boots": "72.72727272727272% 0",
"ring": "81.81818181818181% 0", "bracelet": "90.90909090909092% 0", "necklace": "100% 0", "ring": "81.81818181818181% 0", "bracelet": "90.90909090909092% 0", "necklace": "100% 0",
@ -388,46 +393,45 @@ function displayExpandedItem(item, parent_id){
} }
} }
//Show powder specials ;-; //Show powder specials ;-;
let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots"];//, "ring", "bracelet", "necklace"]; let powder_specials_check = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots"];
if(nonConsumables.includes(item.get("type"))) { if(powder_specials_check.includes(item.get("type"))) {
let powder_special = document.createElement("div"); let powder_special = make_elem("div", ['col']);
powder_special.classList.add("col");
let powders = item.get("powders"); let powders = item.get("powders");
let element = ""; let element;
let power = 0; let power_index;
for (let i = 0; i < powders.length; i++) { for (let i = 0; i < powders.length; i++) {
let firstPowderType = skp_elements[Math.floor(powders[i]/6)]; const firstPowderType = skp_elements[Math.floor(powders[i]/6)];
if (element !== "") break; const powder1_power = powders[i] % 6;
else if (powders[i]%6 > 2) { //t4+ if (powder1_power > 2) { //t4+
for (let j = i+1; j < powders.length; j++) { for (let j = i+1; j < powders.length; j++) {
let currentPowderType = skp_elements[Math.floor(powders[j]/6)] const currentPowderType = skp_elements[Math.floor(powders[j]/6)]
if (powders[j] % 6 > 2 && firstPowderType === currentPowderType) { const powder2_power = powders[j] % 6;
if (powder2_power > 2 && firstPowderType === currentPowderType) {
element = currentPowderType; element = currentPowderType;
power = Math.round(((powders[i] % 6 + powders[j] % 6 + 2) / 2 - 4) * 2); power_index = powder1_power + powder2_power - 6;
break; break;
} }
} }
} }
} }
if (element !== "") {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]" if (element) {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]"
let powderSpecial = powderSpecialStats[ skp_elements.indexOf(element)]; const powderSpecial = powderSpecialStats[skp_elements.indexOf(element)];
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); const specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("span"); const specialTitle = make_elem("span", [damageClasses[skp_elements.indexOf(element) + 1]]);
let specialEffects = document.createElement("span"); const specialEffects = document.createElement("span");
addClasses(specialTitle, [damageClasses[skp_elements.indexOf(element) + 1]]);
let effects; let effects;
if (item.get("category") === "weapon") {//weapon if (item.get("category") === "weapon") {//weapon
effects = powderSpecial["weaponSpecialEffects"]; effects = powderSpecial["weaponSpecialEffects"];
specialTitle.textContent = powderSpecial["weaponSpecialName"]; specialTitle.textContent = powderSpecial["weaponSpecialName"];
}else if (item.get("category") === "armor") {//armor } else if (item.get("category") === "armor") {//armor
effects = powderSpecial["armorSpecialEffects"]; effects = powderSpecial["armorSpecialEffects"];
specialTitle.textContent += powderSpecial["armorSpecialName"] + ": "; specialTitle.textContent += powderSpecial["armorSpecialName"] + ": ";
} }
for (const [key,value] of effects.entries()) { for (const [key,value] of effects.entries()) {
if (key !== "Description") { if (key !== "Description") {
let effect = document.createElement("p"); let effect = make_elem("p", ["m-0"], {
effect.classList.add("m-0"); textContent: key + ": " + value[power_index] + specialSuffixes.get(key)
effect.textContent = key + ": " + value[power] + specialSuffixes.get(key); });
if(key === "Damage"){ if(key === "Damage"){
effect.textContent += elementIcons[skp_elements.indexOf(element)]; effect.textContent += elementIcons[skp_elements.indexOf(element)];
} }
@ -435,20 +439,19 @@ function displayExpandedItem(item, parent_id){
effect.textContent += " / Mana Used"; effect.textContent += " / Mana Used";
} }
specialEffects.appendChild(effect); specialEffects.appendChild(effect);
}else{ } else {
specialTitle.textContent += "[ " + effects.get("Description") + " ]"; specialTitle.textContent += "[ " + effects.get("Description") + " ]";
} }
} }
powder_special.appendChild(specialTitle); powder_special.append(specialTitle, specialEffects);
powder_special.appendChild(specialEffects);
parent_div.appendChild(powder_special); parent_div.appendChild(powder_special);
} }
} }
let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots", "ring", "bracelet", "necklace"];
if(item.get("tier") && item.get("tier") === "Crafted") { if(item.get("tier") && item.get("tier") === "Crafted") {
let dura_elem = document.createElement("div"); let dura_elem = make_elem("div", ["col"]);
dura_elem.classList.add("col"); let dura;
let dura = [];
let suffix = ""; let suffix = "";
if(nonConsumables.includes(item.get("type"))) { if(nonConsumables.includes(item.get("type"))) {
dura = item.get("durability"); dura = item.get("durability");
@ -457,9 +460,9 @@ function displayExpandedItem(item, parent_id){
dura = item.get("duration"); dura = item.get("duration");
dura_elem.textContent = "Duration: " dura_elem.textContent = "Duration: "
suffix = " sec." suffix = " sec."
let charges = document.createElement("b"); parent_div.appendChild(make_elem('b', [], {
charges.textContent = "Charges: " + item.get("charges"); textContent: "Charges: " + item.get("charges")
parent_div.appendChild(charges); }));
} }
if (typeof(dura) === "string") { if (typeof(dura) === "string") {
@ -472,9 +475,7 @@ function displayExpandedItem(item, parent_id){
} }
//Show item tier //Show item tier
if (item.get("tier") && item.get("tier") !== " ") { if (item.get("tier") && item.get("tier") !== " ") {
let item_desc_elem = document.createElement("div"); let item_desc_elem = make_elem("div", ["col", item.get("tier")]);
item_desc_elem.classList.add("col");
item_desc_elem.classList.add(item.get("tier"));
if (tome_types.includes(item.get("type"))) { if (tome_types.includes(item.get("type"))) {
tome_type_map = new Map([["weaponTome", "Weapon Tome"],["armorTome", "Armor Tome"],["guildTome", "Guild Tome"]]); tome_type_map = new Map([["weaponTome", "Weapon Tome"],["armorTome", "Armor Tome"],["guildTome", "Guild Tome"]]);
item_desc_elem.textContent = item.get("tier")+" "+tome_type_map.get(item.get("type")); item_desc_elem.textContent = item.get("tier")+" "+tome_type_map.get(item.get("type"));
@ -486,20 +487,19 @@ function displayExpandedItem(item, parent_id){
//Show item hash if applicable //Show item hash if applicable
if (item.get("crafted") || item.get("custom")) { if (item.get("crafted") || item.get("custom")) {
let item_desc_elem = document.createElement("p"); parent_div.append(make_elem('p', ['itemp'], {
item_desc_elem.classList.add('itemp'); style: {
item_desc_elem.style.maxWidth = "100%"; maxWidth: '100%',
item_desc_elem.style.wordWrap = "break-word"; wordWrap: 'break-word',
item_desc_elem.style.wordBreak = "break-word"; wordBreak: 'break-word'
item_desc_elem.textContent = item.get("hash"); },
parent_div.append(item_desc_elem); textContent: item.get('hash')
}));
} }
if (item.get("category") === "weapon") { if (item.get("category") === "weapon") {
let total_damages = item.get("basedps"); let total_damages = item.get("basedps");
let base_dps_elem = document.createElement("p"); let base_dps_elem = make_elem("p", ["left", "itemp"]);
base_dps_elem.classList.add("left");
base_dps_elem.classList.add("itemp");
if (item.get("tier") === "Crafted") { if (item.get("tier") === "Crafted") {
let base_dps_min = total_damages[0]; let base_dps_min = total_damages[0];
let base_dps_max = total_damages[1]; let base_dps_max = total_damages[1];
@ -509,8 +509,7 @@ function displayExpandedItem(item, parent_id){
else { else {
base_dps_elem.textContent = "Base DPS: "+(total_damages); base_dps_elem.textContent = "Base DPS: "+(total_damages);
} }
parent_div.appendChild(document.createElement("p")); parent_div.append(make_elem("p"), base_dps_elem);
parent_div.appendChild(base_dps_elem);
} }
} }
@ -1268,8 +1267,8 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
//iterate through the special and display its effects. //iterate through the special and display its effects.
let powder_special = make_elem("p", ["pt-3"]); let powder_special = make_elem("p", ["pt-3"]);
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]); let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("p"); let specialTitle = make_elem("p");
let specialEffects = document.createElement("p"); let specialEffects = make_elem("p");
specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]); specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]);
let effects = special[0]["weaponSpecialEffects"]; let effects = special[0]["weaponSpecialEffects"];
let power = special[1]; let power = special[1];

View file

@ -197,7 +197,7 @@ function init_ing_maps() {
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0} posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}
}; };
ing.id = 4001 + ing.pid; ing.id = 4001 + ing.pid;
ing.diplayName = ing.name; ing.displayName = ing.name;
switch(i) { switch(i) {
case 0: case 0:
ing.itemIDs["strReq"] = powderIng["skpReq"]; ing.itemIDs["strReq"] = powderIng["skpReq"];

View file

@ -1,4 +1,4 @@
const TOME_DB_VERSION = 3; const TOME_DB_VERSION = 4;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
let tdb; let tdb;

View file

@ -187,8 +187,17 @@ function calculate_skillpoints(equipment, weapon) {
permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item))); permute_check(idx+1, skillpoints_applied, skillpoints, activeSetCounts, has_skillpoint, total_applied, order.concat(permutation.map(x => x.item)));
} }
} }
// skip root. if (sccs.length === 1) {
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []); // Only crafteds. Just do end check (check req first, then apply sp after)
const total = check_end(best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice());
final_skillpoints = best_skillpoints;
best_total = total;
best_activeSetCounts = best_activeSetCounts;
best = [];
} else {
// skip root.
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []);
}
// add extra sp bonus // add extra sp bonus
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts); apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);

View file

@ -612,16 +612,6 @@ function matchType(object, target) {
return object; return object;
} }
/**
* Add multiple classes to a html element
*/
function addClasses(elem, classes) {
for (let _class of classes) {
elem.classList.add(_class);
}
return elem;
}
/** A utility function that reloads the page forcefully. /** A utility function that reloads the page forcefully.
* *
*/ */
@ -892,6 +882,17 @@ function make_elem(type, classlist = [], args = {}) {
const ret_elem = document.createElement(type); const ret_elem = document.createElement(type);
ret_elem.classList.add(...classlist); ret_elem.classList.add(...classlist);
for (const i in args) { for (const i in args) {
if (i === 'style') {
const style_obj = args[i];
if (typeof style_obj === 'string' || style_obj instanceof String) {
ret_elem.style = style_obj;
continue;
}
for (const k in style_obj) {
ret_elem.style[k] = style_obj[k];
}
continue;
}
ret_elem[i] = args[i]; ret_elem[i] = args[i];
} }
return ret_elem; return ret_elem;

View file

@ -1,6 +1,5 @@
{ {
"tomes": "tomes": [
[
{ {
"name": "Retaliating Tome of Armour Mastery I", "name": "Retaliating Tome of Armour Mastery I",
"tier": "Fabled", "tier": "Fabled",
@ -14,7 +13,8 @@
"ref": 6, "ref": 6,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 0 "id": 0,
"alias": "Thorns I"
}, },
{ {
"name": "Retaliating Tome of Armour Mastery II", "name": "Retaliating Tome of Armour Mastery II",
@ -28,7 +28,8 @@
"thorns": 8, "thorns": 8,
"ref": 8, "ref": 8,
"fixID": false, "fixID": false,
"id": 1 "id": 1,
"alias": "Thorns II"
}, },
{ {
"name": "Destructive Tome of Armour Mastery I", "name": "Destructive Tome of Armour Mastery I",
@ -43,7 +44,8 @@
"mdPct": 5, "mdPct": 5,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 2 "id": 2,
"alias": "Melee I"
}, },
{ {
"name": "Destructive Tome of Armour Mastery II", "name": "Destructive Tome of Armour Mastery II",
@ -57,7 +59,8 @@
"thorns": 6, "thorns": 6,
"reflection": 6, "reflection": 6,
"fixID": false, "fixID": false,
"id": 3 "id": 3,
"alias": "Melee II"
}, },
{ {
"name": "Sorcerer's Tome of Armour Mastery I", "name": "Sorcerer's Tome of Armour Mastery I",
@ -71,7 +74,8 @@
"sdPct": 5, "sdPct": 5,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 4 "id": 4,
"alias": "Spell Damage I"
}, },
{ {
"name": "Sorcerer's Tome of Armour Mastery II", "name": "Sorcerer's Tome of Armour Mastery II",
@ -84,7 +88,8 @@
"defMobs": 5, "defMobs": 5,
"sdPct": 6, "sdPct": 6,
"fixID": false, "fixID": false,
"id": 5 "id": 5,
"alias": "Spell Damage II"
}, },
{ {
"name": "Everlasting Tome of Armour Mastery I", "name": "Everlasting Tome of Armour Mastery I",
@ -98,7 +103,8 @@
"hprRaw": 15, "hprRaw": 15,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 6 "id": 6,
"alias": "Health Regen I"
}, },
{ {
"name": "Everlasting Tome of Armour Mastery II", "name": "Everlasting Tome of Armour Mastery II",
@ -111,7 +117,8 @@
"defMobs": 5, "defMobs": 5,
"hprRaw": 60, "hprRaw": 60,
"fixID": false, "fixID": false,
"id": 7 "id": 7,
"alias": "Health Regen II"
}, },
{ {
"name": "Vampiric Tome of Armour Mastery I", "name": "Vampiric Tome of Armour Mastery I",
@ -125,7 +132,8 @@
"ls": 25, "ls": 25,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 8 "id": 8,
"alias": "Life Steal I"
}, },
{ {
"name": "Vampiric Tome of Armour Mastery II", "name": "Vampiric Tome of Armour Mastery II",
@ -138,7 +146,8 @@
"defMobs": 5, "defMobs": 5,
"ls": 85, "ls": 85,
"fixID": false, "fixID": false,
"id": 9 "id": 9,
"alias": "Life Steal II"
}, },
{ {
"name": "Greedy Tome of Armour Mastery I", "name": "Greedy Tome of Armour Mastery I",
@ -152,7 +161,8 @@
"lb": 5, "lb": 5,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 10 "id": 10,
"alias": "Loot Bonus I"
}, },
{ {
"name": "Greedy Tome of Armour Mastery II", "name": "Greedy Tome of Armour Mastery II",
@ -165,7 +175,8 @@
"defMobs": 5, "defMobs": 5,
"lb": 6, "lb": 6,
"fixID": false, "fixID": false,
"id": 11 "id": 11,
"alias": "Loot Bonus II"
}, },
{ {
"name": "Weightless Tome of Armour Mastery I", "name": "Weightless Tome of Armour Mastery I",
@ -179,7 +190,8 @@
"spd": 5, "spd": 5,
"hpBonus": 120, "hpBonus": 120,
"fixID": false, "fixID": false,
"id": 12 "id": 12,
"alias": "Walk Speed I"
}, },
{ {
"name": "Weightless Tome of Armour Mastery II", "name": "Weightless Tome of Armour Mastery II",
@ -192,7 +204,8 @@
"defMobs": 5, "defMobs": 5,
"spd": 6, "spd": 6,
"fixID": false, "fixID": false,
"id": 13 "id": 13,
"alias": "Walk Speed II"
}, },
{ {
"name": "Blooming Tome of Armour Mastery II", "name": "Blooming Tome of Armour Mastery II",
@ -206,7 +219,8 @@
"eDefPct": 10, "eDefPct": 10,
"hpBonus": 150, "hpBonus": 150,
"fixID": false, "fixID": false,
"id": 14 "id": 14,
"alias": "Earth Defense II"
}, },
{ {
"name": "Pulsing Tome of Armour Mastery II", "name": "Pulsing Tome of Armour Mastery II",
@ -220,7 +234,8 @@
"tDefPct": 10, "tDefPct": 10,
"hpBonus": 150, "hpBonus": 150,
"fixID": false, "fixID": false,
"id": 15 "id": 15,
"alias": "Thunder Defense II"
}, },
{ {
"name": "Oceanic Tome of Armour Mastery II", "name": "Oceanic Tome of Armour Mastery II",
@ -234,7 +249,8 @@
"wDefPct": 10, "wDefPct": 10,
"hpBonus": 150, "hpBonus": 150,
"fixID": false, "fixID": false,
"id": 16 "id": 16,
"alias": "Water Defense II"
}, },
{ {
"name": "Courageous Tome of Armour Mastery II", "name": "Courageous Tome of Armour Mastery II",
@ -248,7 +264,8 @@
"fDefPct": 10, "fDefPct": 10,
"hpBonus": 150, "hpBonus": 150,
"fixID": false, "fixID": false,
"id": 17 "id": 17,
"alias": "Fire Defense II"
}, },
{ {
"name": "Clouded Tome of Armour Mastery II", "name": "Clouded Tome of Armour Mastery II",
@ -262,7 +279,8 @@
"aDefPct": 10, "aDefPct": 10,
"hpBonus": 150, "hpBonus": 150,
"fixID": false, "fixID": false,
"id": 18 "id": 18,
"alias": "Air Defense II"
}, },
{ {
"name": "Radiant Tome of Armour Mastery II", "name": "Radiant Tome of Armour Mastery II",
@ -280,7 +298,8 @@
"aDefPct": 6, "aDefPct": 6,
"hpBonus": 150, "hpBonus": 150,
"fixID": false, "fixID": false,
"id": 19 "id": 19,
"alias": "Rainbow Defense II"
}, },
{ {
"name": "Tome of Weapon Mastery I", "name": "Tome of Weapon Mastery I",
@ -292,7 +311,8 @@
"lvl": 60, "lvl": 60,
"damMobs": 6, "damMobs": 6,
"fixID": false, "fixID": false,
"id": 20 "id": 20,
"alias": "Weapon Mastery I"
}, },
{ {
"name": "Earthbound Tome of Weapon Mastery I", "name": "Earthbound Tome of Weapon Mastery I",
@ -305,7 +325,8 @@
"damMobs": 7, "damMobs": 7,
"str": 3, "str": 3,
"fixID": false, "fixID": false,
"id": 21 "id": 21,
"alias": "Strength I"
}, },
{ {
"name": "Earthbound Tome of Weapon Mastery II", "name": "Earthbound Tome of Weapon Mastery II",
@ -318,7 +339,8 @@
"damMobs": 8, "damMobs": 8,
"str": 3, "str": 3,
"fixID": false, "fixID": false,
"id": 22 "id": 22,
"alias": "Strength II"
}, },
{ {
"name": "Nimble Tome of Weapon Mastery I", "name": "Nimble Tome of Weapon Mastery I",
@ -331,7 +353,8 @@
"damMobs": 7, "damMobs": 7,
"dex": 3, "dex": 3,
"fixID": false, "fixID": false,
"id": 23 "id": 23,
"alias": "Dexterity I"
}, },
{ {
"name": "Nimble Tome of Weapon Mastery II", "name": "Nimble Tome of Weapon Mastery II",
@ -344,7 +367,8 @@
"damMobs": 8, "damMobs": 8,
"dex": 3, "dex": 3,
"fixID": false, "fixID": false,
"id": 24 "id": 24,
"alias": "Dexterity II"
}, },
{ {
"name": "Mystical Tome of Weapon Mastery I", "name": "Mystical Tome of Weapon Mastery I",
@ -357,7 +381,8 @@
"damMobs": 7, "damMobs": 7,
"int": 3, "int": 3,
"fixID": false, "fixID": false,
"id": 25 "id": 25,
"alias": "Intelligence I"
}, },
{ {
"name": "Mystical Tome of Weapon Mastery II", "name": "Mystical Tome of Weapon Mastery II",
@ -370,7 +395,8 @@
"damMobs": 8, "damMobs": 8,
"int": 3, "int": 3,
"fixID": false, "fixID": false,
"id": 26 "id": 26,
"alias": "Intelligence II"
}, },
{ {
"name": "Warding Tome of Weapon Mastery I", "name": "Warding Tome of Weapon Mastery I",
@ -383,7 +409,8 @@
"damMobs": 7, "damMobs": 7,
"def": 3, "def": 3,
"fixID": false, "fixID": false,
"id": 27 "id": 27,
"alias": "Defense I"
}, },
{ {
"name": "Warding Tome of Weapon Mastery II", "name": "Warding Tome of Weapon Mastery II",
@ -396,7 +423,8 @@
"damMobs": 8, "damMobs": 8,
"def": 3, "def": 3,
"fixID": false, "fixID": false,
"id": 28 "id": 28,
"alias": "Defense II"
}, },
{ {
"name": "Athletic Tome of Weapon Mastery I", "name": "Athletic Tome of Weapon Mastery I",
@ -409,7 +437,8 @@
"damMobs": 7, "damMobs": 7,
"agi": 3, "agi": 3,
"fixID": false, "fixID": false,
"id": 29 "id": 29,
"alias": "Agility I"
}, },
{ {
"name": "Athletic Tome of Weapon Mastery II", "name": "Athletic Tome of Weapon Mastery II",
@ -422,7 +451,8 @@
"damMobs": 8, "damMobs": 8,
"agi": 3, "agi": 3,
"fixID": false, "fixID": false,
"id": 30 "id": 30,
"alias": "Agility II"
}, },
{ {
"name": "Cosmic Tome of Weapon Mastery I", "name": "Cosmic Tome of Weapon Mastery I",
@ -439,7 +469,8 @@
"def": 1, "def": 1,
"agi": 1, "agi": 1,
"fixID": false, "fixID": false,
"id": 31 "id": 31,
"alias": "Rainbow Skillpoint I"
}, },
{ {
"name": "Cosmic Tome of Weapon Mastery II", "name": "Cosmic Tome of Weapon Mastery II",
@ -456,7 +487,8 @@
"def": 1, "def": 1,
"agi": 1, "agi": 1,
"fixID": false, "fixID": false,
"id": 32 "id": 32,
"alias": "Rainbow Skillpoint II"
}, },
{ {
"name": "Seismic Tome of Weapon Mastery II", "name": "Seismic Tome of Weapon Mastery II",
@ -469,7 +501,8 @@
"damMobs": 12, "damMobs": 12,
"eDamPct": 7, "eDamPct": 7,
"fixID": false, "fixID": false,
"id": 33 "id": 33,
"alias": "Earth Damage II"
}, },
{ {
"name": "Voltaic Tome of Weapon Mastery II", "name": "Voltaic Tome of Weapon Mastery II",
@ -482,7 +515,8 @@
"damMobs": 12, "damMobs": 12,
"tDamPct": 7, "tDamPct": 7,
"fixID": false, "fixID": false,
"id": 34 "id": 34,
"alias": "Thunder Damage II"
}, },
{ {
"name": "Abyssal Tome of Weapon Mastery II", "name": "Abyssal Tome of Weapon Mastery II",
@ -495,7 +529,8 @@
"damMobs": 12, "damMobs": 12,
"wDamPct": 7, "wDamPct": 7,
"fixID": false, "fixID": false,
"id": 35 "id": 35,
"alias": "Water Damage II"
}, },
{ {
"name": "Infernal Tome of Weapon Mastery II", "name": "Infernal Tome of Weapon Mastery II",
@ -508,7 +543,8 @@
"damMobs": 12, "damMobs": 12,
"fDamPct": 7, "fDamPct": 7,
"fixID": false, "fixID": false,
"id": 36 "id": 36,
"alias": "Fire Damage II"
}, },
{ {
"name": "Cyclonic Tome of Weapon Mastery II", "name": "Cyclonic Tome of Weapon Mastery II",
@ -521,7 +557,8 @@
"damMobs": 12, "damMobs": 12,
"aDamPct": 7, "aDamPct": 7,
"fixID": false, "fixID": false,
"id": 37 "id": 37,
"alias": "Air Damage II"
}, },
{ {
"name": "Astral Tome of Weapon Mastery II", "name": "Astral Tome of Weapon Mastery II",
@ -538,7 +575,8 @@
"fDamPct": 6, "fDamPct": 6,
"aDamPct": 6, "aDamPct": 6,
"fixID": false, "fixID": false,
"id": 38 "id": 38,
"alias": "Rainbow Damage II"
}, },
{ {
"name": "Brute's Tome of Allegiance", "name": "Brute's Tome of Allegiance",
@ -551,7 +589,8 @@
"str": 3, "str": 3,
"eDamPct": 2, "eDamPct": 2,
"fixID": false, "fixID": false,
"id": 39 "id": 39,
"alias": "Strength"
}, },
{ {
"name": "Sadist's Tome of Allegiance", "name": "Sadist's Tome of Allegiance",
@ -564,7 +603,8 @@
"dex": 3, "dex": 3,
"tDamPct": 2, "tDamPct": 2,
"fixID": false, "fixID": false,
"id": 40 "id": 40,
"alias": "Dexterity"
}, },
{ {
"name": "Mastermind's Tome of Allegiance", "name": "Mastermind's Tome of Allegiance",
@ -577,7 +617,8 @@
"int": 3, "int": 3,
"wDamPct": 2, "wDamPct": 2,
"fixID": false, "fixID": false,
"id": 41 "id": 41,
"alias": "Intelligence"
}, },
{ {
"name": "Arsonist's Tome of Allegiance", "name": "Arsonist's Tome of Allegiance",
@ -590,7 +631,8 @@
"def": 3, "def": 3,
"fDamPct": 2, "fDamPct": 2,
"fixID": false, "fixID": false,
"id": 42 "id": 42,
"alias": "Defense"
}, },
{ {
"name": "Ghost's Tome of Allegiance", "name": "Ghost's Tome of Allegiance",
@ -603,7 +645,8 @@
"agi": 3, "agi": 3,
"aDamPct": 2, "aDamPct": 2,
"fixID": false, "fixID": false,
"id": 43 "id": 43,
"alias": "Agility"
}, },
{ {
"name": "Psychopath's Tome of Allegiance", "name": "Psychopath's Tome of Allegiance",
@ -616,7 +659,8 @@
"str": 2, "str": 2,
"dex": 2, "dex": 2,
"fixID": false, "fixID": false,
"id": 44 "id": 44,
"alias": "ET"
}, },
{ {
"name": "Loner's Tome of Allegiance", "name": "Loner's Tome of Allegiance",
@ -629,7 +673,8 @@
"str": 2, "str": 2,
"int": 2, "int": 2,
"fixID": false, "fixID": false,
"id": 45 "id": 45,
"alias": "EW"
}, },
{ {
"name": "Warlock's Tome of Allegiance", "name": "Warlock's Tome of Allegiance",
@ -642,7 +687,8 @@
"dex": 2, "dex": 2,
"int": 2, "int": 2,
"fixID": false, "fixID": false,
"id": 46 "id": 46,
"alias": "TW"
}, },
{ {
"name": "Destroyer's Tome of Allegiance", "name": "Destroyer's Tome of Allegiance",
@ -655,7 +701,8 @@
"str": 2, "str": 2,
"def": 2, "def": 2,
"fixID": false, "fixID": false,
"id": 47 "id": 47,
"alias": "EF"
}, },
{ {
"name": "Devil's Tome of Allegiance", "name": "Devil's Tome of Allegiance",
@ -668,7 +715,8 @@
"dex": 2, "dex": 2,
"def": 2, "def": 2,
"fixID": false, "fixID": false,
"id": 48 "id": 48,
"alias": "TF"
}, },
{ {
"name": "Alchemist's Tome of Allegiance", "name": "Alchemist's Tome of Allegiance",
@ -681,7 +729,8 @@
"int": 2, "int": 2,
"def": 2, "def": 2,
"fixID": false, "fixID": false,
"id": 49 "id": 49,
"alias": "WF"
}, },
{ {
"name": "Barbarian's Tome of Allegiance", "name": "Barbarian's Tome of Allegiance",
@ -694,7 +743,8 @@
"str": 2, "str": 2,
"agi": 2, "agi": 2,
"fixID": false, "fixID": false,
"id": 50 "id": 50,
"alias": "EA"
}, },
{ {
"name": "Freelancer's Tome of Allegiance", "name": "Freelancer's Tome of Allegiance",
@ -707,7 +757,8 @@
"dex": 2, "dex": 2,
"agi": 2, "agi": 2,
"fixID": false, "fixID": false,
"id": 51 "id": 51,
"alias": "TA"
}, },
{ {
"name": "Sycophant's Tome of Allegiance", "name": "Sycophant's Tome of Allegiance",
@ -720,7 +771,8 @@
"int": 2, "int": 2,
"agi": 2, "agi": 2,
"fixID": false, "fixID": false,
"id": 52 "id": 52,
"alias": "WA"
}, },
{ {
"name": "Fanatic's Tome of Allegiance", "name": "Fanatic's Tome of Allegiance",
@ -733,7 +785,8 @@
"def": 2, "def": 2,
"agi": 2, "agi": 2,
"fixID": false, "fixID": false,
"id": 53 "id": 53,
"alias": "FA"
}, },
{ {
"name": "Assimilator's Tome of Allegiance", "name": "Assimilator's Tome of Allegiance",
@ -749,7 +802,8 @@
"def": 1, "def": 1,
"agi": 1, "agi": 1,
"fixID": false, "fixID": false,
"id": 54 "id": 54,
"alias": "Rainbow"
} }
] ]
} }