Damage Stats
@@ -446,7 +618,7 @@
Spell Damage %:
-
+
Original Value: 0
@@ -457,7 +629,7 @@
Spell Damage Raw:
-
+
Original Value: 0
@@ -468,7 +640,7 @@
Melee Damage %:
-
+
Original Value: 0
@@ -479,7 +651,7 @@
Melee Damage Raw:
-
+
Original Value: 0
@@ -492,7 +664,7 @@
Poison:
-
+
Original Value: 0
@@ -503,7 +675,7 @@
Damage %:
-
+
Original Value: 0
@@ -514,7 +686,7 @@
Damage %:
-
+
Original Value: 0
@@ -525,7 +697,7 @@
Damage %:
-
+
Original Value: 0
@@ -538,7 +710,7 @@
Damage %:
-
+
Original Value: 0
@@ -549,7 +721,7 @@
Damage %:
-
+
Original Value: 0
@@ -560,7 +732,7 @@
+ Tier:
-
+
Original Value: 0
@@ -579,7 +751,7 @@
Defense %:
-
+
Original Value: 0
@@ -590,7 +762,7 @@
Defense %:
-
+
Original Value: 0
@@ -601,7 +773,7 @@
Defense %:
-
+
Original Value: 0
@@ -612,7 +784,7 @@
Defense %:
-
+
Original Value: 0
@@ -625,7 +797,7 @@
Defense %:
-
+
Original Value: 0
@@ -636,7 +808,7 @@
Health Regen Raw:
-
+
Original Value: 0
@@ -647,7 +819,7 @@
Health Regen %:
-
+
Original Value: 0
@@ -658,7 +830,7 @@
Health Bonus:
-
+
Original Value: 0
@@ -674,7 +846,7 @@
1st Spell Cost %:
-
+
Original Value: 0
@@ -685,7 +857,7 @@
2nd Spell Cost %:
-
+
Original Value: 0
@@ -696,7 +868,7 @@
3rd Spell Cost %:
-
+
Original Value: 0
@@ -707,7 +879,7 @@
4th Spell Cost %:
-
+
Original Value: 0
@@ -720,7 +892,7 @@
1st Spell Cost Raw:
-
+
Original Value: 0
@@ -731,7 +903,7 @@
2nd Spell Cost Raw:
-
+
Original Value: 0
@@ -742,7 +914,7 @@
3rd Spell Cost Raw:
-
+
Original Value: 0
@@ -753,7 +925,7 @@
4th Spell Cost Raw:
-
+
Original Value: 0
@@ -1118,6 +1290,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1209,6 +1395,7 @@
+
diff --git a/css/wynnstyles.css b/css/wynnstyles.css
index 4b834a6..1a87e2f 100644
--- a/css/wynnstyles.css
+++ b/css/wynnstyles.css
@@ -173,3 +173,7 @@ Wynn-Related CSS
}
.aDam:before, .Air:before, .Air_powder:before { content: "\274b" ' '; }
+
+.restrict {
+ color: #ff8180;
+}
\ No newline at end of file
diff --git a/js/build_utils.js b/js/build_utils.js
index 6192b4a..16bacb5 100644
--- a/js/build_utils.js
+++ b/js/build_utils.js
@@ -51,7 +51,7 @@ const armorTypes = [ "helmet", "chestplate", "leggings", "boots" ];
const accessoryTypes = [ "ring", "bracelet", "necklace" ];
const weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
const consumableTypes = [ "potion", "scroll", "food"];
-const tomeTypes = ["armorTome", "weaponTome", "guildTome", "dungeonTome", "gatheringTome", "slayingTome"]
+const tomeTypes = ["armorTome", "weaponTome", "guildTome"]; //"dungeonTome", "gatheringTome", "slayingTome"
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
//0.51, 0.82, 1.50, 2.05, 2.50, 3.11, 4.27
diff --git a/js/load.js b/js/load.js
index d04b729..7f614e2 100644
--- a/js/load.js
+++ b/js/load.js
@@ -238,7 +238,7 @@ function init_maps() {
item.has_negstat = false;
item.reqs = [0, 0, 0, 0, 0];
item.fixID = true;
- item.tier = "Normal";//do not get rid of this @hpp
+ item.tier = "Normal";
item.id = 10000 + i;
item.nDam = "0-0";
item.eDam = "0-0";
diff --git a/js/load_tome.js b/js/load_tome.js
new file mode 100644
index 0000000..afd9315
--- /dev/null
+++ b/js/load_tome.js
@@ -0,0 +1,184 @@
+const TOME_DB_VERSION = 1;
+// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
+
+let tdb;
+let treload = false;
+let tload_complete = false;
+let tload_in_progress = false;
+let tomes;
+let tomeMap;
+let tometomeIDMap;
+let tomeRedirectMap;
+let tomeLists = new Map();
+/*
+ * Load tome set from local DB. Calls init() on success.
+ */
+async function load_tome_local(init_func) {
+ let get_tx = tdb.transaction(['tome_db'], 'readonly');
+ let get_store = get_tx.objectStore('tome_db');
+ let request = get_store.getAll();
+ request.onerror = function(event) {
+ console.log("Could not read local tome db...");
+ }
+ request.onsuccess = function(event) {
+ console.log("Successfully read local tome db.");
+ tomes = request.result;
+
+ init_tome_maps();
+ init_func();
+ tload_complete = true;
+ }
+ await get_tx.complete;
+ tdb.close();
+}
+
+/*
+ * Load tome set from remote DB (json). Calls init() on success.
+ */
+async function load_tome(init_func) {
+
+ let getUrl = window.location;
+ let baseUrl = getUrl.protocol + "//" + getUrl.host + "/";// + getUrl.pathname.split('/')[1];
+ // "Random" string to prevent caching!
+ let url = baseUrl + "/tomes.json?"+new Date();
+ let result = await (await fetch(url)).json();
+ tomes = result.tomes
+
+ let add_tx = tdb.transaction(['tome_db'], 'readwrite');
+ add_tx.onabort = function(e) {
+ console.log(e);
+ console.log("Not enough space...");
+ };
+ let tomes_store = add_tx.objectStore('tome_db');
+ let add_promises = [];
+ for (const tome of tomes) {
+ //dependency on clean_item in load.js
+ clean_item(tome);
+ let req = tomes_store.add(tome, tome.name);
+ req.onerror = function() {
+ console.log("ADD TOME ERROR? " + tome.name);
+ };
+ add_promises.push(req);
+ }
+ Promise.all(add_promises).then((values) => {
+ init_tome_maps();
+ init_func();
+ tload_complete = true;
+ });
+ // DB not closed? idfk man
+}
+
+function load_tome_init(init_func) {
+ if (tload_complete) {
+ console.log("Tome db already loaded, skipping load sequence");
+ init_func();
+ return;
+ }
+ let request = window.indexedDB.open('tome_db', TOME_DB_VERSION);
+
+ request.onerror = function() {
+ console.log("DB failed to open...");
+ };
+
+ request.onsuccess = function() {
+ (async function() {
+ tdb = request.result;
+ if (!treload) {
+ console.log("Using stored data...")
+ load_tome_local(init_func);
+ }
+ else {
+ if (tload_in_progress) {
+ while (!tload_complete) {
+ await sleep(100);
+ }
+ console.log("Skipping load...")
+ init_func();
+ }
+ else {
+ // Not 100% safe... whatever!
+ tload_in_progress = true
+ console.log("Using new data...")
+ load_tome(init_func);
+ }
+ }
+ })()
+ }
+
+ request.onupgradeneeded = function(e) {
+ treload = true;
+
+ let tdb = e.target.result;
+
+ try {
+ tdb.deleteObjectStore('tome_db');
+ }
+ catch (error) {
+ console.log("Could not delete tome DB. This is probably fine");
+ }
+
+ tdb.createObjectStore('tome_db');
+
+ console.log("DB setup complete...");
+ }
+}
+
+function init_tome_maps() {
+ //warp
+ tomeMap = new Map();
+ /* Mapping from item names to set names. */
+ tomeIDMap = new Map();
+
+ tomeRedirectMap = new Map();
+ for (const it of tomeTypes) {
+ tomeLists.set(it, []);
+ }
+
+ let noneTomes = [
+ ["tome", "weaponTome", "No Weapon Tome"],
+ ["tome", "armorTome", "No Armor Tome"],
+ ["tome", "guildTome", "No Guild Tome"]
+ ];
+ for (let i = 0; i < 3; i++) {
+ let tome = Object();
+ tome.slots = 0;
+ tome.category = noneTomes[i][0];
+ tome.type = noneTomes[i][1];
+ tome.name = noneTomes[i][2];
+ tome.displayName = tome.name;
+ tome.set = null;
+ tome.quest = null;
+ tome.skillpoints = [0, 0, 0, 0, 0];
+ tome.has_negstat = false;
+ tome.reqs = [0, 0, 0, 0, 0];
+ tome.fixID = true;
+ tome.tier = "Normal";
+ tome.id = 61 + i; //special case!
+ tome.nDam = "0-0";
+ tome.eDam = "0-0";
+ tome.tDam = "0-0";
+ tome.wDam = "0-0";
+ tome.fDam = "0-0";
+ tome.aDam = "0-0";
+ //dependency - load.js
+ clean_item(tome);
+
+ noneTomes[i] = tome;
+ }
+ tomes = tomes.concat(noneTomes);
+ for (const tome of tomes) {
+ if (tome.remapID === undefined) {
+ tomeLists.get(tome.type).push(tome.displayName);
+ tomeMap.set(tome.displayName, tome);
+ if (noneTomes.includes(tome)) {
+ tomeIDMap.set(tome.id, "");
+ }
+ else {
+ tomeIDMap.set(tome.id, tome.displayName);
+ }
+ }
+ else {
+ tomeRedirectMap.set(tome.id, tome.remapID);
+ }
+ }
+}
diff --git a/js/skillpoints.js b/js/skillpoints.js
index 8522dea..12c8805 100644
--- a/js/skillpoints.js
+++ b/js/skillpoints.js
@@ -1,7 +1,6 @@
function calculate_skillpoints(equipment, weapon) {
// Calculate equipment equipping order and required skillpoints.
// Return value: [equip_order, best_skillpoints, final_skillpoints, best_total];
-
let fixed = [];
let consider = [];
let noboost = [];
diff --git a/js/sq2bs.js b/js/sq2bs.js
index 3620c2c..50675c8 100644
--- a/js/sq2bs.js
+++ b/js/sq2bs.js
@@ -3,7 +3,7 @@ let armor_keys = ['helmet', 'chestplate', 'leggings', 'boots'];
let skp_keys = ['str', 'dex', 'int', 'def', 'agi'];
let accessory_keys= ['ring1', 'ring2', 'bracelet', 'necklace'];
let powderable_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'weapon'];
-let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'];
+let equipment_keys = ['helmet', 'chestplate', 'leggings', 'boots', 'ring1', 'ring2', 'bracelet', 'necklace', 'weapon'].concat(tome_keys);
let powder_keys = ['e', 't', 'w', 'f', 'a'];
let spell_disp = ['spell0-info', 'spell1-info', 'spell2-info', 'spell3-info'];
@@ -13,13 +13,18 @@ document.addEventListener('DOMContentLoaded', function() {
for (const eq of equipment_keys) {
document.querySelector("#"+eq+"-choice").setAttribute("oninput", "update_field('"+ eq +"'); calcBuildSchedule();");
- document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip'); "); //toggle_plus_minus('" + eq + "-pm');
+ document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm');
}
for (const eq of powderable_keys) {
document.querySelector("#"+eq+"-powder").setAttribute("oninput", "calcBuildSchedule(); update_field('"+ eq +"');");
}
+ for (const eq of tome_keys) {
+ document.querySelector("#" + eq + "-choice").setAttribute("oninput", "update_field('" + eq + "'); 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+"')");
}
@@ -144,6 +149,14 @@ function update_field(field) {
category = item.category;
type = item.type;
}
+ else if (tomeMap.get(item)) {
+ tome = tomeMap.get(item);
+ if (!tome) {return false;}
+ powder_slots = 0;
+ tier = tome.tier;
+ category = tome.category;
+ type = tome.type;
+ }
else {
// item not found
document.querySelector("#"+field+"-choice").classList.add("text-light");
@@ -299,6 +312,9 @@ function reset_powder_specials() {
function init_autocomplete() {
let dropdowns = new Map()
for (const eq of equipment_keys) {
+ if (tome_keys.includes(eq)) {
+ continue;
+ }
// build dropdown
let item_arr = [];
if (eq == 'weapon') {
@@ -375,6 +391,72 @@ function init_autocomplete() {
}
}));
}
+
+ for (const eq of tome_keys) {
+ // build dropdown
+ let tome_arr = [];
+ for (const tome of tomeLists.get(eq.replace(/[0-9]/g, ''))) {
+ let tome_obj = tomeMap.get(tome);
+ if (tome_obj["restrict"] && tome_obj["restrict"] === "DEPRECATED") {
+ continue;
+ }
+ //this should suffice for tomes - jank
+ if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) {
+ continue;
+ }
+ let tome_name = tome;
+ tome_arr.push(tome_name);
+ }
+
+ // create dropdown
+ dropdowns.set(eq, new autoComplete({
+ data: {
+ src: tome_arr
+ },
+ selector: "#"+ eq +"-choice",
+ wrapper: false,
+ resultsList: {
+ maxResults: 1000,
+ 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";
+ list.style.maxHeight = position.height * 2 +"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: (tome, data) => {
+ tome.classList.add(tomeMap.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) {
dropdowns.set(i+"-choice", new autoComplete({
diff --git a/js/sq2build.js b/js/sq2build.js
index 0cbf697..ca1932a 100644
--- a/js/sq2build.js
+++ b/js/sq2build.js
@@ -100,8 +100,12 @@ class Build{
* @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.
+ *
+ * @param {Object[]} tomes: List of tomes.
+ * In order: 2x Weapon Mastery Tome, 4x Armor Mastery Tome, 1x Guild Tome.
+ * 2x Slaying Mastery Tome, 2x Dungeoneering Mastery Tome, 2x Gathering Mastery Tome are in game, but do not have "useful" stats (those that affect damage calculations or building)
*/
- constructor(level,equipment, powders, externalStats, inputerrors=[]){
+ constructor(level,equipment, powders, externalStats, inputerrors=[], tomes){
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).
@@ -326,6 +330,142 @@ class Build{
errors.push(new ItemNotFound(equipment[8], "weapon", true));
}
}
+
+ //cannot craft tomes
+
+ if(tomeMap.get(tomes[0]) && tomeMap.get(tomes[0]).type === "weaponTome") {
+ const weaponTome1 = tomeMap.get(tomes[0]);
+ this.weaponTome1 = expandItem(weaponTome1, []);
+ } else {
+ try {
+ let weaponTome1 = getCustomFromHash(tomes[0]) ? getCustomFromHash(tomes[0]) : undefined;
+ if (weaponTome1.statMap.get("type") !== "weaponTome1") {
+ throw new Error("Not a Weapon Tome");
+ }
+ if (this.weaponTome1.get("custom")) {
+ this.customItems.push(weaponTome1);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const weaponTome1 = tomeMap.get("No Weapon Tome");
+ this.weaponTome1 = expandItem(weaponTome1, []);
+ errors.push(new ItemNotFound(tomes[0], "weaponTome1", true));
+ }
+ }
+ if(tomeMap.get(tomes[1]) && tomeMap.get(tomes[1]).type === "weaponTome") {
+ const weaponTome2 = tomeMap.get(tomes[1]);
+ this.weaponTome2 = expandItem(weaponTome2, []);
+ } else {
+ try {
+ let weaponTome2 = getCustomFromHash(tomes[1]) ? getCustomFromHash(tomes[1]) : undefined;
+ if (weaponTome2.statMap.get("type") !== "weaponTome2") {
+ throw new Error("Not a Weapon Tome");
+ }
+ if (this.weaponTome2.get("custom")) {
+ this.customItems.push(weaponTome2);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const weaponTome2 = tomeMap.get("No Weapon Tome");
+ this.weaponTome2 = expandItem(weaponTome2, []);
+ errors.push(new ItemNotFound(tomes[1], "weaponTome2", true));
+ }
+ }
+ if(tomeMap.get(tomes[2]) && tomeMap.get(tomes[2]).type === "armorTome") {
+ const armorTome1 = tomeMap.get(tomes[2]);
+ this.armorTome1 = expandItem(armorTome1, []);
+ } else {
+ try {
+ let armorTome1 = getCustomFromHash(tomes[2]) ? getCustomFromHash(tomes[2]) : undefined;
+ if (armorTome1.statMap.get("type") !== "armorTome1") {
+ throw new Error("Not an Armor Tome");
+ }
+ if (this.armorTome1.get("custom")) {
+ this.customItems.push(armorTome1);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const armorTome1 = tomeMap.get("No Armor Tome");
+ this.armorTome1 = expandItem(armorTome1, []);
+ errors.push(new ItemNotFound(tomes[2], "armorTome1", true));
+ }
+ }
+ if(tomeMap.get(tomes[3]) && tomeMap.get(tomes[3]).type === "armorTome") {
+ const armorTome2 = tomeMap.get(tomes[3]);
+ this.armorTome2 = expandItem(armorTome2, []);
+ } else {
+ try {
+ let armorTome2 = getCustomFromHash(tomes[3]) ? getCustomFromHash(tomes[3]) : undefined;
+ if (armorTome2.statMap.get("type") !== "armorTome2") {
+ throw new Error("Not an Armor Tome");
+ }
+ if (this.armorTome2.get("custom")) {
+ this.customItems.push(armorTome2);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const armorTome2 = tomeMap.get("No Armor Tome");
+ this.armorTome2 = expandItem(armorTome2, []);
+ errors.push(new ItemNotFound(tomes[3], "armorTome2", true));
+ }
+ }
+ if(tomeMap.get(tomes[4]) && tomeMap.get(tomes[4]).type === "armorTome") {
+ const armorTome3 = tomeMap.get(tomes[4]);
+ this.armorTome3 = expandItem(armorTome3, []);
+ } else {
+ try {
+ let armorTome3 = getCustomFromHash(tomes[4]) ? getCustomFromHash(tomes[4]) : undefined;
+ if (armorTome3.statMap.get("type") !== "armorTome3") {
+ throw new Error("Not an Armor Tome");
+ }
+ if (this.armorTome3.get("custom")) {
+ this.customItems.push(armorTome3);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const armorTome3 = tomeMap.get("No Armor Tome");
+ this.armorTome3 = expandItem(armorTome3, []);
+ errors.push(new ItemNotFound(tomes[4], "armorTome3", true));
+ }
+ }
+ if(tomeMap.get(tomes[5]) && tomeMap.get(tomes[5]).type === "armorTome") {
+ const armorTome4 = tomeMap.get(tomes[5]);
+ this.armorTome4 = expandItem(armorTome4, []);
+ } else {
+ try {
+ let armorTome4 = getCustomFromHash(tomes[5]) ? getCustomFromHash(tomes[5]) : undefined;
+ if (armorTome4.statMap.get("type") !== "armorTome4") {
+ throw new Error("Not an Armor Tome");
+ }
+ if (this.armorTome4.get("custom")) {
+ this.customItems.push(armorTome4);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const armorTome4 = tomeMap.get("No Armor Tome");
+ this.armorTome4 = expandItem(armorTome4, []);
+ errors.push(new ItemNotFound(tomes[5], "armorTome4", true));
+ }
+ }
+ if(tomeMap.get(tomes[6]) && tomeMap.get(tomes[6]).type === "guildTome") {
+ const guildTome1 = tomeMap.get(tomes[6]);
+ this.guildTome1 = expandItem(guildTome1, []);
+ } else {
+ try {
+ let guildTome1 = getCustomFromHash(tomes[6]) ? getCustomFromHash(tomes[6]) : undefined;
+ if (guildTome1.statMap.get("type") !== "guildTome1") {
+ throw new Error("Not an Guild Tome");
+ }
+ if (this.guildTome1.get("custom")) {
+ this.customItems.push(guildTome1);
+ } //can't craft tomes
+
+ } catch (Error) {
+ const guildTome1 = tomeMap.get("No Guild Tome");
+ this.guildTome1 = expandItem(guildTome1, []);
+ errors.push(new ItemNotFound(tomes[6], "guildTome1", true));
+ }
+ }
//console.log(this.craftedItems)
@@ -345,9 +485,10 @@ class Build{
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]);
+ this.tomes = [this.weaponTome1, this.weaponTome2, this.armorTome1, this.armorTome2, this.armorTome3, this.armorTome4, this.guildTome1];
+ this.items = this.equipment.concat([this.weapon]).concat(this.tomes);
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
- let result = calculate_skillpoints(this.equipment, this.weapon);
+ let result = calculate_skillpoints(this.equipment.concat(this.tomes), this.weapon);
console.log(result);
this.equip_order = result[0];
// How many skillpoints the player had to assign (5 number)
@@ -378,7 +519,7 @@ class Build{
/*Returns build in string format
*/
toString(){
- return [this.equipment,this.weapon].flat();
+ return [this.equipment,this.weapon,this.tomes].flat();
}
/* Getters */
diff --git a/js/sq2builder.js b/js/sq2builder.js
index f1f0902..c3791b9 100644
--- a/js/sq2builder.js
+++ b/js/sq2builder.js
@@ -61,6 +61,15 @@ let equipment_fields = [
"necklace",
"weapon"
];
+let tome_fields = [
+ "weaponTome1",
+ "weaponTome2",
+ "armorTome1",
+ "armorTome2",
+ "armorTome3",
+ "armorTome4",
+ "guildTome1",
+]
let equipment_names = [
"Helmet",
"Chestplate",
@@ -72,8 +81,19 @@ let equipment_names = [
"Necklace",
"Weapon"
];
+
+let tome_names = [
+ "Weapon Tome",
+ "Weapon Tome",
+ "Armor Tome",
+ "Armor Tome",
+ "Armor Tome",
+ "Armor Tome",
+ "Guild Tome",
+]
let equipmentInputs = equipment_fields.map(x => x + "-choice");
-let buildFields = equipment_fields.map(x => x+"-tooltip");
+let buildFields = equipment_fields.map(x => x+"-tooltip").concat(tome_fields.map(x => x + "-tooltip"));
+let tomeInputs = tome_fields.map(x => x + "-choice");
let powderInputs = [
"helmet-powder",
@@ -83,15 +103,6 @@ let powderInputs = [
"weapon-powder",
];
-function init() {
- console.log("builder.js init");
- init_autocomplete();
- decodeBuild(url_tag);
- for (const i of equipment_keys) {
- update_field(i);
- }
-}
-
function getItemNameFromID(id) {
if (redirectMap.has(id)) {
return getItemNameFromID(redirectMap.get(id));
@@ -99,13 +110,20 @@ function getItemNameFromID(id) {
return idMap.get(id);
}
+function getTomeNameFromID(id) {
+ if (tomeRedirectMap.has(id)) {
+ return getTomeNameFromID(tomeRedirectMap.get(id));
+ }
+ return tomeIDMap.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");
+ // 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);
@@ -127,13 +145,17 @@ function parsePowdering(powder_info) {
*/
function decodeBuild(url_tag) {
if (url_tag) {
+ //default values
let equipment = [null, null, null, null, null, null, null, null, null];
+ let tomes = [null, null, null, null, null, null, null];
let powdering = ["", "", "", "", ""];
let info = url_tag.split("_");
let version = info[0];
let save_skp = false;
let skillpoints = [0, 0, 0, 0, 0];
let level = 106;
+
+ //equipment (items)
if (version === "0" || version === "1" || version === "2" || version === "3") {
let equipments = info[1];
for (let i = 0; i < 9; ++i ) {
@@ -177,6 +199,42 @@ function decodeBuild(url_tag) {
}
info[1] = info_str.slice(start_idx);
}
+ if (version === "6") {
+ 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;
+ }
+ }
+ //tomes!
+ for (let i = 0; i < 7; ++i) {
+ let tome_str = info_str.charAt(start_idx);
+ tomes[i] = getTomeNameFromID(Base64.toInt(tome_str));
+ start_idx += 1;
+ }
+ info[1] = info_str.slice(start_idx);
+
+ //tome values do not appear in anything before v6.
+ for (let i in tomes) {
+ setValue(tomeInputs[i], tomes[i]);
+ }
+ }
+ //constant in all versions
+ for (let i in equipment) {
+ setValue(equipmentInputs[i], equipment[i]);
+ }
+
+ //level, skill point assignments, and powdering
if (version === "1") {
let powder_info = info[1];
powdering = parsePowdering(powder_info);
@@ -189,7 +247,7 @@ function decodeBuild(url_tag) {
let powder_info = info[1].slice(10);
powdering = parsePowdering(powder_info);
- } else if (version === "3" || version === "4" || version === "5"){
+ } else if (version === "3" || version === "4" || version === "5" || version === "6"){
level = Base64.toInt(info[1].slice(10,12));
setValue("level-choice",level);
save_skp = true;
@@ -202,13 +260,10 @@ function decodeBuild(url_tag) {
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);
}
}
@@ -219,8 +274,11 @@ function encodeBuild() {
if (player_build) {
let build_string;
- if (player_build.customItems.length > 0) { //v5 encoding
- build_string = "5_";
+
+ //V6 encoding - Tomes
+ if (player_build.items.length == 16 && player_build.tomes) {
+ build_string = "6_";
+
let crafted_idx = 0;
let custom_idx = 0;
for (const item of player_build.items) {
@@ -232,37 +290,8 @@ function encodeBuild() {
} 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 if (item.get("category") === "tome") {
+ build_string += Base64.fromIntN(item.get("id"), 1);
} else {
build_string += Base64.fromIntN(item.get("id"), 3);
}
@@ -288,8 +317,89 @@ function encodeBuild() {
}
}
}
+
return build_string;
}
+ /* For reference in development - V5 and V4 encoding schemes */
+ // //V5 encoding - Custom Items
+ // if (player_build.customItems.length > 0) {
+ // build_string = "5_";
+ // let crafted_idx = 0;
+ // let custom_idx = 0;
+ // for (const item of player_build.items) {
+ // //skip tomes (do we skip them?)
+ // if (item.category === "tome") {
+ // continue;
+ // }
+
+ // 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) {
+ // //skip tomes for now
+ // if (item.get("category") === "tome") {
+ // continue;
+ // }
+
+ // 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);
+ // }
+ // }
+ // }
// 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) +
@@ -306,12 +416,13 @@ function encodeBuild() {
function calculateBuild(save_skp, skp){
try {
resetEditableIDs();
- if(player_build){
+ if (player_build) {
reset_powder_specials();
updateBoosts("skip", false);
updatePowderSpecials("skip", false);
}
let weaponName = getValue(equipmentInputs[8]);
+ //bruh @hpp
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) {
@@ -362,11 +473,24 @@ function calculateBuild(save_skp, skp){
//console.log("POWDERING: " + powdering);
powderings.push(powdering);
}
+ let tomes = [ null, null, null, null, null, null, null];
+ for (let i in tomes) {
+ let equip = getValue(tomeInputs[i]).trim();
+ if (equip === "") {
+ equip = "No " + tome_names[i]
+ }
+ else {
+ setValue(tomeInputs[i], equip);
+ }
+ tomes[i] = equip;
+ }
let level = document.getElementById("level-choice").value;
- player_build = new Build(level, equipment, powderings, new Map(), errors);
+ player_build = new Build(level, equipment, powderings, new Map(), errors, tomes);
console.log(player_build);
+
+ //isn't this deprecated?
for (let i of document.getElementsByClassName("hide-container-block")) {
i.style.display = "block";
}
@@ -1094,7 +1218,20 @@ function optimizeStrDex() {
}
// TODO: Learn and use await
+function init() {
+ console.log("builder.js init");
+ init_autocomplete();
+ decodeBuild(url_tag);
+ for (const i of equipment_keys) {
+ update_field(i);
+ }
+}
function init2() {
load_ing_init(init);
}
-load_init(init2);
+function init3() {
+ load_tome_init(init2)
+}
+
+
+load_init(init3);
diff --git a/js/sq2display.js b/js/sq2display.js
index 2252710..b6b588b 100644
--- a/js/sq2display.js
+++ b/js/sq2display.js
@@ -465,7 +465,12 @@ function displaysq2ExpandedItem(item, parent_id){
let item_desc_elem = document.createElement("div");
item_desc_elem.classList.add("col");
item_desc_elem.classList.add(item.get("tier"));
- item_desc_elem.textContent = item.get("tier")+" "+item.get("type");
+ if (tome_types.includes(item.get("type"))) {
+ 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"));
+ } else {
+ item_desc_elem.textContent = item.get("tier")+" "+item.get("type");
+ }
parent_div.append(item_desc_elem);
}
diff --git a/js/sq2display_constants.js b/js/sq2display_constants.js
index 5152c30..6f352fd 100644
--- a/js/sq2display_constants.js
+++ b/js/sq2display_constants.js
@@ -61,9 +61,15 @@ let item_types = [
"Bow",
"Potion",
"Scroll",
- "Food"
+ "Food",
+ "Weapon Tome",
+ "Armor Tome",
+ "Guild Tome"
]
+let tome_types = ['weaponTome', 'armorTome', 'guildTome'];
+let tome_keys = ['weaponTome1', 'weaponTome2', 'armorTome1', 'armorTome2', 'armorTome3', 'armorTome4', 'guildTome1'];
+
/*
* Display commands
*/
diff --git a/media/items/new/generic-armorTome.png b/media/items/new/generic-armorTome.png
new file mode 100644
index 0000000..c46428d
Binary files /dev/null and b/media/items/new/generic-armorTome.png differ
diff --git a/media/items/new/generic-guildTome.png b/media/items/new/generic-guildTome.png
new file mode 100644
index 0000000..c46428d
Binary files /dev/null and b/media/items/new/generic-guildTome.png differ
diff --git a/media/items/new/generic-weaponTome.png b/media/items/new/generic-weaponTome.png
new file mode 100644
index 0000000..c46428d
Binary files /dev/null and b/media/items/new/generic-weaponTome.png differ
diff --git a/media/items/old/generic-armorTome.png b/media/items/old/generic-armorTome.png
new file mode 100644
index 0000000..c46428d
Binary files /dev/null and b/media/items/old/generic-armorTome.png differ
diff --git a/media/items/old/generic-guildTome.png b/media/items/old/generic-guildTome.png
new file mode 100644
index 0000000..c46428d
Binary files /dev/null and b/media/items/old/generic-guildTome.png differ
diff --git a/media/items/old/generic-weaponTome.png b/media/items/old/generic-weaponTome.png
new file mode 100644
index 0000000..c46428d
Binary files /dev/null and b/media/items/old/generic-weaponTome.png differ
diff --git a/py_script/update_tomes_in_items.py b/py_script/update_tomes_in_items.py
index efb3125..bc177bb 100644
--- a/py_script/update_tomes_in_items.py
+++ b/py_script/update_tomes_in_items.py
@@ -13,15 +13,15 @@ tome_mapping = dict()
max_id = 0
for tome in tome_data:
- if "tomeID" in tome:
- if tome["tomeID"] > max_id:
- max_id = tome["tomeID"]
- tome_mapping[tome["name"]] = tome["tomeID"]
+ if "id" in tome:
+ if tome["id"] > max_id:
+ max_id = tome["id"]
+ tome_mapping[tome["name"]] = tome["id"]
i = max_id + 1
for tome in tome_data:
- if "tomeID" not in tome:
- tome["tomeID"] = i
+ if "id" not in tome:
+ tome["id"] = i
tome_mapping[tome["name"]] = i
i += 1
diff --git a/tomes.json b/tomes.json
index e58b0a0..913efa4 100644
--- a/tomes.json
+++ b/tomes.json
@@ -1,732 +1,735 @@
-[
- {
- "name": "Retaliating Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "thorns": 6,
- "ref": 6,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 0
- },
- {
- "name": "Retaliating Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "thorns": 8,
- "ref": 8,
- "fixID": false,
- "tomeID": 1
- },
- {
- "name": "Destructive Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "exploding": 5,
- "mdPct": 5,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 2
- },
- {
- "name": "Destructive Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "thorns": 6,
- "reflection": 6,
- "fixID": false,
- "tomeID": 3
- },
- {
- "name": "Sorcerer's Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "sdPct": 5,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 4
- },
- {
- "name": "Sorcerer's Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "sdPct": 6,
- "fixID": false,
- "tomeID": 5
- },
- {
- "name": "Everlasting Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "hprRaw": 15,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 6
- },
- {
- "name": "Everlasting Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "hprRaw": 60,
- "fixID": false,
- "tomeID": 7
- },
- {
- "name": "Vampiric Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "ls": 25,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 8
- },
- {
- "name": "Vampiric Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "ls": 85,
- "fixID": false,
- "tomeID": 9
- },
- {
- "name": "Greedy Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "lb": 5,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 10
- },
- {
- "name": "Greedy Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "lb": 6,
- "fixID": false,
- "tomeID": 11
- },
- {
- "name": "Weightless Tome of Armour Mastery I",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "spd": 5,
- "hpBonus": 120,
- "fixID": false,
- "tomeID": 12
- },
- {
- "name": "Weightless Tome of Armour Mastery II",
- "tier": "Fabled",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "spd": 6,
- "fixID": false,
- "tomeID": 13
- },
- {
- "name": "Blooming Tome of Armour Mastery II",
- "tier": "Mythic",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "eDefPct": 10,
- "hpBonus": 150,
- "fixID": false,
- "tomeID": 14
- },
- {
- "name": "Pulsing Tome of Armour Mastery II",
- "tier": "Mythic",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "tDefPct": 10,
- "hpBonus": 150,
- "fixID": false,
- "tomeID": 15
- },
- {
- "name": "Oceanic Tome of Armour Mastery II",
- "tier": "Mythic",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "wDefPct": 10,
- "hpBonus": 150,
- "fixID": false,
- "tomeID": 16
- },
- {
- "name": "Courageous Tome of Armour Mastery II",
- "tier": "Mythic",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "fDefPct": 10,
- "hpBonus": 150,
- "fixID": false,
- "tomeID": 17
- },
- {
- "name": "Clouded Tome of Armour Mastery II",
- "tier": "Mythic",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "aDefPct": 10,
- "hpBonus": 150,
- "fixID": false,
- "tomeID": 18
- },
- {
- "name": "Radiant Tome of Armour Mastery II",
- "tier": "Mythic",
- "type": "armorTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 100,
- "eDefPct": 6,
- "tDefPct": 6,
- "wDefPct": 6,
- "fDefPct": 6,
- "aDefPct": 6,
- "hpBonus": 150,
- "fixID": false,
- "tomeID": 19
- },
- {
- "name": "Tome of Weapon Mastery I",
- "tier": "Legendary",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 60,
- "dmgMobs": 6,
- "fixID": false,
- "tomeID": 20
- },
- {
- "name": "Earthbound Tome of Weapon Mastery I",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 7,
- "str": 3,
- "fixID": false,
- "tomeID": 21
- },
- {
- "name": "Earthbound Tome of Weapon Mastery II",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 8,
- "str": 3,
- "fixID": false,
- "tomeID": 22
- },
- {
- "name": "Nimble Tome of Weapon Mastery I",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 7,
- "dex": 3,
- "fixID": false,
- "tomeID": 23
- },
- {
- "name": "Nimble Tome of Weapon Mastery II",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 8,
- "dex": 3,
- "fixID": false,
- "tomeID": 24
- },
- {
- "name": "Mystical Tome of Weapon Mastery I",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 7,
- "int": 3,
- "fixID": false,
- "tomeID": 25
- },
- {
- "name": "Mystical Tome of Weapon Mastery II",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 8,
- "int": 3,
- "fixID": false,
- "tomeID": 26
- },
- {
- "name": "Warding Tome of Weapon Mastery I",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 7,
- "def": 3,
- "fixID": false,
- "tomeID": 27
- },
- {
- "name": "Warding Tome of Weapon Mastery II",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 8,
- "def": 3,
- "fixID": false,
- "tomeID": 28
- },
- {
- "name": "Athletic Tome of Weapon Mastery I",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 7,
- "agi": 3,
- "fixID": false,
- "tomeID": 29
- },
- {
- "name": "Athletic Tome of Weapon Mastery II",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 8,
- "agi": 3,
- "fixID": false,
- "tomeID": 30
- },
- {
- "name": "Cosmic Tome of Weapon Mastery I",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 7,
- "str": 1,
- "dex": 1,
- "int": 1,
- "def": 1,
- "agi": 1,
- "fixID": false,
- "tomeID": 31
- },
- {
- "name": "Cosmic Tome of Weapon Mastery II",
- "tier": "Fabled",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 8,
- "str": 1,
- "dex": 1,
- "int": 1,
- "def": 1,
- "agi": 1,
- "fixID": false,
- "tomeID": 32
- },
- {
- "name": "Seismic Tome of Weapon Mastery II",
- "tier": "Mythic",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 12,
- "eDamPct": 7,
- "fixID": false,
- "tomeID": 33
- },
- {
- "name": "Voltaic Tome of Weapon Mastery II",
- "tier": "Mythic",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 12,
- "tDamPct": 7,
- "fixID": false,
- "tomeID": 34
- },
- {
- "name": "Abyssal Tome of Weapon Mastery II",
- "tier": "Mythic",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 12,
- "wDamPct": 7,
- "fixID": false,
- "tomeID": 35
- },
- {
- "name": "Infernal Tome of Weapon Mastery II",
- "tier": "Mythic",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 12,
- "fDamPct": 7,
- "fixID": false,
- "tomeID": 36
- },
- {
- "name": "Cyclonic Tome of Weapon Mastery II",
- "tier": "Mythic",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 12,
- "aDamPct": 7,
- "fixID": false,
- "tomeID": 37
- },
- {
- "name": "Astral Tome of Weapon Mastery II",
- "tier": "Mythic",
- "type": "weaponTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Soulbound",
- "lvl": 80,
- "dmgMobs": 12,
- "eDamPct": 6,
- "tDamPct": 6,
- "wDamPct": 6,
- "fDamPct": 6,
- "aDamPct": 6,
- "fixID": false,
- "tomeID": 38
- },
- {
- "name": "Brute's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "str": 3,
- "eDamPct": 2,
- "fixID": false,
- "tomeID": 39
- },
- {
- "name": "Sadist's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "dex": 3,
- "tDamPct": 2,
- "fixID": false,
- "tomeID": 40
- },
- {
- "name": "Mastermind's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "int": 3,
- "wDamPct": 2,
- "fixID": false,
- "tomeID": 41
- },
- {
- "name": "Arsonist's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "def": 3,
- "fDamPct": 2,
- "fixID": false,
- "tomeID": 42
- },
- {
- "name": "Ghost's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "agi": 3,
- "aDamPct": 2,
- "fixID": false,
- "tomeID": 43
- },
- {
- "name": "Psychopath's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "str": 2,
- "dex": 2,
- "fixID": false,
- "tomeID": 44
- },
- {
- "name": "Loner's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "str": 2,
- "int": 2,
- "fixID": false,
- "tomeID": 45
- },
- {
- "name": "Warlock's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "dex": 2,
- "int": 2,
- "fixID": false,
- "tomeID": 46
- },
- {
- "name": "Destroyer's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "str": 2,
- "def": 2,
- "fixID": false,
- "tomeID": 47
- },
- {
- "name": "Devil's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "dex": 2,
- "def": 2,
- "fixID": false,
- "tomeID": 48
- },
- {
- "name": "Alchemist's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "int": 2,
- "def": 2,
- "fixID": false,
- "tomeID": 49
- },
- {
- "name": "Barbarian's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "str": 2,
- "agi": 2,
- "fixID": false,
- "tomeID": 50
- },
- {
- "name": "Freelancer's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "dex": 2,
- "agi": 2,
- "fixID": false,
- "tomeID": 51
- },
- {
- "name": "Sycophant's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "int": 2,
- "agi": 2,
- "fixID": false,
- "tomeID": 52
- },
- {
- "name": "Fanatic's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "def": 2,
- "agi": 2,
- "fixID": false,
- "tomeID": 53
- },
- {
- "name": "Assimilator's Tome of Allegiance",
- "tier": "Legendary",
- "type": "guildTome",
- "category": "tome",
- "drop": "never",
- "restrict": "Untradable",
- "lvl": 100,
- "str": 1,
- "dex": 1,
- "int": 1,
- "def": 1,
- "agi": 1,
- "fixID": false,
- "tomeID": 54
- }
-]
\ No newline at end of file
+{
+ "tomes":
+ [
+ {
+ "name": "Retaliating Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "thorns": 6,
+ "ref": 6,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 0
+ },
+ {
+ "name": "Retaliating Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "thorns": 8,
+ "ref": 8,
+ "fixID": false,
+ "id": 1
+ },
+ {
+ "name": "Destructive Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "exploding": 5,
+ "mdPct": 5,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 2
+ },
+ {
+ "name": "Destructive Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "thorns": 6,
+ "reflection": 6,
+ "fixID": false,
+ "id": 3
+ },
+ {
+ "name": "Sorcerer's Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "sdPct": 5,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 4
+ },
+ {
+ "name": "Sorcerer's Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "sdPct": 6,
+ "fixID": false,
+ "id": 5
+ },
+ {
+ "name": "Everlasting Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "hprRaw": 15,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 6
+ },
+ {
+ "name": "Everlasting Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "hprRaw": 60,
+ "fixID": false,
+ "id": 7
+ },
+ {
+ "name": "Vampiric Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "ls": 25,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 8
+ },
+ {
+ "name": "Vampiric Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "ls": 85,
+ "fixID": false,
+ "id": 9
+ },
+ {
+ "name": "Greedy Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "lb": 5,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 10
+ },
+ {
+ "name": "Greedy Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "lb": 6,
+ "fixID": false,
+ "id": 11
+ },
+ {
+ "name": "Weightless Tome of Armour Mastery I",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "spd": 5,
+ "hpBonus": 120,
+ "fixID": false,
+ "id": 12
+ },
+ {
+ "name": "Weightless Tome of Armour Mastery II",
+ "tier": "Fabled",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "spd": 6,
+ "fixID": false,
+ "id": 13
+ },
+ {
+ "name": "Blooming Tome of Armour Mastery II",
+ "tier": "Mythic",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "eDefPct": 10,
+ "hpBonus": 150,
+ "fixID": false,
+ "id": 14
+ },
+ {
+ "name": "Pulsing Tome of Armour Mastery II",
+ "tier": "Mythic",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "tDefPct": 10,
+ "hpBonus": 150,
+ "fixID": false,
+ "id": 15
+ },
+ {
+ "name": "Oceanic Tome of Armour Mastery II",
+ "tier": "Mythic",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "wDefPct": 10,
+ "hpBonus": 150,
+ "fixID": false,
+ "id": 16
+ },
+ {
+ "name": "Courageous Tome of Armour Mastery II",
+ "tier": "Mythic",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "fDefPct": 10,
+ "hpBonus": 150,
+ "fixID": false,
+ "id": 17
+ },
+ {
+ "name": "Clouded Tome of Armour Mastery II",
+ "tier": "Mythic",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "aDefPct": 10,
+ "hpBonus": 150,
+ "fixID": false,
+ "id": 18
+ },
+ {
+ "name": "Radiant Tome of Armour Mastery II",
+ "tier": "Mythic",
+ "type": "armorTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 100,
+ "eDefPct": 6,
+ "tDefPct": 6,
+ "wDefPct": 6,
+ "fDefPct": 6,
+ "aDefPct": 6,
+ "hpBonus": 150,
+ "fixID": false,
+ "id": 19
+ },
+ {
+ "name": "Tome of Weapon Mastery I",
+ "tier": "Legendary",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 60,
+ "dmgMobs": 6,
+ "fixID": false,
+ "id": 20
+ },
+ {
+ "name": "Earthbound Tome of Weapon Mastery I",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 7,
+ "str": 3,
+ "fixID": false,
+ "id": 21
+ },
+ {
+ "name": "Earthbound Tome of Weapon Mastery II",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 8,
+ "str": 3,
+ "fixID": false,
+ "id": 22
+ },
+ {
+ "name": "Nimble Tome of Weapon Mastery I",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 7,
+ "dex": 3,
+ "fixID": false,
+ "id": 23
+ },
+ {
+ "name": "Nimble Tome of Weapon Mastery II",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 8,
+ "dex": 3,
+ "fixID": false,
+ "id": 24
+ },
+ {
+ "name": "Mystical Tome of Weapon Mastery I",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 7,
+ "int": 3,
+ "fixID": false,
+ "id": 25
+ },
+ {
+ "name": "Mystical Tome of Weapon Mastery II",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 8,
+ "int": 3,
+ "fixID": false,
+ "id": 26
+ },
+ {
+ "name": "Warding Tome of Weapon Mastery I",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 7,
+ "def": 3,
+ "fixID": false,
+ "id": 27
+ },
+ {
+ "name": "Warding Tome of Weapon Mastery II",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 8,
+ "def": 3,
+ "fixID": false,
+ "id": 28
+ },
+ {
+ "name": "Athletic Tome of Weapon Mastery I",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 7,
+ "agi": 3,
+ "fixID": false,
+ "id": 29
+ },
+ {
+ "name": "Athletic Tome of Weapon Mastery II",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 8,
+ "agi": 3,
+ "fixID": false,
+ "id": 30
+ },
+ {
+ "name": "Cosmic Tome of Weapon Mastery I",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 7,
+ "str": 1,
+ "dex": 1,
+ "int": 1,
+ "def": 1,
+ "agi": 1,
+ "fixID": false,
+ "id": 31
+ },
+ {
+ "name": "Cosmic Tome of Weapon Mastery II",
+ "tier": "Fabled",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 8,
+ "str": 1,
+ "dex": 1,
+ "int": 1,
+ "def": 1,
+ "agi": 1,
+ "fixID": false,
+ "id": 32
+ },
+ {
+ "name": "Seismic Tome of Weapon Mastery II",
+ "tier": "Mythic",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 12,
+ "eDamPct": 7,
+ "fixID": false,
+ "id": 33
+ },
+ {
+ "name": "Voltaic Tome of Weapon Mastery II",
+ "tier": "Mythic",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 12,
+ "tDamPct": 7,
+ "fixID": false,
+ "id": 34
+ },
+ {
+ "name": "Abyssal Tome of Weapon Mastery II",
+ "tier": "Mythic",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 12,
+ "wDamPct": 7,
+ "fixID": false,
+ "id": 35
+ },
+ {
+ "name": "Infernal Tome of Weapon Mastery II",
+ "tier": "Mythic",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 12,
+ "fDamPct": 7,
+ "fixID": false,
+ "id": 36
+ },
+ {
+ "name": "Cyclonic Tome of Weapon Mastery II",
+ "tier": "Mythic",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 12,
+ "aDamPct": 7,
+ "fixID": false,
+ "id": 37
+ },
+ {
+ "name": "Astral Tome of Weapon Mastery II",
+ "tier": "Mythic",
+ "type": "weaponTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Soulbound Item",
+ "lvl": 80,
+ "dmgMobs": 12,
+ "eDamPct": 6,
+ "tDamPct": 6,
+ "wDamPct": 6,
+ "fDamPct": 6,
+ "aDamPct": 6,
+ "fixID": false,
+ "id": 38
+ },
+ {
+ "name": "Brute's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "str": 3,
+ "eDamPct": 2,
+ "fixID": false,
+ "id": 39
+ },
+ {
+ "name": "Sadist's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "dex": 3,
+ "tDamPct": 2,
+ "fixID": false,
+ "id": 40
+ },
+ {
+ "name": "Mastermind's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "int": 3,
+ "wDamPct": 2,
+ "fixID": false,
+ "id": 41
+ },
+ {
+ "name": "Arsonist's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "def": 3,
+ "fDamPct": 2,
+ "fixID": false,
+ "id": 42
+ },
+ {
+ "name": "Ghost's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "agi": 3,
+ "aDamPct": 2,
+ "fixID": false,
+ "id": 43
+ },
+ {
+ "name": "Psychopath's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "str": 2,
+ "dex": 2,
+ "fixID": false,
+ "id": 44
+ },
+ {
+ "name": "Loner's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "str": 2,
+ "int": 2,
+ "fixID": false,
+ "id": 45
+ },
+ {
+ "name": "Warlock's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "dex": 2,
+ "int": 2,
+ "fixID": false,
+ "id": 46
+ },
+ {
+ "name": "Destroyer's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "str": 2,
+ "def": 2,
+ "fixID": false,
+ "id": 47
+ },
+ {
+ "name": "Devil's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "dex": 2,
+ "def": 2,
+ "fixID": false,
+ "id": 48
+ },
+ {
+ "name": "Alchemist's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "int": 2,
+ "def": 2,
+ "fixID": false,
+ "id": 49
+ },
+ {
+ "name": "Barbarian's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "str": 2,
+ "agi": 2,
+ "fixID": false,
+ "id": 50
+ },
+ {
+ "name": "Freelancer's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "dex": 2,
+ "agi": 2,
+ "fixID": false,
+ "id": 51
+ },
+ {
+ "name": "Sycophant's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "int": 2,
+ "agi": 2,
+ "fixID": false,
+ "id": 52
+ },
+ {
+ "name": "Fanatic's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "def": 2,
+ "agi": 2,
+ "fixID": false,
+ "id": 53
+ },
+ {
+ "name": "Assimilator's Tome of Allegiance",
+ "tier": "Legendary",
+ "type": "guildTome",
+ "category": "tome",
+ "drop": "never",
+ "restrict": "Untradable",
+ "lvl": 100,
+ "str": 1,
+ "dex": 1,
+ "int": 1,
+ "def": 1,
+ "agi": 1,
+ "fixID": false,
+ "id": 54
+ }
+ ]
+}
diff --git a/wynnfo/scripts/main.js b/wynnfo/scripts/main.js
index 8df2dda..c5632af 100644
--- a/wynnfo/scripts/main.js
+++ b/wynnfo/scripts/main.js
@@ -14,6 +14,12 @@ const pdfs = new Map([
const changelog = new Map([
+ ["Build Version 6 (20 May 2022)",
+ [
+ " + Added Tomes",
+ " + Changed Build encode and decode schemes to account for tomes",
+ ]
+ ],
["WynnBuilder^2 (12 May 2022)",
[
" + Switched most of Wynnbuilder over to Bootstrap",