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
</div>
<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')">
War Scream
</button>

View file

@ -198,6 +198,60 @@ input.equipment-input:not(.is-invalid) {
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) {
:root {
--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:
@ -74,6 +79,7 @@ stat_bonus: {
stat_scaling: {
"type": "stat_scaling",
"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_step": Optional[float],
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;
case 'stat_scaling':
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) {
const slider_val = slider_map.get(effect.slider_name).slider.value;
total = parseInt(slider_val) * scaling[0];
round = false;
positive = false;
}
else {
// 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 (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 (Array.isArray(effect.output)) {
for (const output of effect.output) {

View file

@ -5242,7 +5242,9 @@ const atrees = {
"Teleport"
],
"dependencies": [],
"blockers": [],
"blockers": [
"Wisdom"
],
"cost": 1,
"display": {
"row": 6,
@ -5387,20 +5389,6 @@ const atrees = {
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"
],
"dependencies": [],
"blockers": [],
"blockers": [
"Sunshower",
"Larger Heal",
"Orphion's Pulse"
],
"cost": 2,
"display": {
"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]]);
@ -10,15 +15,9 @@ class Build{
/**
* @description Construct a build.
* @param {Number} level : Level of the player.
* @param {String[]} equipment : List of equipment names that make up the build.
* In order: boots, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Weapon.
* @param {Number[]} powders : Powder application. List of lists of integers (powder IDs).
* In order: boots, Chestplate, Leggings, Boots, Weapon.
* @param {Object[]} inputerrors : List of instances of error-like classes.
*
* @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)
* @param {String[]} items: List of equipment names that make up the build.
* In order: Helmet, Chestplate, Leggings, Boots, Ring1, Ring2, Brace, Neck, Tomes [x7].
* @param {Item} weapon: Weapon that this build is using.
*/
constructor(level, items, weapon){
@ -39,11 +38,16 @@ class Build{
this.equipment = items;
this.weapon = weapon;
this.items = this.equipment.concat([this.weapon]);
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
// calc skillpoints requires statmaps only
let result = calculate_skillpoints(this.equipment.map((x) => x.statMap), this.weapon.statMap);
this.equip_order = result[0];
this.equip_order = result[0].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)
this.base_skillpoints = result[1];
// How many skillpoints the build ended up with (5 number)
@ -61,7 +65,6 @@ class Build{
return [this.equipment,this.weapon].flat();
}
/* 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.
*/

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.
* @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() {
const buildList = document.getElementById("build-choice");
@ -60,6 +63,9 @@ function resetFields(){
for (const i of equipment_inputs) {
setValue(i, "");
}
for (const i of tomeInputs) {
setValue(i, "");
}
setValue("str-skp", "0");
setValue("dex-skp", "0");
setValue("int-skp", "0");
@ -231,8 +237,9 @@ 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);
let tome_aliases = new Map();
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") {
continue;
}
@ -240,8 +247,10 @@ function init_autocomplete() {
if (tome_obj["name"].includes('No ' + eq.charAt(0).toUpperCase())) {
continue;
}
let tome_name = tome;
let tome_alias = tome_obj['alias'];
tome_arr.push(tome_name);
tome_arr.push(tome_alias);
tome_aliases.set(tome_alias, tome_name);
}
// create dropdown
@ -276,14 +285,18 @@ function init_autocomplete() {
class: "scaled-font search-item",
selected: "dark-5",
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: {
input: {
selection: (event) => {
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'));
},

View file

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

View file

@ -31,16 +31,6 @@ let player_craft;
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 {
document.getElementById("recipe-choice").addEventListener("change", (event) => {
updateMaterials();
@ -56,17 +46,16 @@ function init_crafter() {
});
for (let i = 1; i < 4; ++i) {
document.getElementById("mat-1-"+i).setAttribute("onclick", document.getElementById("mat-1-"+i).getAttribute("onclick") + "; calculateCraftSchedule();");
document.getElementById("mat-2-"+i).setAttribute("onclick", document.getElementById("mat-2-"+i).getAttribute("onclick") + "; calculateCraftSchedule();");
document.getElementById("mat-1-"+i).addEventListener("click", (e) => calculateCraftSchedule());
document.getElementById("mat-2-"+i).addEventListener("click", (e) => calculateCraftSchedule());
}
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"]) {
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();
decodeCraft(ing_url_tag);
} catch (error) {
@ -259,12 +248,10 @@ function populateFields() {
ing_list.appendChild(el);
}
}
}
/*
Copies the CR Hash (CR-blahblahblah)
*/
* Copies the CR Hash (CR-blahblahblah)
*/
function copyRecipeHash() {
if (player_craft) {
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) {
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",
"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",
@ -388,46 +393,45 @@ function displayExpandedItem(item, parent_id){
}
}
//Show powder specials ;-;
let nonConsumables = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots"];//, "ring", "bracelet", "necklace"];
if(nonConsumables.includes(item.get("type"))) {
let powder_special = document.createElement("div");
powder_special.classList.add("col");
let powder_specials_check = ["relik", "wand", "bow", "spear", "dagger", "chestplate", "helmet", "leggings", "boots"];
if(powder_specials_check.includes(item.get("type"))) {
let powder_special = make_elem("div", ['col']);
let powders = item.get("powders");
let element = "";
let power = 0;
let element;
let power_index;
for (let i = 0; i < powders.length; i++) {
let firstPowderType = skp_elements[Math.floor(powders[i]/6)];
if (element !== "") break;
else if (powders[i]%6 > 2) { //t4+
const firstPowderType = skp_elements[Math.floor(powders[i]/6)];
const powder1_power = powders[i] % 6;
if (powder1_power > 2) { //t4+
for (let j = i+1; j < powders.length; j++) {
let currentPowderType = skp_elements[Math.floor(powders[j]/6)]
if (powders[j] % 6 > 2 && firstPowderType === currentPowderType) {
const currentPowderType = skp_elements[Math.floor(powders[j]/6)]
const powder2_power = powders[j] % 6;
if (powder2_power > 2 && firstPowderType === currentPowderType) {
element = currentPowderType;
power = Math.round(((powders[i] % 6 + powders[j] % 6 + 2) / 2 - 4) * 2);
power_index = powder1_power + powder2_power - 6;
break;
}
}
}
}
if (element !== "") {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]"
let powderSpecial = powderSpecialStats[ skp_elements.indexOf(element)];
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("span");
let specialEffects = document.createElement("span");
addClasses(specialTitle, [damageClasses[skp_elements.indexOf(element) + 1]]);
if (element) {//powder special is "[e,t,w,f,a]+[0,1,2,3,4]"
const powderSpecial = powderSpecialStats[skp_elements.indexOf(element)];
const specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
const specialTitle = make_elem("span", [damageClasses[skp_elements.indexOf(element) + 1]]);
const specialEffects = document.createElement("span");
let effects;
if (item.get("category") === "weapon") {//weapon
effects = powderSpecial["weaponSpecialEffects"];
specialTitle.textContent = powderSpecial["weaponSpecialName"];
}else if (item.get("category") === "armor") {//armor
} else if (item.get("category") === "armor") {//armor
effects = powderSpecial["armorSpecialEffects"];
specialTitle.textContent += powderSpecial["armorSpecialName"] + ": ";
}
for (const [key,value] of effects.entries()) {
if (key !== "Description") {
let effect = document.createElement("p");
effect.classList.add("m-0");
effect.textContent = key + ": " + value[power] + specialSuffixes.get(key);
let effect = make_elem("p", ["m-0"], {
textContent: key + ": " + value[power_index] + specialSuffixes.get(key)
});
if(key === "Damage"){
effect.textContent += elementIcons[skp_elements.indexOf(element)];
}
@ -435,20 +439,19 @@ function displayExpandedItem(item, parent_id){
effect.textContent += " / Mana Used";
}
specialEffects.appendChild(effect);
}else{
} else {
specialTitle.textContent += "[ " + effects.get("Description") + " ]";
}
}
powder_special.appendChild(specialTitle);
powder_special.appendChild(specialEffects);
powder_special.append(specialTitle, specialEffects);
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") {
let dura_elem = document.createElement("div");
dura_elem.classList.add("col");
let dura = [];
let dura_elem = make_elem("div", ["col"]);
let dura;
let suffix = "";
if(nonConsumables.includes(item.get("type"))) {
dura = item.get("durability");
@ -457,9 +460,9 @@ function displayExpandedItem(item, parent_id){
dura = item.get("duration");
dura_elem.textContent = "Duration: "
suffix = " sec."
let charges = document.createElement("b");
charges.textContent = "Charges: " + item.get("charges");
parent_div.appendChild(charges);
parent_div.appendChild(make_elem('b', [], {
textContent: "Charges: " + item.get("charges")
}));
}
if (typeof(dura) === "string") {
@ -472,9 +475,7 @@ function displayExpandedItem(item, parent_id){
}
//Show item tier
if (item.get("tier") && item.get("tier") !== " ") {
let item_desc_elem = document.createElement("div");
item_desc_elem.classList.add("col");
item_desc_elem.classList.add(item.get("tier"));
let item_desc_elem = make_elem("div", ["col", item.get("tier")]);
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"));
@ -486,20 +487,19 @@ function displayExpandedItem(item, parent_id){
//Show item hash if applicable
if (item.get("crafted") || item.get("custom")) {
let item_desc_elem = document.createElement("p");
item_desc_elem.classList.add('itemp');
item_desc_elem.style.maxWidth = "100%";
item_desc_elem.style.wordWrap = "break-word";
item_desc_elem.style.wordBreak = "break-word";
item_desc_elem.textContent = item.get("hash");
parent_div.append(item_desc_elem);
parent_div.append(make_elem('p', ['itemp'], {
style: {
maxWidth: '100%',
wordWrap: 'break-word',
wordBreak: 'break-word'
},
textContent: item.get('hash')
}));
}
if (item.get("category") === "weapon") {
let total_damages = item.get("basedps");
let base_dps_elem = document.createElement("p");
base_dps_elem.classList.add("left");
base_dps_elem.classList.add("itemp");
let base_dps_elem = make_elem("p", ["left", "itemp"]);
if (item.get("tier") === "Crafted") {
let base_dps_min = total_damages[0];
let base_dps_max = total_damages[1];
@ -509,8 +509,7 @@ function displayExpandedItem(item, parent_id){
else {
base_dps_elem.textContent = "Base DPS: "+(total_damages);
}
parent_div.appendChild(document.createElement("p"));
parent_div.appendChild(base_dps_elem);
parent_div.append(make_elem("p"), base_dps_elem);
}
}
@ -1268,8 +1267,8 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
//iterate through the special and display its effects.
let powder_special = make_elem("p", ["pt-3"]);
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = document.createElement("p");
let specialEffects = document.createElement("p");
let specialTitle = make_elem("p");
let specialEffects = make_elem("p");
specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]);
let effects = special[0]["weaponSpecialEffects"];
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}
};
ing.id = 4001 + ing.pid;
ing.diplayName = ing.name;
ing.displayName = ing.name;
switch(i) {
case 0:
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
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)));
}
}
// skip root.
permute_check(1, best_skillpoints, final_skillpoints, best_activeSetCounts, allFalse.slice(), 0, []);
if (sccs.length === 1) {
// 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
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);

View file

@ -612,16 +612,6 @@ function matchType(object, target) {
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.
*
*/
@ -892,6 +882,17 @@ function make_elem(type, classlist = [], args = {}) {
const ret_elem = document.createElement(type);
ret_elem.classList.add(...classlist);
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];
}
return ret_elem;

View file

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