diff --git a/css/sq2bs.css b/css/sq2bs.css
index c574126..f45d056 100644
--- a/css/sq2bs.css
+++ b/css/sq2bs.css
@@ -8,7 +8,7 @@
/* builder containers */
-.e_slider, .t_slider, .w_slider, .f_slider, .a_slider {
+.slider {
-webkit-appearance: none;
background: #AAAAAA;
border-radius: 30px;
@@ -16,15 +16,14 @@
}
/***** Chrome, Safari, Opera, and Edge Chromium *****/
-.e_slider::-webkit-slider-runnable-track, .t_slider::-webkit-slider-runnable-track, .w_slider::-webkit-slider-runnable-track, .f_slider::-webkit-slider-runnable-track, .a_slider::-webkit-slider-runnable-track {
+.slider::-webkit-slider-runnable-track{
-webkit-appeareance: none;
background:transparent;
height: 0.5rem;
}
-
/******** Firefox **** **/
-.e_slider::-moz-range-track, .t_slider::-moz-range-track, .w_slider::-moz-range-track, .f_slider::-moz-range-track, .a_slider::-moz-range-track {
+.slider::-moz-range-track {
-webkit-appearance: none;
background-color: transparent;
border-radius: 30px;
@@ -32,7 +31,7 @@
}
-.e_slider::-webkit-slider-thumb, .t_slider::-webkit-slider-thumb, .w_slider::-webkit-slider-thumb, .f_slider::-webkit-slider-thumb, .a_slider::-webkit-slider-thumb {
+.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
height: 0.75rem;
@@ -504,4 +503,4 @@ a:hover {
}
.ferricles{
color: #5be553;
-}
\ No newline at end of file
+}
diff --git a/js/atree.js b/js/atree.js
index 9c0db51..e5a6b2b 100644
--- a/js/atree.js
+++ b/js/atree.js
@@ -76,6 +76,10 @@ stat_scaling: {
"slider": bool,
"slider_name": Optional[str],
"slider_step": Optional[float],
+ slider_behavior: Optional[str] // One of: "merge", "modify". default: merge
+ // merge: add if exist, make new part if not exist
+ // modify: change existing part. do nothing if not exist
+ slider_max: Optional[float] // affected by slider_behavior
"inputs": Optional[list[scaling_target]],
"output": scaling_target | List[scaling_target],
"scaling": list[float],
@@ -170,6 +174,31 @@ const atree_node = new (class extends ComputeNode {
}
})();
+/**
+ * Create a reverse topological sort of the tree in the result list.
+ * NOTE: our structure isn't a tree... it isn't even acyclic... but do it anyway i guess...
+ *
+ * https://en.wikipedia.org/wiki/Topological_sorting
+ * @param tree: Root of tree to sort
+ * @param res: Result list (reverse topological order)
+ * @param mark_state: Bookkeeping. Call with empty Map()
+ */
+function topological_sort_tree(tree, res, mark_state) {
+ const state = mark_state.get(tree);
+ if (state === undefined) {
+ // unmarked.
+ mark_state.set(tree, false); // temporary mark
+ for (const child of tree.children) {
+ topological_sort_tree(child, res, mark_state);
+ }
+ mark_state.set(tree, true); // permanent mark
+ res.push(tree);
+ }
+ // these cases are not needed. Case 1 does nothing, case 2 should never happen.
+ // else if (state === true) { return; } // permanent mark.
+ // else if (state === false) { throw "not a DAG"; } // temporary mark.
+}
+
/**
* Display ability tree from topologically sorted list.
*
@@ -212,30 +241,6 @@ const atree_state_node = new (class extends ComputeNode {
}
})().link_to(atree_render, 'atree-render');
-/**
- * Create a reverse topological sort of the tree in the result list.
- *
- * https://en.wikipedia.org/wiki/Topological_sorting
- * @param tree: Root of tree to sort
- * @param res: Result list (reverse topological order)
- * @param mark_state: Bookkeeping. Call with empty Map()
- */
-function topological_sort_tree(tree, res, mark_state) {
- const state = mark_state.get(tree);
- if (state === undefined) {
- // unmarked.
- mark_state.set(tree, false); // temporary mark
- for (const child of tree.children) {
- topological_sort_tree(child, res, mark_state);
- }
- mark_state.set(tree, true); // permanent mark
- res.push(tree);
- }
- // these cases are not needed. Case 1 does nothing, case 2 should never happen.
- // else if (state === true) { return; } // permanent mark.
- // else if (state === false) { throw "not a DAG"; } // temporary mark.
-}
-
/**
* Collect abilities and condense them into a list of "final abils".
* This is just for rendering purposes, and for collecting things that modify spells into one chunk.
@@ -269,18 +274,21 @@ const atree_merge = new (class extends ComputeNode {
}
const abil = node.ability;
- if (abils_merged.has(abil.base_abil)) {
- // Merge abilities.
- // TODO: What if there is more than one base abil?
- let base_abil = abils_merged.get(abil.base_abil);
- if (Array.isArray(abil.desc)) { base_abil.desc = base_abil.desc.concat(abil.desc); }
- else { base_abil.desc.push(abil.desc); }
+ if ('base_abil' in abil) {
+ if (abils_merged.has(abil.base_abil)) {
+ // Merge abilities.
+ // TODO: What if there is more than one base abil?
+ let base_abil = abils_merged.get(abil.base_abil);
+ if (Array.isArray(abil.desc)) { base_abil.desc = base_abil.desc.concat(abil.desc); }
+ else { base_abil.desc.push(abil.desc); }
- base_abil.subparts.push(abil.id);
- base_abil.effects = base_abil.effects.concat(abil.effects);
- for (let propname in abil.properties) {
- base_abil[propname] = abil[propname];
+ base_abil.subparts.push(abil.id);
+ base_abil.effects = base_abil.effects.concat(abil.effects);
+ for (let propname in abil.properties) {
+ base_abil[propname] = abil[propname];
+ }
}
+ // do nothing otherwise.
}
else {
let tmp_abil = deepcopy(abil);
@@ -381,6 +389,12 @@ function atree_dfs_mark(start, atree_state, mark) {
}
}
+/**
+ * Render ability tree.
+ * Return map of id -> corresponding html element.
+ *
+ * Signature: AbilityTreeRenderActiveNode(atree-merged: MergedATree, atree-order: ATree, atree-errors: List[str]) => Map[int, ATreeNode]
+ */
const atree_render_active = new (class extends ComputeNode {
constructor() {
super('atree-render-active');
@@ -413,6 +427,7 @@ const atree_render_active = new (class extends ComputeNode {
errorbox.appendChild(atree_warning);
}
}
+ const ret_map = new Map();
for (const node of atree_order) {
if (!merged_abils.has(node.ability.id)) {
continue;
@@ -433,9 +448,11 @@ const atree_render_active = new (class extends ComputeNode {
active_tooltip_desc.textContent = desc;
active_tooltip.appendChild(active_tooltip_desc);
}
+ ret_map.set(abil.id, active_tooltip);
this.list_elem.appendChild(active_tooltip);
}
+ return ret_map;
}
})().link_to(atree_node, 'atree-order').link_to(atree_merge, 'atree-merged').link_to(atree_validate, 'atree-errors');
@@ -546,12 +563,84 @@ const atree_collect_spells = new (class extends ComputeNode {
}
})().link_to(atree_merge, 'atree-merged').link_to(atree_validate, 'atree-errors');
+
+/**
+ * Make interactive elements (sliders, buttons)
+ *
+ * Signature: AbilityActiveUINode(atree-merged: MergedATree) => Map
+ *
+ * ElemState: {
+ * value: int // value for sliders; 0-1 for toggles
+ * }
+ */
+const atree_make_interactives = new (class extends ComputeNode {
+ constructor() { super('atree-make-interactives'); }
+
+ compute_func(input_map) {
+ const merged_abils = input_map.get('atree-merged');
+ const atree_order = input_map.get('atree-order');
+ const atree_html = input_map.get('atree-elements');
+
+ /**
+ * slider_info
+ * label_name: str,
+ * max: int,
+ * step: int,
+ * id: str,
+ * abil: atree_node
+ * slider: html element
+ * }
+ */
+ // Map
+ const slider_map = new Map();
+
+ // first, pull out all the sliders.
+ for (const [abil_id, ability] of merged_abils.entries()) {
+ for (const effect of ability.effects) {
+ if (effect['type'] === "stat_scaling" && effect['slider'] === true) {
+ const { slider_name, slider_behavior = 'merge', slider_max, slider_step } = effect;
+ if (slider_map.has(slider_name)) {
+ const slider_info = slider_map.get(slider_name);
+ slider_info.max += slider_max;
+ }
+ else if (slider_behavior === 'merge') {
+ slider_map.set(slider_name, {
+ label_name: slider_name,
+ max: slider_max,
+ step: slider_step,
+ id: "ability-slider"+ability.id,
+ //color: effect['slider_color'] TODO: add colors to json
+ abil: ability
+ });
+ }
+ }
+ }
+ }
+ // next, render the sliders onto the abilities.
+ for (const [slider_name, slider_info] of slider_map.entries()) {
+ let slider_container = gen_slider_labeled(slider_info);
+ atree_html.get(slider_info.abil.id).appendChild(slider_container);
+ slider_info.slider = document.getElementById(slider_info.id);
+ slider_info.slider.addEventListener("change", (e) => atree_stats.mark_dirty().update());
+ }
+ return slider_map;
+ }
+})().link_to(atree_node, 'atree-order').link_to(atree_merge, 'atree-merged').link_to(atree_render_active, 'atree-elements');
+
+
+/**
+ * Collect stats from ability tree.
+ * Return StatMap of added stats (incl. cost modifications as raw cost)
+ *
+ * Signature: AbilityTreeStatsNode(atree-merged: MergedATree, build: Build, atree-interactive: Map) => StatMap
+ */
const atree_stats = new (class extends ComputeNode {
constructor() { super('atree-stats-collector'); }
compute_func(input_map) {
- if (input_map.size !== 1) { throw "AbilityTreeCollectStats accepts exactly one input (atree-merged)"; }
- const [atree_merged] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
+ const atree_merged = input_map.get('atree-merged');
+ const item_stats = input_map.get('build').statMap;
+ const interactive_map = input_map.get('atree-interactive');
let ret_effects = new Map();
for (const [abil_id, abil] of atree_merged.entries()) {
@@ -560,7 +649,45 @@ const atree_stats = new (class extends ComputeNode {
for (const effect of abil.effects) {
switch (effect.type) {
case 'stat_scaling':
- // TODO: handle
+ if (effect.slider) {
+ // TODO: handle
+ const slider_val = interactive_map.get(effect.slider_name).slider.value;
+ const total = parseInt(slider_val) * effect.scaling[0];
+ if (Array.isArray(effect.output)) {
+ for (const output of effect.output) {
+ if (output.type === 'stat') {
+ merge_stat(ret_effects, output.name, total);
+ }
+ }
+ }
+ else {
+ if (effect.output.type === 'stat') {
+ merge_stat(ret_effects, effect.output.name, total);
+ }
+ }
+ }
+ else {
+ const cap = effect.max;
+ // TODO: type: prop?
+ let total = 0;
+ for (const [scaling, input] of zip2(effect.scaling, effect.inputs)) {
+ total += scaling * item_stats.get(input.name);
+ }
+ if (total > cap) { total = cap; }
+ // TODO: output (list...)
+ if (Array.isArray(effect.output)) {
+ for (const output of effect.output) {
+ if (output.type === 'stat') {
+ merge_stat(ret_effects, output.name, total);
+ }
+ }
+ }
+ else {
+ if (effect.output.type === 'stat') {
+ merge_stat(ret_effects, effect.output.name, total);
+ }
+ }
+ }
continue;
case 'raw_stat':
// TODO: toggles...
@@ -568,8 +695,7 @@ const atree_stats = new (class extends ComputeNode {
const { type, name, abil = "", value } = bonus;
// TODO: prop
if (type === "stat") {
- if (ret_effects.has(name)) { ret_effects.set(name, ret_effects.get(name) + value); }
- else { ret_effects.set(name, value); }
+ merge_stat(ret_effects, name, value);
}
}
continue;
@@ -586,10 +712,12 @@ const atree_stats = new (class extends ComputeNode {
}
}
}
- console.log(ret_effects);
+ if (ret_effects.has('baseResist')) {
+ merge_stat(ret_effects, "defMult", 1 - (ret_effects.get('baseResist') / 100));
+ }
return ret_effects;
}
-})().link_to(atree_merge, 'atree-merged');
+})().link_to(atree_merge, 'atree-merged').link_to(atree_make_interactives, 'atree-interactive');
/**
@@ -670,42 +798,21 @@ class AbilityTreeEnsureNodesNode extends ComputeNode {
function render_AT(UI_elem, list_elem, tree) {
console.log("constructing ability tree UI");
// add in the "Active" title to atree
- let active_row = document.createElement("div");
- active_row.classList.add("row", "item-title", "mx-auto", "justify-content-center");
- let active_word = document.createElement("div");
- active_word.classList.add("col-auto");
- active_word.textContent = "Active Abilities:";
+ let active_row = make_elem("div", ["row", "item-title", "mx-auto", "justify-content-center"]);
+ let active_word = make_elem("div", ["col-auto"], {textContent: "Active Abilities:"});
- let active_AP_container = document.createElement("div");
- active_AP_container.classList.add("col-auto");
+ let active_AP_container = make_elem("div", ["col-auto"]);
+ let active_AP_subcontainer = make_elem("div", ["row"]);
+ let active_AP_cost = make_elem("div", ["col-auto", "mx-0", "px-0"], {id: "active_AP_cost", textContent: "0"});
- let active_AP_subcontainer = document.createElement("div");
- active_AP_subcontainer.classList.add("row");
+ let active_AP_slash = make_elem("div", ["col-auto", "mx-0", "px-0"], {textContent: "/"});
+ let active_AP_cap = make_elem("div", ["col-auto", "mx-0", "px-0"], {id: "active_AP_cap", textContent: "45"});
+ let active_AP_end = make_elem("div", ["col-auto", "mx-0", "px-0"], {textContent: " AP"});
- let active_AP_cost = document.createElement("div");
- active_AP_cost.classList.add("col-auto", "mx-0", "px-0");
- active_AP_cost.id = "active_AP_cost";
- active_AP_cost.textContent = "0";
- let active_AP_slash = document.createElement("div");
- active_AP_slash.classList.add("col-auto", "mx-0", "px-0");
- active_AP_slash.textContent = "/";
- let active_AP_cap = document.createElement("div");
- active_AP_cap.classList.add("col-auto", "mx-0", "px-0");
- active_AP_cap.id = "active_AP_cap";
- active_AP_cap.textContent = "45";
- let active_AP_end = document.createElement("div");
- active_AP_end.classList.add("col-auto", "mx-0", "px-0");
- active_AP_end.textContent = " AP";
-
- //I can't believe we can't pass in multiple children at once
- active_AP_subcontainer.appendChild(active_AP_cost);
- active_AP_subcontainer.appendChild(active_AP_slash);
- active_AP_subcontainer.appendChild(active_AP_cap);
- active_AP_subcontainer.appendChild(active_AP_end);
active_AP_container.appendChild(active_AP_subcontainer);
+ active_AP_subcontainer.append(active_AP_cost, active_AP_slash, active_AP_cap, active_AP_end);
- active_row.appendChild(active_word);
- active_row.appendChild(active_AP_container);
+ active_row.append(active_word, active_AP_container);
list_elem.appendChild(active_row);
let atree_map = new Map();
@@ -794,7 +901,7 @@ function render_AT(UI_elem, list_elem, tree) {
let node_elem = document.createElement('div');
let icon = ability.display.icon;
if (icon === undefined) {
- icon = "node";
+ icon = "node_0";
}
let node_img = document.createElement('img');
node_img.src = '../media/atree/'+icon+'.png';
@@ -802,7 +909,7 @@ function render_AT(UI_elem, list_elem, tree) {
node_elem.appendChild(node_img);
node_elem.classList.add("atree-circle");
- // add tooltip
+ // add node tooltip
node_elem.addEventListener('mouseover', function(e) {
if (e.target !== this) {return;}
let tooltip = this.children[0];
@@ -817,39 +924,52 @@ function render_AT(UI_elem, list_elem, tree) {
tooltip.style.display = "none";
});
- node_elem.classList.add("fake-button");
+ //node tooltip and active tooltip have common parts - let's make them first
- let node_tooltip = document.createElement('div');
- node_tooltip.classList.add("rounded-bottom", "dark-4", "border", "p-0", "mx-2", "my-4", "dark-shadow");
- node_tooltip.style.display = "none";
-
- // tooltip text formatting
-
- let node_tooltip_title = document.createElement('b');
- node_tooltip_title.classList.add("scaled-font");
- node_tooltip_title.innerHTML = ability.display_name;
- node_tooltip.appendChild(node_tooltip_title);
+ let tooltip_title = document.createElement('div');
+ tooltip_title.classList.add("row", "justify-content-center", "fw-bold");
+ tooltip_title.textContent = ability.display_name;
+ let tooltip_archetype;
if ('archetype' in ability && ability.archetype !== "") {
- let node_tooltip_archetype = document.createElement('p');
- node_tooltip_archetype.classList.add("scaled-font");
- node_tooltip_archetype.innerHTML = "(Archetype: " + ability.archetype+")";
- node_tooltip.appendChild(node_tooltip_archetype);
+ tooltip_archetype = document.createElement('div');
+ tooltip_archetype.classList.add("row", "mx-1", "text-start");
+ tooltip_archetype.textContent = "(Archetype: " + ability.archetype+")";
}
- let node_tooltip_desc = document.createElement('p');
- node_tooltip_desc.classList.add("scaled-font-sm", "my-0", "mx-1", "text-wrap");
- node_tooltip_desc.textContent = ability.desc;
- node_tooltip.appendChild(node_tooltip_desc);
+ let tooltip_desc = document.createElement('div');
+ tooltip_desc.classList.add("row", "mx-1", "text-wrap");
+ tooltip_desc.textContent = ability.desc;
- let node_tooltip_cost = document.createElement('p');
- node_tooltip_cost.classList.add("scaled-font-sm", "my-0", "mx-1", "text-start");
- node_tooltip_cost.textContent = "Cost: " + ability.cost + " AP";
- node_tooltip.appendChild(node_tooltip_cost);
+ let tooltip_cost = document.createElement('div');
+ tooltip_cost.classList.add("row", "mx-1", "text-start");
+ tooltip_cost.textContent = "Cost: " + ability.cost + " AP";
+ //create node tooltip
+ let node_tooltip = document.createElement('div');
+ node_tooltip.classList.add("rounded-bottom", "dark-4", "border", "p-0", "my-1", "dark-shadow", "scaled-font", "container");
+ node_tooltip.style.display = "none";
+ node_tooltip.append(tooltip_title, tooltip_archetype ? tooltip_archetype : "", tooltip_desc);
+
+ //active = copy of node
+ let active_tooltip = node_tooltip.cloneNode(true);
+
+ //node tooltip specific stuff that we don't want to be copied
node_tooltip.style.position = "absolute";
node_tooltip.style.zIndex = "100";
+ node_tooltip.appendChild(tooltip_cost);
+ //add in anything new for active tooltips
+ active_tooltip.id = "atree-ab-" + ability.id;
+
+ if (ability.blockers.length > 0) {
+ let active_tooltip_blockers = document.createElement("div");
+ active_tooltip_blockers.classList.add("row", "mx-1", "text-start");
+ active_tooltip_blockers.textContent = "Blockers: " + ability.blockers.join(", ");
+ active_tooltip.append(active_tooltip_blockers);
+ }
+
+ //append node and active tooltips to corresponding parent elems
node_elem.appendChild(node_tooltip);
//list_elem.appendChild(active_tooltip); NOTE: moved to `atree_render_active`
@@ -881,7 +1001,6 @@ function render_AT(UI_elem, list_elem, tree) {
document.getElementById("atree-row-" + ability.display.row).children[ability.display.col].appendChild(node_elem);
};
- console.log(atree_connectors_map);
atree_render_connection(atree_connectors_map);
return atree_map;
diff --git a/js/atree_constants.js b/js/atree_constants.js
index 97c7143..f7500cc 100644
--- a/js/atree_constants.js
+++ b/js/atree_constants.js
@@ -1405,7 +1405,7 @@ const atrees = {
"type": "raw_stat",
"bonuses": [{
"type": "stat",
- "name": "mdCritPct",
+ "name": "critDamPct",
"value": 30
}]
}]
@@ -1688,10 +1688,10 @@ const atrees = {
"slider_name": "Focus",
"output": {
"type": "stat",
- "name": "damMult"
+ "name": "damMult.Focus"
},
"scaling": [40],
- "max": 3
+ "slider_max": 3
}]
},
{
@@ -1699,6 +1699,7 @@ const atrees = {
"desc": "Add +2 max Focus",
"archetype": "Sharpshooter",
"archetype_req": 0,
+ "base_abil": "Focus",
"parents": ["Cheaper Arrow Storm", "Grappling Hook"],
"dependencies": ["Focus"],
"blockers": [],
@@ -1713,12 +1714,12 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Focus",
+ "slider_max": 2,
"output": {
"type": "stat",
- "name": "damMult"
+ "name": "damMult.Focus"
},
- "scaling": [30],
- "max": 5
+ "scaling": [-5]
}]
},
{
@@ -1726,6 +1727,7 @@ const atrees = {
"desc": "Add +2 max Focus",
"archetype": "Sharpshooter",
"archetype_req": 0,
+ "base_abil": "Focus",
"parents": ["Crepuscular Ray", "Snow Storm"],
"dependencies": ["Focus"],
"blockers": [],
@@ -1740,12 +1742,12 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Focus",
+ "slider_max": 2,
"output": {
"type": "stat",
- "name": "damMult"
+ "name": "damMult.Focus"
},
- "scaling": [25],
- "max": 7
+ "scaling": [-5]
}]
},
{
@@ -1798,13 +1800,13 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Trap Wait Time",
+ "slider_max": 4,
"output": {
"type": "stat",
- "name": "damMult:Basaltic Trap"
+ "name": "damMult.Basaltic:Basaltic Trap"
},
"slider_step": 1,
- "scaling": [20],
- "max": 80
+ "scaling": [20]
}]
},
{
@@ -1828,12 +1830,7 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Trap Wait Time",
- "output": {
- "type": "stat",
- "name": "damMult:Basaltic Trap"
- },
- "scaling": [20],
- "max": 80
+ "slider_max": 4
},
{
"type": "raw_stat",
@@ -1878,6 +1875,7 @@ const atrees = {
"desc": "Condense Arrow Storm into a single ray that damages enemies 10 times per second",
"archetype": "Sharpshooter",
"archetype_req": 0,
+ "base_abil": "Arrow Storm",
"parents": ["Water Mastery", "Fire Creep"],
"dependencies": ["Arrow Storm"],
"blockers": ["Windstorm", "Nimble String", "Arrow Hurricane"],
@@ -1966,12 +1964,12 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Phantom Ray hits",
+ "slider_max": 7,
"output": {
"type": "stat",
- "name": "damMult:Single Arrow"
+ "name": "damMult.Decimator:Single Arrow"
},
- "scaling": 10,
- "max": 70
+ "scaling": 10
}]
}
],
@@ -2209,7 +2207,7 @@ const atrees = {
{
"display_name": "Tougher Skin",
- "desc": "Harden your skin and become permanently +5% more resistant\nFor every 1% or 1 Raw Heath Regen you have from items, gain +10 Health (Max 100)",
+ "desc": "Harden your skin and become permanently +5% more resistant. For every 1% or 1 Raw Heath Regen you have from items, gain +10 Health (Max 100)",
"archetype": "Paladin",
"archetype_req": 0,
"parents": ["Charge"],
@@ -2229,7 +2227,7 @@ const atrees = {
"bonuses": [
{
"type": "stat",
- "name": "baseResist",
+ "name": "defMult.Base",
"value": 5
}
]
@@ -2777,7 +2775,7 @@ const atrees = {
{
"type": "raw_stat",
"toggle": true,
- "bonuses": [{ "type": "stat", "name": "defPct", "value": 70}]
+ "bonuses": [{ "type": "stat", "name": "defMult.Mantle", "value": 70}]
}
]
},
@@ -2804,13 +2802,13 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Corrupted",
+ "slider_max": 100,
+ "slider_step": 1,
"output": {
"type": "stat",
"name": "damRaw"
},
- "scaling": [4],
- "slider_step": 1,
- "max": 120
+ "scaling": [2]
}
]
},
@@ -2985,19 +2983,12 @@ const atrees = {
"effects": [
{
"type": "stat_scaling",
- "slider": false,
- "inputs": [
- {
- "type": "stat",
- "name": "hpBonus"
- }
- ],
+ "slider_name": "Corrupted",
"output": {
"type": "stat",
- "name": "damMult"
+ "name": "damMult.Enraged"
},
- "scaling": [3],
- "max": 300
+ "scaling": [3]
}
]
},
@@ -3139,7 +3130,7 @@ const atrees = {
{
"type": "raw_stat",
"toggle": true,
- "bonuses": [ {"type": "stat", "name": "damMult", "value": 30} ]
+ "bonuses": [ {"type": "stat", "name": "damMult.Ragnarokkr", "value": 30} ]
}
]
},
@@ -3195,7 +3186,7 @@ const atrees = {
"type": "stat",
"name": "fDamPct"
},
- "scaling": [2],
+ "scaling": [0.02],
"max": 100
}
]
@@ -3355,8 +3346,7 @@ const atrees = {
"type": "stat",
"name": "damRaw"
},
- "scaling": [0.5],
- "max": 50
+ "scaling": [0.5]
},
{
"type": "raw_stat",
@@ -3448,7 +3438,7 @@ const atrees = {
"bonuses": [
{
"type": "stat",
- "name": "baseResist",
+ "name": "defMult.Base",
"value": 5
}
]
@@ -3478,7 +3468,7 @@ const atrees = {
{
"type": "raw_stat",
"toggle": true,
- "bonuses": [ {"type": "stat", "name": "damMult", "value": 30} ]
+ "bonuses": [ {"type": "stat", "name": "damMult.ArmorBreaker", "value": 30} ]
}
]
},
@@ -3754,7 +3744,7 @@ const atrees = {
{
"display_name": "Discombobulate",
- "desc": "Every time you hit an enemy, briefly increase your elemental damage dealt to them by +2 (Additive, Max +50). This bonus decays -5 every second",
+ "desc": "Every time you hit an enemy, briefly increase your elemental damage dealt to them by +3 (Additive, Max +80). This bonus decays -5 every second",
"archetype": "Battle Monk",
"archetype_req": 11,
"parents": ["Cyclone"],
@@ -3773,6 +3763,7 @@ const atrees = {
"type": "stat_scaling",
"slider": true,
"slider_name": "Hits dealt",
+ "slider_max": 27,
"output": [
{ "type": "stat", "name": "eDamAddMin" }, { "type": "stat", "name": "eDamAddMax" },
{ "type": "stat", "name": "tDamAddMin" }, { "type": "stat", "name": "tDamAddMax" },
@@ -3929,7 +3920,13 @@ const atrees = {
"icon": "node_2"
},
"properties": {},
- "effects": []
+ "effects": [
+ {
+ "type": "raw_stat",
+ "toggle": true,
+ "bonuses": [{ "type": "stat", "name": "defMult.Brink", "value": 40}]
+ }
+ ]
},
{
diff --git a/js/atree_constants_min.js b/js/atree_constants_min.js
index 7f2374a..53ec1e3 100644
--- a/js/atree_constants_min.js
+++ b/js/atree_constants_min.js
@@ -1 +1 @@
-const atrees={"Archer":[{"display_name":"Arrow Shield","desc":"Create a shield around you that deal damage and knockback mobs when triggered. (2 Charges)","parents":[60,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6,"icon":"node_4"},"properties":{"charges":2,"duration":60,"aoe":5000},"effects":[{"type":"replace_spell","name":"Arrow Shield","cost":30,"base_spell":4,"display":"Total Damage","parts":[{"name":"Shield Damage","type":"damage","multipliers":[90,0,0,0,0,10]},{"name":"Total Damage","type":"total","hits":{"Shield Damage":2}}]}],"id":0},{"display_name":"Escape","desc":"Throw yourself backward to avoid danger. (Hold shift while escaping to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4,"icon":"node_4"},"properties":{"aoe":0,"range":0},"effects":[{"type":"replace_spell","name":"Escape","cost":25,"base_spell":2,"display":"","parts":[]}],"id":1},{"display_name":"Arrow Bomb","desc":"Throw a long-range arrow that explodes and deal high damage in a large area. (Self-damage for 25% of your DPS)","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_4"},"properties":{"aoe":4.5,"range":26},"effects":[{"type":"replace_spell","name":"Arrow Bomb","cost":50,"base_spell":3,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Arrow Bomb","type":"damage","multipliers":[160,0,0,0,20,0]},{"name":"Total Damage","type":"total","hits":{"Arrow Bomb":1}}]}],"id":2},{"display_name":"Heart Shatter","desc":"If you hit a mob directly with Arrow Bomb, shatter its heart and deal bonus damage.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Heart Shatter":1}}],"id":3},{"display_name":"Fire Creep","desc":"Arrow Bomb will leak a trail of fire for 6s, Damaging enemies that walk into it every 0.4s.","base_abil":2,"parents":[68,39,5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6,"icon":"node_1"},"properties":{"aoe":0.8,"duration":6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[30,0,0,0,20,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Burn Damage","hits":{"Fire Creep":15}}],"id":4},{"display_name":"Bryophyte Roots","desc":"When you hit an enemy with Arrow Storm, create an area that slows them down and deals damage every 0.4s.","base_abil":7,"archetype":"Trapper","archetype_req":1,"parents":[4,35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":8,"icon":"node_1"},"properties":{"aoe":2,"duration":5},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Bryophyte Roots","cost":0,"multipliers":[40,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Roots Damage","hits":{"Bryophyte Roots":12}}],"id":5},{"display_name":"Nimble String","desc":"Arrow Storm throw out +6 arrows per stream and shoot twice as fast.","base_abil":7,"parents":[36,69],"dependencies":[7],"blockers":[68],"cost":2,"display":{"row":15,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-15,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","hits":{"Single Arrow":6}}],"id":6},{"display_name":"Arrow Storm","desc":"Shoot a stream of 8 arrows, dealing significant damage to close mobs and pushing them back.","parents":[58,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2,"icon":"node_4"},"properties":{"range":16},"effects":[{"type":"replace_spell","name":"Arrow Storm","cost":40,"base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","multipliers":[30,0,10,0,0,0]},{"name":"Single Stream","hits":{"Single Arrow":8}},{"name":"Total Damage","hits":{"Single Stream":1}}]}],"id":7},{"display_name":"Guardian Angels","desc":"Your protective arrows from Arrow Shield will become sentient bows, dealing damage up to 8 times each to nearby enemies. (Arrow Shield will no longer push nearby enemies)","archetype":"Boltslinger","archetype_req":3,"base_abil":0,"parents":[59,67],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":19,"col":1,"icon":"node_3"},"properties":{"range":4,"duration":60,"shots":8,"charges":2},"effects":[{"type":"replace_spell","name":"Guardian Angels","base_spell":4,"display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[30,0,0,0,0,10]},{"name":"Single Bow","type":"total","hits":{"Single Arrow":8}},{"name":"Total Damage","type":"total","hits":{"Single Bow":2}}]}],"id":8},{"display_name":"Windy Feet","desc":"When casting Escape, give speed to yourself and nearby allies.","base_abil":1,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":1,"icon":"node_0"},"properties":{"aoe":8,"duration":120},"effects":[{"type":"stat_bonus","toggle":"Windy Feet","bonuses":[{"type":"stat","name":"spd","value":20}]}],"id":9},{"display_name":"Basaltic Trap","desc":"When you hit the ground with Arrow Bomb, leave a Trap that damages enemies. (Max 2 Traps)","archetype":"Trapper","archetype_req":2,"base_abil":2,"parents":[5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":8,"icon":"node_3"},"properties":{"aoe":7,"traps":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","multipliers":[140,30,0,0,30,0]}],"id":10},{"display_name":"Windstorm","desc":"Arrow Storm shoot +1 stream of arrows, and each stream shoots +2 arrows, effectively doubling its damage.","base_abil":7,"parents":[8,33],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":21,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-10,0,-2,0,0,2]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","cost":0,"hits":{"Single Arrow":2}}],"id":11},{"display_name":"Grappling Hook","base_abil":1,"desc":"When casting Escape, throw a hook that pulls you when hitting a block. If you hit an enemy, pull them towards you instead. (Escape will not throw you backward anymore)","archetype":"Trapper","archetype_req":0,"parents":[61,40,33],"dependencies":[],"blockers":[20],"cost":2,"display":{"row":21,"col":5,"icon":"node_2"},"properties":{"range":26},"effects":[],"id":12},{"display_name":"Implosion","desc":"Arrow bomb will pull enemies towards you. If a trap is nearby, it will pull them towards it instead. Increase Heart Shatter's damage.","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[12,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[40,0,0,0,0,0]}],"id":13},{"display_name":"Twain's Arc","desc":"When you have 2+ Focus, holding shift will summon the Twain's Arc. Charge it up to shoot a destructive long-range beam. (Damage is dealt as Main Attack Damage)","archetype":"Sharpshooter","archetype_req":4,"parents":[62,64],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":25,"col":4,"icon":"node_2"},"properties":{"range":64,"focusReq":2},"effects":[{"type":"replace_spell","name":"Twain's Arc","base_spell":5,"scaling":"melee","use_atkspd":false,"display":"Single Shot","parts":[{"name":"Single Shot","type":"damage","multipliers":[200,0,0,0,0,0]}]}],"id":14},{"display_name":"Fierce Stomp","desc":"When using Escape, hold shift to quickly drop down and deal damage.","archetype":"Boltslinger","archetype_req":0,"base_abil":1,"parents":[42,64],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Fierce Stomp","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","cost":0,"hits":{"Fierce Stomp":1},"display":"Stomp Damage"}],"id":15},{"display_name":"Scorched Earth","desc":"Fire Creep become much stronger.","archetype":"Sharpshooter","archetype_req":0,"parents":[14],"dependencies":[4],"blockers":[],"cost":1,"display":{"row":26,"col":5,"icon":"node_1"},"properties":{"duration":2,"aoe":0.4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[10,0,0,0,5,0]}],"id":16},{"display_name":"Leap","desc":"When you double tap jump, leap foward. (2s Cooldown)","archetype":"Boltslinger","archetype_req":5,"parents":[42,55],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_1"},"properties":{"cooldown":2},"effects":[],"id":17},{"display_name":"Shocking Bomb","desc":"Arrow Bomb will not be affected by gravity, and all damage conversions become Thunder.","archetype":"Sharpshooter","archetype_req":5,"base_abil":2,"parents":[14,44,55],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":28,"col":4,"icon":"node_1"},"properties":{"gravity":0},"effects":[{"type":"convert_spell_conv","target_part":"all","conversion":"Thunder"}],"id":18},{"display_name":"Mana Trap","desc":"Your Traps will give you 2.85 Mana per second when you stay close to them.","archetype":"Trapper","archetype_req":5,"base_abil":2,"parents":[43,44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":8,"icon":"node_3"},"properties":{"range":16,"manaRegen":2.85},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":10}],"id":19},{"display_name":"Escape Artist","desc":"When casting Escape, release 120 arrows towards the ground.","archetype":"Boltslinger","archetype_req":0,"base_abil":1,"parents":[46,17],"dependencies":[],"blockers":[12],"cost":2,"display":{"row":31,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Per Arrow","multipliers":[20,0,10,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Max Damage (Escape Artist)","hits":{"Per Arrow":120},"display":"Max Damage (Escape Artist)"}],"id":20},{"display_name":"Initiator","desc":"If you do not damage an enemy for 5s or more, your next sucessful hit will deal +50% damage and add +1 Focus.","archetype":"Sharpshooter","archetype_req":5,"parents":[18,44,47],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":31,"col":5,"icon":"node_2"},"properties":{},"effects":[],"id":21},{"display_name":"Call of the Hound","desc":"Arrow Shield summon a Hound that will attack and drag aggressive enemies towards your traps.","archetype":"Trapper","archetype_req":0,"base_abil":0,"parents":[21,47],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_2"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Hound Damage","multipliers":[40,0,0,0,0,0]}],"id":22},{"display_name":"Arrow Hurricane","desc":"Arrow Storm will shoot +2 stream of arrows.","archetype":"Boltslinger","archetype_req":8,"base_abil":7,"parents":[48,20],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":33,"col":0,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":2}}],"id":23},{"display_name":"Geyser Stomp","desc":"Fierce Stomp will create geysers, dealing more damage and vertical knockback.","base_abil":1,"parents":[56],"dependencies":[15],"blockers":[],"cost":2,"display":{"row":37,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Geyser Stomp","multipliers":[0,0,0,50,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","hits":{"Geyser Stomp":1}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":15,"name":"aoe","value":1}]}],"id":24},{"display_name":"Crepuscular Ray","desc":"If you have 5 Focus, casting Arrow Storm will make you levitate and shoot 20 homing arrows per second until you run out of Focus. While in that state, you will lose 1 Focus per second.","archetype":"Sharpshooter","archetype_req":10,"parents":[49],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":37,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Crepuscular Ray","base_spell":6,"display":"DPS","parts":[{"name":"Single Arrow","multipliers":[20,0,0,5,0,0]},{"name":"DPS","hits":{"Single Arrow":20}},{"name":"Total Damage","hits":{"DPS":7}}]}],"id":25},{"display_name":"Grape Bomb","desc":"Arrow bomb will throw 3 additional smaller bombs when exploding.","base_abil":2,"parents":[51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7,"icon":"node_2"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Grape Bomb","multipliers":[30,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Grape Bomb":3}}],"id":26},{"display_name":"Tangled Traps","desc":"Your Traps will be connected by a rope that deals damage to enemies every 0.2s.","archetype":"Trapper","archetype_req":0,"parents":[26],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":38,"col":6,"icon":"node_1"},"properties":{"attackSpeed":0.2},"effects":[{"type":"replace_spell","name":"Tangled Traps","base_spell":7,"display":"DPS","parts":[{"name":"Damage Tick","multipliers":[20,0,0,0,0,20]},{"name":"DPS","hits":{"Damage Tick":5}}]}],"id":27},{"display_name":"Snow Storm","desc":"Enemies near you will be slowed down.","parents":[24,63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":2,"icon":"node_2"},"properties":{"range":2.5,"slowness":0.3},"effects":[],"id":28},{"display_name":"All-Seeing Panoptes","desc":"Your bows from Guardian Angels become all-seeing, increasing their range, damage and letting them shoot up to +5 times each.","archetype":"Boltslinger","archetype_req":11,"base_abil":0,"parents":[28],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":40,"col":1,"icon":"node_3"},"properties":{"range":8,"shots":5},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Arrow","multipliers":[0,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Arrow":5}}],"id":29},{"display_name":"Minefield","desc":"Allow you to place +6 Traps, but with reduced damage and range.","archetype":"Trapper","archetype_req":10,"base_abil":2,"parents":[26,53],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":40,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","cost":0,"multipliers":[-80,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"aoe","value":-2},{"type":"prop","abil":10,"name":"traps","value":6}]}],"id":30},{"display_name":"Bow Proficiency I","desc":"Improve your Main Attack's damage and range when using a bow.","base_abil":999,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":31},{"display_name":"Cheaper Arrow Bomb","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-10}],"id":32},{"display_name":"Cheaper Arrow Storm","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[12,11,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":21,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":33},{"display_name":"Cheaper Escape","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[7,0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":34},{"display_name":"Earth Mastery","desc":"Increases your base damage from all Earth attacks","archetype":"Trapper","archetype_req":0,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":35},{"display_name":"Thunder Mastery","desc":"Increases your base damage from all Thunder attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7,39,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":36},{"display_name":"Water Mastery","desc":"Increases your base damage from all Water attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[34,36,39],"dependencies":[],"blockers":[],"cost":1,"display":{"row":14,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":37},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":38},{"display_name":"Fire Mastery","desc":"Increases base damage from all Fire attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[36,0,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":39},{"display_name":"More Shields","desc":"Give +2 charges to Arrow Shield.","base_abil":0,"parents":[12,10],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":21,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Shield Damage":2,"Single Bow":2}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"charges","value":2}]}],"id":40},{"display_name":"Stormy Feet","desc":"Windy Feet will last longer and add more speed.","archetype":"Boltslinger","base_abil":1,"parents":[11],"dependencies":[9],"blockers":[],"cost":1,"display":{"row":23,"col":1,"icon":"node_0"},"properties":{"duration":60},"effects":[{"type":"stat_bonus","toggle":"Windy Feet","bonuses":[{"type":"stat","name":"spdPct","value":20}]}],"id":41},{"display_name":"Refined Gunpowder","desc":"Increase the damage of Arrow Bomb.","base_abil":2,"parents":[11,64],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","multipliers":[50,0,0,0,0,0]}],"id":42},{"display_name":"More Traps","desc":"Increase the maximum amount of active Traps you can have by +2.","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[54],"dependencies":[10],"blockers":[],"cost":1,"display":{"row":26,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"traps","value":2}]}],"id":43},{"display_name":"Better Arrow Shield","desc":"Arrow Shield will gain additional area of effect, knockback and damage.","archetype":"Sharpshooter","archetype_req":0,"base_abil":0,"parents":[19,18,14],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":28,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Shield","behavior":"modify","multipliers":[40,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"behavior":"modify","name":"aoe","value":1}]}],"id":44},{"display_name":"Better Leap","desc":"Reduce leap's cooldown by 1s.","archetype":"Boltslinger","archetype_req":0,"base_abil":17,"parents":[17,55],"dependencies":[17],"blockers":[],"cost":1,"display":{"row":29,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":17,"name":"cooldown","value":-1}]}],"id":45},{"display_name":"Better Guardian Angels","desc":"Your Guardian Angels can shoot +4 arrows before disappearing.","archetype":"Boltslinger","archetype_req":0,"base_abil":0,"parents":[20,55],"dependencies":[8],"blockers":[],"cost":1,"display":{"row":31,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Arrow":4}}],"id":46},{"display_name":"Cheaper Arrow Storm (2)","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[21,19],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":47},{"display_name":"Precise Shot","desc":"+30% Critical Hit Damage","parents":[46,49,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdCritPct","value":30}]}],"id":48},{"display_name":"Cheaper Arrow Shield","desc":"Reduce the Mana cost of Arrow Shield.","base_abil":0,"parents":[48,21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":49},{"display_name":"Rocket Jump","desc":"Arrow Bomb's self-damage will knockback you farther away.","base_abil":2,"parents":[47,21],"dependencies":[2],"blockers":[],"cost":1,"display":{"row":33,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":50},{"display_name":"Cheaper Escape (2)","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[22,70],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":51},{"display_name":"Stronger Hook","desc":"Increase your Grappling Hook's range, speed and strength.","archetype":"Trapper","archetype_req":5,"base_abil":1,"parents":[51],"dependencies":[12],"blockers":[],"cost":1,"display":{"row":35,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":12,"name":"range","value":8}]}],"id":52},{"display_name":"Cheaper Arrow Bomb (2)","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[63,30],"dependencies":[],"blockers":[],"cost":1,"display":{"row":40,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":53},{"display_name":"Bouncing Bomb","desc":"Arrow Bomb will bounce once when hitting a block or enemy","base_abil":2,"parents":[40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7,"icon":"node_2"},"properties":{},"effects":[],"id":54},{"display_name":"Homing Shots","desc":"Your Main Attack arrows will follow nearby enemies and not be affected by gravity","archetype":"Sharpshooter","base_abil":999,"parents":[17,18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_2"},"properties":{},"effects":[],"id":55},{"display_name":"Shrapnel Bomb","desc":"Arrow Bomb's explosion will fling 15 shrapnel, dealing damage in a large area","archetype":"Boltslinger","archetype_req":8,"base_abil":2,"parents":[23,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Shrapnel Bomblet","multipliers":[40,0,0,0,20,0]}],"id":56},{"display_name":"Elusive","desc":"If you do not get hit for 8+ seconds, become immune to self-damage and remove Arrow Storm's recoil. (Dodging counts as not getting hit)","archetype":"Boltslinger","archetype_req":0,"parents":[24],"dependencies":[],"blockers":[],"cost":2,"display":{"row":38,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":57},{"display_name":"Double Shots","desc":"Double Main Attack arrows, but they deal -30% damage per arrow (harder to hit far enemies)","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[60],"cost":1,"display":{"row":7,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-30,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":2},"display":"Total Damage"}],"id":58},{"display_name":"Triple Shots","desc":"Triple Main Attack arrows, but they deal -20% damage per arrow","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[69,67],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-20,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":1},"display":"Total Damage"}],"id":59},{"display_name":"Power Shots","desc":"Main Attack arrows have increased speed and knockback","archetype":"Sharpshooter","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[58],"cost":1,"display":{"row":7,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":60},{"display_name":"Focus","desc":"When hitting an aggressive mob 5+ blocks away, gain +1 Focus (Max 3). Resets if you miss once","archetype":"Sharpshooter","archetype_req":2,"parents":[68],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","name":"damMult"},"scaling":[40],"max":3}],"id":61},{"display_name":"More Focus","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"parents":[33,12],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","name":"damMult"},"scaling":[30],"max":5}],"id":62},{"display_name":"More Focus (2)","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"parents":[25,28],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":39,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","name":"damMult"},"scaling":[25],"max":7}],"id":63},{"display_name":"Traveler","desc":"For every 1% Walk Speed you have from items, gain +1 Raw Spell Damage (Max 100)","parents":[42,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"spd"}],"output":{"type":"stat","name":"sdRaw"},"scaling":[1],"max":100}],"id":64},{"display_name":"Patient Hunter","desc":"Your Traps will deal +20% more damage for every second they are active (Max +80%)","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[40],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":8,"icon":"node_1"},"properties":{"max":80},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","output":{"type":"stat","name":"damMult:Basaltic Trap"},"slider_step":1,"scaling":[20],"max":80}],"id":65},{"display_name":"Stronger Patient Hunter","desc":"Add +80% Max Damage to Patient Hunter","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[26],"dependencies":[65],"blockers":[],"cost":1,"display":{"row":38,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","output":{"type":"stat","name":"damMult:Basaltic Trap"},"scaling":[20],"max":80},{"type":"raw_stat","bonuses":[{"type":"prop","abil":65,"name":"max","value":80}]}],"id":66},{"display_name":"Frenzy","desc":"Every time you hit an enemy, briefly gain +6% Walk Speed (Max 200%). Decay -40% of the bonus every second","archetype":"Boltslinger","archetype_req":0,"parents":[59,6],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","output":{"type":"stat","name":"spd"},"scaling":[6],"max":160}],"id":67},{"display_name":"Phantom Ray","desc":"Condense Arrow Storm into a single ray that damages enemies 10 times per second","archetype":"Sharpshooter","archetype_req":0,"parents":[37,4],"dependencies":[7],"blockers":[11,6,23],"cost":2,"display":{"row":16,"col":4,"icon":"node_2"},"properties":{},"effects":[{"type":"replace_spell","name":"Phantom Ray","base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[25,0,5,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Arrow":16}}]},{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":68},{"display_name":"Arrow Rain","desc":"When Arrow Shield loses its last charge, unleash 150 arrows raining down on enemies","base_abil":0,"parents":[6,38],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":15,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Per Arrow)","multipliers":[80,0,0,0,0,60]},{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Total)","hits":{"Arrow Rain (Per Arrow)":150}}],"id":69},{"display_name":"Decimator","desc":"Phantom Ray will increase its damage by 10% everytime you do not miss with it (Max 70%)","archetype":"Sharpshooter","archetype_req":0,"parents":[49,51],"dependencies":[68],"blockers":[],"cost":1,"display":{"row":34,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Phantom Ray hits","output":{"type":"stat","name":"damMult:Single Arrow"},"scaling":10,"max":70}],"id":70}],"Warrior":[{"display_name":"Bash","desc":"Violently bash the ground, dealing high damage in a large area","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_4"},"properties":{"aoe":4,"range":3},"effects":[{"type":"replace_spell","name":"Bash","cost":45,"base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Hit","type":"damage","multipliers":[130,20,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Hit":1}}]}],"id":0},{"display_name":"Spear Proficiency 1","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":1},{"display_name":"Cheaper Bash","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":2},{"display_name":"Double Bash","desc":"Bash will hit a second time at a farther range","parents":[1],"base_abil":0,"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{"range":3},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","cost":0,"hits":{"Single Hit":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","cost":0,"multipliers":[-50,0,0,0,0,0]}],"id":3},{"display_name":"Charge","desc":"Charge forward at high speed (hold shift to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_4"},"properties":{},"effects":[{"type":"replace_spell","name":"Charge","cost":25,"base_spell":2,"spell_type":"damage","scaling":"spell","display":"","parts":[]}],"id":4},{"display_name":"Heavy Impact","desc":"After using Charge, violently crash down into the ground and deal damage","base_abil":4,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Heavy Impact","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Heavy Impact":1}}],"id":5},{"display_name":"Vehement","desc":"For every 1% or 1 Raw Main Attack Damage you have from items, gain +2% Walk Speed (Max 20%)","archetype":"Fallen","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[7],"cost":1,"display":{"row":6,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"mdPct"},{"type":"stat","name":"mdRaw"}],"output":{"type":"stat","name":"spd"},"scaling":[2,2],"max":20}],"id":6},{"display_name":"Tougher Skin","desc":"Harden your skin and become permanently +5% more resistant\nFor every 1% or 1 Raw Heath Regen you have from items, gain +10 Health (Max 100)","archetype":"Paladin","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[6],"cost":1,"display":{"row":6,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"baseResist","value":5}]},{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hprRaw"},{"type":"stat","name":"hprPct"}],"output":{"type":"stat","name":"hpBonus"},"scaling":[10,10],"max":100}],"id":7},{"display_name":"Uppercut","desc":"Rocket enemies in the air and deal massive damage","parents":[6,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":2,"icon":"node_4"},"properties":{"aoe":3,"range":5},"effects":[{"type":"replace_spell","name":"Uppercut","cost":45,"base_spell":3,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Uppercut","multipliers":[200,40,40,0,0,0]},{"name":"Total Damage","hits":{"Uppercut":1}}]}],"id":8},{"display_name":"Cheaper Charge","desc":"Reduce the Mana cost of Charge","base_abil":4,"parents":[8,10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":9},{"display_name":"War Scream","desc":"Emit a terrorizing roar that deals damage, pull nearby enemies, and add damage resistance to yourself and allies","parents":[7,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":6,"icon":"node_4"},"properties":{"duration":30,"aoe":12,"defense_bonus":10},"effects":[{"type":"replace_spell","name":"War Scream","cost":35,"base_spell":4,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"War Scream","multipliers":[50,0,0,0,50,0]},{"name":"Total Damage","hits":{"War Scream":1}}]}],"id":10},{"display_name":"Earth Mastery","desc":"Increases base damage from all Earth attacks","archetype":"Fallen","archetype_req":0,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":11},{"display_name":"Thunder Mastery","desc":"Increases base damage from all Thunder attacks","archetype":"Fallen","archetype_req":0,"parents":[8,14,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":12},{"display_name":"Water Mastery","desc":"Increases base damage from all Water attacks","archetype":"Battle Monk","archetype_req":0,"parents":[9,12,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":11,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":13},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Battle Monk","archetype_req":0,"parents":[10,12,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":14},{"display_name":"Fire Mastery","desc":"Increases base damage from all Fire attacks","archetype":"Paladin","archetype_req":0,"parents":[10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":15},{"display_name":"Quadruple Bash","desc":"Bash will hit 4 times at an even larger range","archetype":"Fallen","archetype_req":0,"base_abil":0,"parents":[11,17],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":0,"icon":"node_1"},"properties":{"range":6},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Hit":2}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[-20,0,0,0,0,0]}],"id":16},{"display_name":"Fireworks","desc":"Mobs hit by Uppercut will explode mid-air and receive additional damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[12,16],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fireworks","multipliers":[80,0,20,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Fireworks":1}}],"id":17},{"display_name":"Half-Moon Swipe","desc":"Uppercut will deal a footsweep attack at a longer and wider angle. All elemental conversions become Water","archetype":"Battle Monk","archetype_req":1,"base_abil":8,"parents":[13],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":13,"col":4,"icon":"node_1"},"properties":{"range":4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":-10,"multipliers":[-70,0,0,30,0,0]}],"id":18},{"display_name":"Flyby Jab","desc":"Damage enemies in your way when using Charge","base_abil":4,"parents":[14,20],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":6,"icon":"node_1"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flyby Jab","multipliers":[20,0,0,0,0,40]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Flyby Jab":1}}],"id":19},{"display_name":"Flaming Uppercut","desc":"Uppercut will light mobs on fire, dealing damage every 0.6 seconds","archetype":"Paladin","archetype_req":0,"base_abil":8,"parents":[15,19],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":12,"col":8,"icon":"node_1"},"properties":{"duration":3,"tick":0.6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Flaming Uppercut","multipliers":[0,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Flaming Uppercut Total Damage","hits":{"Flaming Uppercut":5}},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Flaming Uppercut":5}}],"id":20},{"display_name":"Iron Lungs","desc":"War Scream deals more damage","archetype":"Paladin","archetype_req":0,"base_abil":10,"parents":[19,20],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"War Scream","cost":0,"multipliers":[30,0,0,0,0,30]}],"id":21},{"display_name":"Generalist","desc":"After casting 3 different spells in a row, your next spell will cost 5 mana","archetype":"Battle Monk","archetype_req":3,"parents":[23],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":2,"icon":"node_3"},"properties":{},"effects":[],"id":22},{"display_name":"Counter","desc":"When dodging a nearby enemy attack, get 30% chance to instantly attack back","archetype":"Battle Monk","archetype_req":0,"parents":[18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":4,"icon":"node_1"},"properties":{"chance":30},"effects":[{"type":"replace_spell","name":"Counter","base_spell":5,"display":"Counter Damage","parts":[{"name":"Counter Damage","multipliers":[60,0,20,0,0,20]}]}],"id":23},{"display_name":"Mantle of the Bovemists","desc":"When casting War Scream, create a holy shield around you that reduces all incoming damage by 70% for 3 hits (20s cooldown)","archetype":"Paladin","archetype_req":3,"parents":[21],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":15,"col":7,"icon":"node_3"},"properties":{"mantle_charge":3},"effects":[{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"defPct","value":70}]}],"id":24},{"display_name":"Bak'al's Grasp","desc":"After casting War Scream, become Corrupted (15s Cooldown). You cannot heal while in that state\n\nWhile Corrupted, every 2% of Health you lose will add +4 Raw Damage to your attacks (Max 120)","archetype":"Fallen","archetype_req":2,"parents":[16,17],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":16,"col":1,"icon":"node_3"},"properties":{"cooldown":15},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"stat","name":"damRaw"},"scaling":[4],"slider_step":1,"max":120}],"id":25},{"display_name":"Spear Proficiency 2","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[25,27],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":26},{"display_name":"Cheaper Uppercut","desc":"Reduce the Mana Cost of Uppercut","base_abil":8,"parents":[26,28,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":27},{"display_name":"Aerodynamics","desc":"During Charge, you can steer and change direction","archetype":"Battle Monk","archetype_req":0,"base_abil":4,"parents":[27,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":28},{"display_name":"Provoke","desc":"Mobs damaged by War Scream will target only you for at least 5s \n\nReduce the Mana cost of War Scream","archetype":"Paladin","archetype_req":0,"base_abil":10,"parents":[28,24],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":29},{"display_name":"Precise Strikes","desc":"+30% Critical Hit Damage","parents":[27,26],"dependencies":[],"blockers":[],"cost":1,"display":{"row":18,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":30},{"display_name":"Air Shout","desc":"War Scream will fire a projectile that can go through walls and deal damage multiple times","base_abil":10,"parents":[28,29],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":18,"col":6,"icon":"node_1"},"properties":{"attackRate":2},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Air Shout","multipliers":[40,0,0,0,0,10]}],"id":31},{"display_name":"Enraged Blow","desc":"While Corriupted, every 1% of Health you lose will increase your damage by +3% (Max 300%)","archetype":"Fallen","archetype_req":0,"base_abil":25,"parents":[26],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":20,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hpBonus"}],"output":{"type":"stat","name":"damMult"},"scaling":[3],"max":300}],"id":32},{"display_name":"Flying Kick","desc":"When using Charge, mobs hit will halt your momentum and get knocked back","archetype":"Battle Monk","archetype_req":1,"base_abil":4,"parents":[27,34],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":3,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick","multipliers":[150,0,0,20,0,30]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Flying Kick":1},"display":"Flying Kick Max Damage"}],"id":33},{"display_name":"Stronger Mantle","desc":"Add +2 additional charges to Mantle of the Bovemists","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[35,33],"dependencies":[24],"blockers":[],"cost":1,"display":{"row":20,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":24,"name":"mantle_charge","value":2}]}],"id":34},{"display_name":"Manachism","desc":"If you receive a hit that's less than 5% of your max HP, gain 10 Mana (1s Cooldown)","archetype":"Paladin","archetype_req":3,"parents":[34,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":8,"icon":"node_2"},"properties":{"cooldown":1},"effects":[],"id":35},{"display_name":"Boiling Blood","desc":"Bash leaves a trail of boiling blood behind its first explosion, slowing down and damaging enemies above it every 0.4 seconds","base_abil":0,"parents":[32,37],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Boiling Blood","cost":0,"multipliers":[25,0,0,0,5,0]}],"id":36},{"display_name":"Ragnarokkr","desc":"War Scream become deafening, increasing its range and giving damage bonus to players","archetype":"Fallen","archetype_req":0,"base_abil":10,"parents":[36,33],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_2"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":10},{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"damMult","value":30}]}],"id":37},{"display_name":"Ambidextrous","desc":"Increase your chance to attack with Counter by +30%","base_abil":23,"parents":[33,34,39],"dependencies":[23],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":23,"name":"chance","value":30}]}],"id":38},{"display_name":"Burning Heart","desc":"For every 100 Health Bonus you have from item IDs, gain +2% Fire Damage (Max 100%)","archetype":"Paladin","archetype_req":0,"parents":[38,40],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hpBonus"}],"output":{"type":"stat","name":"fDamPct"},"scaling":[2],"max":100}],"id":39},{"display_name":"Stronger Bash","desc":"Increase the damage of Bash","base_abil":0,"parents":[39,35],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[30,0,0,0,0,0]}],"id":40},{"display_name":"Intoxicating Blood","desc":"After leaving Corrupted, gain 2% of the health lost back for each enemy killed while Corrupted","archetype":"Fallen","archetype_req":5,"base_abil":25,"parents":[37,36],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":23,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":41},{"display_name":"Comet","desc":"After being hit by Fireworks, enemies will crash into the ground and receive more damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[37],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":24,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Comet","cost":0,"multipliers":[80,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"hits":{"Comet":1}}],"id":42},{"display_name":"Collide","desc":"Mobs thrown into walls from Flying Kick will explode and receive additonal damage","archetype":"Battle Monk","archetype_req":4,"base_abil":4,"parents":[38,39],"dependencies":[33],"blockers":[],"cost":2,"display":{"row":23,"col":5,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Collide","cost":0,"multipliers":[150,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Collide":1}}],"id":43},{"display_name":"Rejuvenating Skin","desc":"Regain back 30% of the damage you take as healing over 30s","archetype":"Paladin","archetype_req":5,"parents":[39,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":23,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":44},{"display_name":"Uncontainable Corruption","desc":"Reduce the cooldown of Bak'al's Grasp by -5s, and increase the raw damage gained for every 2% of health lost by +1","base_abil":25,"parents":[36,46],"dependencies":[25],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"stat","name":"damRaw"},"scaling":[0.5],"max":50},{"type":"raw_stat","bonuses":[{"type":"prop","abil":25,"name":"cooldown","value":-5}]}],"id":45},{"display_name":"Radiant Devotee","desc":"For every 4% Reflection you have from items, gain +1/5s Mana Regen (Max 10/5s)","archetype":"Battle Monk","archetype_req":1,"parents":[47,45],"dependencies":[],"blockers":[],"cost":1,"display":{"row":26,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","inputs":[{"type":"stat","name":"ref"}],"output":{"type":"stat","name":"mr"},"scaling":[0.25],"max":10}],"id":46},{"display_name":"Whirlwind Strike","desc":"Uppercut will create a strong gust of air, launching you upward with enemies (Hold shift to stay grounded)","archetype":"Battle Monk","archetype_req":5,"base_abil":8,"parents":[38,46],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":26,"col":4,"icon":"node_1"},"properties":{"range":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","multipliers":[0,0,0,0,0,50]}],"id":47},{"display_name":"Mythril Skin","desc":"Gain +5% Base Resistance and become immune to knockback","archetype":"Paladin","archetype_req":6,"parents":[44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"baseResist","value":5}]}],"id":48},{"display_name":"Armour Breaker","desc":"While Corrupted, losing 30% Health will make your next Uppercut destroy enemies' defense, rendering them weaker to damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[45,46],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":27,"col":1,"icon":"node_2"},"properties":{"duration":5},"effects":[{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"damMult","value":30}]}],"id":49},{"display_name":"Shield Strike","desc":"When your Mantle of the Bovemist loses all charges, deal damage around you for each Mantle individually lost","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[48,51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Shield Strike","base_spell":6,"display":"Damage per Shield","parts":[{"name":"Damage per Shield","multipliers":[60,0,20,0,0,0]}]}],"id":50},{"display_name":"Sparkling Hope","desc":"Everytime you heal 5% of your max health, deal damage to all nearby enemies","archetype":"Paladin","archetype_req":0,"parents":[48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":8,"icon":"node_2"},"properties":{"aoe":6},"effects":[{"type":"replace_spell","name":"Sparkling Hope","base_spell":6,"display":"Damage Tick","parts":[{"name":"Damage Tick","multipliers":[10,0,5,0,0,0]}]}],"id":51},{"display_name":"Massive Bash","desc":"While Corrupted, every 3% Health you lose will add +1 AoE to Bash (Max 10)","archetype":"Fallen","archetype_req":8,"base_abil":0,"parents":[53,45],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"prop","abil":0,"name":"aoe"},"scaling":[0.3333333333333333],"max":10}],"id":52},{"display_name":"Tempest","desc":"War Scream will ripple the ground and deal damage 3 times in a large area","archetype":"Battle Monk","archetype_req":0,"base_abil":10,"parents":[52,54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_1"},"properties":{"aoe":16},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest","multipliers":[30,10,0,0,0,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest Total Damage","hits":{"Tempest":3}},{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Tempest":3}}],"id":53},{"display_name":"Spirit of the Rabbit","desc":"Reduce the Mana cost of Charge and increase your Walk Speed by +20%","archetype":"Battle Monk","archetype_req":5,"base_abil":4,"parents":[53,47],"dependencies":[],"blockers":[],"cost":1,"display":{"row":28,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5},{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":20}]}],"id":54},{"display_name":"Massacre","desc":"While Corrupted, if your effective attack speed is Slow or lower, hitting an enemy with your Main Attack will add +1% to your Corrupted bar","archetype":"Fallen","archetype_req":5,"base_abil":999,"parents":[53,52],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":55},{"display_name":"Axe Kick","desc":"Increase the damage of Uppercut, but also increase its mana cost","base_abil":8,"parents":[53,54],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":10,"multipliers":[100,0,0,0,0,0]}],"id":56},{"display_name":"Radiance","desc":"Bash will buff your allies' positive IDs. (15s Cooldown)","archetype":"Paladin","archetype_req":2,"base_abil":0,"parents":[54,58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":5,"icon":"node_2"},"properties":{"cooldown":15},"effects":[],"id":57},{"display_name":"Cheaper Bash 2","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[57,50,51],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":58},{"display_name":"Cheaper War Scream","desc":"Reduce the Mana cost of War Scream","base_abil":10,"parents":[52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":59},{"display_name":"Discombobulate","desc":"Every time you hit an enemy, briefly increase your elemental damage dealt to them by +2 (Additive, Max +50). This bonus decays -5 every second","archetype":"Battle Monk","archetype_req":11,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":2,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","output":[{"type":"stat","name":"eDamAddMin"},{"type":"stat","name":"eDamAddMax"},{"type":"stat","name":"tDamAddMin"},{"type":"stat","name":"tDamAddMax"},{"type":"stat","name":"wDamAddMin"},{"type":"stat","name":"wDamAddMax"},{"type":"stat","name":"fDamAddMin"},{"type":"stat","name":"fDamAddMax"},{"type":"stat","name":"aDamAddMin"},{"type":"stat","name":"aDamAddMax"}],"scaling":[3],"max":80}],"id":60},{"display_name":"Thunderclap","desc":"Bash will cast at the player's position and gain additional AoE.\n\n All elemental conversions become Thunder","archetype":"Battle Monk","archetype_req":8,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"convert_spell_conv","target_part":"all","conversion":"Thunder"},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"aoe","value":3}]}],"id":61},{"display_name":"Cyclone","desc":"After casting War Scream, envelop yourself with a vortex that damages nearby enemies every 0.5s","archetype":"Battle Monk","archetype_req":0,"parents":[54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":4,"icon":"node_1"},"properties":{"aoe":4,"duration":20},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone","multipliers":[10,0,0,0,5,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone Total Damage","hits":{"Cyclone":40}}],"id":62},{"display_name":"Second Chance","desc":"When you receive a fatal blow, survive and regain 30% of your Health (10m Cooldown)","archetype":"Paladin","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":63},{"display_name":"Blood Pact","desc":"If you do not have enough mana to cast a spell, spend health instead (0.6% health per mana)","archetype":"Fallen","archetype_req":10,"parents":[59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_3"},"properties":{"health_cost":0.6},"effects":[],"id":64},{"display_name":"Haemorrhage","desc":"Reduce Blood Pact's health cost. (0.3% health per mana)","archetype":"Fallen","archetype_req":0,"base_abil":64,"parents":[64],"dependencies":[64],"blockers":[],"cost":1,"display":{"row":35,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":64,"name":"health_cost","value":-0.3}]}],"id":65},{"display_name":"Brink of Madness","desc":"If your health is 25% full or less, gain +40% Resistance","parents":[64,67],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":4,"icon":"node_2"},"properties":{},"effects":[],"id":66},{"display_name":"Cheaper Uppercut 2","desc":"Reduce the Mana cost of Uppercut","base_abil":8,"parents":[63,66],"dependencies":[],"blockers":[],"cost":1,"display":{"row":35,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":67},{"display_name":"Martyr","desc":"When you receive a fatal blow, all nearby allies become invincible","archetype":"Paladin","archetype_req":0,"parents":[63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":8,"icon":"node_1"},"properties":{"duration":3,"aoe":12},"effects":[],"id":68}]}
\ No newline at end of file
+const atrees={"Archer":[{"display_name":"Arrow Shield","desc":"Create a shield around you that deal damage and knockback mobs when triggered. (2 Charges)","parents":[60,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6,"icon":"node_4"},"properties":{"charges":2,"duration":60,"aoe":5000},"effects":[{"type":"replace_spell","name":"Arrow Shield","cost":30,"base_spell":4,"display":"Total Damage","parts":[{"name":"Shield Damage","type":"damage","multipliers":[90,0,0,0,0,10]},{"name":"Total Damage","type":"total","hits":{"Shield Damage":2}}]}],"id":0},{"display_name":"Escape","desc":"Throw yourself backward to avoid danger. (Hold shift while escaping to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4,"icon":"node_4"},"properties":{"aoe":0,"range":0},"effects":[{"type":"replace_spell","name":"Escape","cost":25,"base_spell":2,"display":"","parts":[]}],"id":1},{"display_name":"Arrow Bomb","desc":"Throw a long-range arrow that explodes and deal high damage in a large area. (Self-damage for 25% of your DPS)","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_4"},"properties":{"aoe":4.5,"range":26},"effects":[{"type":"replace_spell","name":"Arrow Bomb","cost":50,"base_spell":3,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Arrow Bomb","type":"damage","multipliers":[160,0,0,0,20,0]},{"name":"Total Damage","type":"total","hits":{"Arrow Bomb":1}}]}],"id":2},{"display_name":"Heart Shatter","desc":"If you hit a mob directly with Arrow Bomb, shatter its heart and deal bonus damage.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Heart Shatter":1}}],"id":3},{"display_name":"Fire Creep","desc":"Arrow Bomb will leak a trail of fire for 6s, Damaging enemies that walk into it every 0.4s.","base_abil":2,"parents":[68,39,5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6,"icon":"node_1"},"properties":{"aoe":0.8,"duration":6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[30,0,0,0,20,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Burn Damage","hits":{"Fire Creep":15}}],"id":4},{"display_name":"Bryophyte Roots","desc":"When you hit an enemy with Arrow Storm, create an area that slows them down and deals damage every 0.4s.","base_abil":7,"archetype":"Trapper","archetype_req":1,"parents":[4,35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":8,"icon":"node_1"},"properties":{"aoe":2,"duration":5},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Bryophyte Roots","cost":0,"multipliers":[40,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Roots Damage","hits":{"Bryophyte Roots":12}}],"id":5},{"display_name":"Nimble String","desc":"Arrow Storm throw out +6 arrows per stream and shoot twice as fast.","base_abil":7,"parents":[36,69],"dependencies":[7],"blockers":[68],"cost":2,"display":{"row":15,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-15,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","hits":{"Single Arrow":6}}],"id":6},{"display_name":"Arrow Storm","desc":"Shoot a stream of 8 arrows, dealing significant damage to close mobs and pushing them back.","parents":[58,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2,"icon":"node_4"},"properties":{"range":16},"effects":[{"type":"replace_spell","name":"Arrow Storm","cost":40,"base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","multipliers":[30,0,10,0,0,0]},{"name":"Single Stream","hits":{"Single Arrow":8}},{"name":"Total Damage","hits":{"Single Stream":1}}]}],"id":7},{"display_name":"Guardian Angels","desc":"Your protective arrows from Arrow Shield will become sentient bows, dealing damage up to 8 times each to nearby enemies. (Arrow Shield will no longer push nearby enemies)","archetype":"Boltslinger","archetype_req":3,"base_abil":0,"parents":[59,67],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":19,"col":1,"icon":"node_3"},"properties":{"range":4,"duration":60,"shots":8,"charges":2},"effects":[{"type":"replace_spell","name":"Guardian Angels","base_spell":4,"display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[30,0,0,0,0,10]},{"name":"Single Bow","type":"total","hits":{"Single Arrow":8}},{"name":"Total Damage","type":"total","hits":{"Single Bow":2}}]}],"id":8},{"display_name":"Windy Feet","desc":"When casting Escape, give speed to yourself and nearby allies.","base_abil":1,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":1,"icon":"node_0"},"properties":{"aoe":8,"duration":120},"effects":[{"type":"stat_bonus","toggle":"Windy Feet","bonuses":[{"type":"stat","name":"spd","value":20}]}],"id":9},{"display_name":"Basaltic Trap","desc":"When you hit the ground with Arrow Bomb, leave a Trap that damages enemies. (Max 2 Traps)","archetype":"Trapper","archetype_req":2,"base_abil":2,"parents":[5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":8,"icon":"node_3"},"properties":{"aoe":7,"traps":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","multipliers":[140,30,0,0,30,0]}],"id":10},{"display_name":"Windstorm","desc":"Arrow Storm shoot +1 stream of arrows, and each stream shoots +2 arrows, effectively doubling its damage.","base_abil":7,"parents":[8,33],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":21,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","multipliers":[-10,0,-2,0,0,2]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","cost":0,"hits":{"Single Arrow":2}}],"id":11},{"display_name":"Grappling Hook","base_abil":1,"desc":"When casting Escape, throw a hook that pulls you when hitting a block. If you hit an enemy, pull them towards you instead. (Escape will not throw you backward anymore)","archetype":"Trapper","archetype_req":0,"parents":[61,40,33],"dependencies":[],"blockers":[20],"cost":2,"display":{"row":21,"col":5,"icon":"node_2"},"properties":{"range":26},"effects":[],"id":12},{"display_name":"Implosion","desc":"Arrow bomb will pull enemies towards you. If a trap is nearby, it will pull them towards it instead. Increase Heart Shatter's damage.","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[12,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Heart Shatter","multipliers":[40,0,0,0,0,0]}],"id":13},{"display_name":"Twain's Arc","desc":"When you have 2+ Focus, holding shift will summon the Twain's Arc. Charge it up to shoot a destructive long-range beam. (Damage is dealt as Main Attack Damage)","archetype":"Sharpshooter","archetype_req":4,"parents":[62,64],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":25,"col":4,"icon":"node_2"},"properties":{"range":64,"focusReq":2},"effects":[{"type":"replace_spell","name":"Twain's Arc","base_spell":5,"scaling":"melee","use_atkspd":false,"display":"Single Shot","parts":[{"name":"Single Shot","type":"damage","multipliers":[200,0,0,0,0,0]}]}],"id":14},{"display_name":"Fierce Stomp","desc":"When using Escape, hold shift to quickly drop down and deal damage.","archetype":"Boltslinger","archetype_req":0,"base_abil":1,"parents":[42,64],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Fierce Stomp","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","cost":0,"hits":{"Fierce Stomp":1},"display":"Stomp Damage"}],"id":15},{"display_name":"Scorched Earth","desc":"Fire Creep become much stronger.","archetype":"Sharpshooter","archetype_req":0,"parents":[14],"dependencies":[4],"blockers":[],"cost":1,"display":{"row":26,"col":5,"icon":"node_1"},"properties":{"duration":2,"aoe":0.4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","multipliers":[10,0,0,0,5,0]}],"id":16},{"display_name":"Leap","desc":"When you double tap jump, leap foward. (2s Cooldown)","archetype":"Boltslinger","archetype_req":5,"parents":[42,55],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_1"},"properties":{"cooldown":2},"effects":[],"id":17},{"display_name":"Shocking Bomb","desc":"Arrow Bomb will not be affected by gravity, and all damage conversions become Thunder.","archetype":"Sharpshooter","archetype_req":5,"base_abil":2,"parents":[14,44,55],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":28,"col":4,"icon":"node_1"},"properties":{"gravity":0},"effects":[{"type":"convert_spell_conv","target_part":"all","conversion":"Thunder"}],"id":18},{"display_name":"Mana Trap","desc":"Your Traps will give you 2.85 Mana per second when you stay close to them.","archetype":"Trapper","archetype_req":5,"base_abil":2,"parents":[43,44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":8,"icon":"node_3"},"properties":{"range":16,"manaRegen":2.85},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":10}],"id":19},{"display_name":"Escape Artist","desc":"When casting Escape, release 120 arrows towards the ground.","archetype":"Boltslinger","archetype_req":0,"base_abil":1,"parents":[46,17],"dependencies":[],"blockers":[12],"cost":2,"display":{"row":31,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Per Arrow","multipliers":[20,0,10,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Max Damage (Escape Artist)","hits":{"Per Arrow":120},"display":"Max Damage (Escape Artist)"}],"id":20},{"display_name":"Initiator","desc":"If you do not damage an enemy for 5s or more, your next sucessful hit will deal +50% damage and add +1 Focus.","archetype":"Sharpshooter","archetype_req":5,"parents":[18,44,47],"dependencies":[61],"blockers":[],"cost":2,"display":{"row":31,"col":5,"icon":"node_2"},"properties":{},"effects":[],"id":21},{"display_name":"Call of the Hound","desc":"Arrow Shield summon a Hound that will attack and drag aggressive enemies towards your traps.","archetype":"Trapper","archetype_req":0,"base_abil":0,"parents":[21,47],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_2"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Hound Damage","multipliers":[40,0,0,0,0,0]}],"id":22},{"display_name":"Arrow Hurricane","desc":"Arrow Storm will shoot +2 stream of arrows.","archetype":"Boltslinger","archetype_req":8,"base_abil":7,"parents":[48,20],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":33,"col":0,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Stream":2}}],"id":23},{"display_name":"Geyser Stomp","desc":"Fierce Stomp will create geysers, dealing more damage and vertical knockback.","base_abil":1,"parents":[56],"dependencies":[15],"blockers":[],"cost":2,"display":{"row":37,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Geyser Stomp","multipliers":[0,0,0,50,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Stomp Damage","hits":{"Geyser Stomp":1}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":15,"name":"aoe","value":1}]}],"id":24},{"display_name":"Crepuscular Ray","desc":"If you have 5 Focus, casting Arrow Storm will make you levitate and shoot 20 homing arrows per second until you run out of Focus. While in that state, you will lose 1 Focus per second.","archetype":"Sharpshooter","archetype_req":10,"parents":[49],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":37,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"replace_spell","name":"Crepuscular Ray","base_spell":6,"display":"DPS","parts":[{"name":"Single Arrow","multipliers":[20,0,0,5,0,0]},{"name":"DPS","hits":{"Single Arrow":20}},{"name":"Total Damage","hits":{"DPS":7}}]}],"id":25},{"display_name":"Grape Bomb","desc":"Arrow bomb will throw 3 additional smaller bombs when exploding.","base_abil":2,"parents":[51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7,"icon":"node_2"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Grape Bomb","multipliers":[30,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Grape Bomb":3}}],"id":26},{"display_name":"Tangled Traps","desc":"Your Traps will be connected by a rope that deals damage to enemies every 0.2s.","archetype":"Trapper","archetype_req":0,"parents":[26],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":38,"col":6,"icon":"node_1"},"properties":{"attackSpeed":0.2},"effects":[{"type":"replace_spell","name":"Tangled Traps","base_spell":7,"display":"DPS","parts":[{"name":"Damage Tick","multipliers":[20,0,0,0,0,20]},{"name":"DPS","hits":{"Damage Tick":5}}]}],"id":27},{"display_name":"Snow Storm","desc":"Enemies near you will be slowed down.","parents":[24,63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":2,"icon":"node_2"},"properties":{"range":2.5,"slowness":0.3},"effects":[],"id":28},{"display_name":"All-Seeing Panoptes","desc":"Your bows from Guardian Angels become all-seeing, increasing their range, damage and letting them shoot up to +5 times each.","archetype":"Boltslinger","archetype_req":11,"base_abil":0,"parents":[28],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":40,"col":1,"icon":"node_3"},"properties":{"range":8,"shots":5},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Arrow","multipliers":[0,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Arrow":5}}],"id":29},{"display_name":"Minefield","desc":"Allow you to place +6 Traps, but with reduced damage and range.","archetype":"Trapper","archetype_req":10,"base_abil":2,"parents":[26,53],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":40,"col":7,"icon":"node_3"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","cost":0,"multipliers":[-80,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"aoe","value":-2},{"type":"prop","abil":10,"name":"traps","value":6}]}],"id":30},{"display_name":"Bow Proficiency I","desc":"Improve your Main Attack's damage and range when using a bow.","base_abil":999,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":31},{"display_name":"Cheaper Arrow Bomb","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-10}],"id":32},{"display_name":"Cheaper Arrow Storm","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[12,11,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":21,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":33},{"display_name":"Cheaper Escape","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[7,0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":34},{"display_name":"Earth Mastery","desc":"Increases your base damage from all Earth attacks","archetype":"Trapper","archetype_req":0,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":35},{"display_name":"Thunder Mastery","desc":"Increases your base damage from all Thunder attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7,39,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":36},{"display_name":"Water Mastery","desc":"Increases your base damage from all Water attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[34,36,39],"dependencies":[],"blockers":[],"cost":1,"display":{"row":14,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":37},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Boltslinger","archetype_req":0,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":38},{"display_name":"Fire Mastery","desc":"Increases base damage from all Fire attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[36,0,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":39},{"display_name":"More Shields","desc":"Give +2 charges to Arrow Shield.","base_abil":0,"parents":[12,10],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":21,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Shield Damage":2,"Single Bow":2}},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"charges","value":2}]}],"id":40},{"display_name":"Stormy Feet","desc":"Windy Feet will last longer and add more speed.","archetype":"Boltslinger","base_abil":1,"parents":[11],"dependencies":[9],"blockers":[],"cost":1,"display":{"row":23,"col":1,"icon":"node_0"},"properties":{"duration":60},"effects":[{"type":"stat_bonus","toggle":"Windy Feet","bonuses":[{"type":"stat","name":"spdPct","value":20}]}],"id":41},{"display_name":"Refined Gunpowder","desc":"Increase the damage of Arrow Bomb.","base_abil":2,"parents":[11,64],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","multipliers":[50,0,0,0,0,0]}],"id":42},{"display_name":"More Traps","desc":"Increase the maximum amount of active Traps you can have by +2.","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[54],"dependencies":[10],"blockers":[],"cost":1,"display":{"row":26,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":10,"name":"traps","value":2}]}],"id":43},{"display_name":"Better Arrow Shield","desc":"Arrow Shield will gain additional area of effect, knockback and damage.","archetype":"Sharpshooter","archetype_req":0,"base_abil":0,"parents":[19,18,14],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":28,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Shield","behavior":"modify","multipliers":[40,0,0,0,0,0]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"behavior":"modify","name":"aoe","value":1}]}],"id":44},{"display_name":"Better Leap","desc":"Reduce leap's cooldown by 1s.","archetype":"Boltslinger","archetype_req":0,"base_abil":17,"parents":[17,55],"dependencies":[17],"blockers":[],"cost":1,"display":{"row":29,"col":1,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":17,"name":"cooldown","value":-1}]}],"id":45},{"display_name":"Better Guardian Angels","desc":"Your Guardian Angels can shoot +4 arrows before disappearing.","archetype":"Boltslinger","archetype_req":0,"base_abil":0,"parents":[20,55],"dependencies":[8],"blockers":[],"cost":1,"display":{"row":31,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","hits":{"Single Arrow":4}}],"id":46},{"display_name":"Cheaper Arrow Storm (2)","desc":"Reduce the Mana cost of Arrow Storm.","base_abil":7,"parents":[21,19],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":47},{"display_name":"Precise Shot","desc":"+30% Critical Hit Damage","parents":[46,49,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":48},{"display_name":"Cheaper Arrow Shield","desc":"Reduce the Mana cost of Arrow Shield.","base_abil":0,"parents":[48,21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":49},{"display_name":"Rocket Jump","desc":"Arrow Bomb's self-damage will knockback you farther away.","base_abil":2,"parents":[47,21],"dependencies":[2],"blockers":[],"cost":1,"display":{"row":33,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":50},{"display_name":"Cheaper Escape (2)","desc":"Reduce the Mana cost of Escape.","base_abil":1,"parents":[22,70],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":51},{"display_name":"Stronger Hook","desc":"Increase your Grappling Hook's range, speed and strength.","archetype":"Trapper","archetype_req":5,"base_abil":1,"parents":[51],"dependencies":[12],"blockers":[],"cost":1,"display":{"row":35,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":12,"name":"range","value":8}]}],"id":52},{"display_name":"Cheaper Arrow Bomb (2)","desc":"Reduce the Mana cost of Arrow Bomb.","base_abil":2,"parents":[63,30],"dependencies":[],"blockers":[],"cost":1,"display":{"row":40,"col":5,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":53},{"display_name":"Bouncing Bomb","desc":"Arrow Bomb will bounce once when hitting a block or enemy","base_abil":2,"parents":[40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7,"icon":"node_2"},"properties":{},"effects":[],"id":54},{"display_name":"Homing Shots","desc":"Your Main Attack arrows will follow nearby enemies and not be affected by gravity","archetype":"Sharpshooter","base_abil":999,"parents":[17,18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_2"},"properties":{},"effects":[],"id":55},{"display_name":"Shrapnel Bomb","desc":"Arrow Bomb's explosion will fling 15 shrapnel, dealing damage in a large area","archetype":"Boltslinger","archetype_req":8,"base_abil":2,"parents":[23,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Shrapnel Bomblet","multipliers":[40,0,0,0,20,0]}],"id":56},{"display_name":"Elusive","desc":"If you do not get hit for 8+ seconds, become immune to self-damage and remove Arrow Storm's recoil. (Dodging counts as not getting hit)","archetype":"Boltslinger","archetype_req":0,"parents":[24],"dependencies":[],"blockers":[],"cost":2,"display":{"row":38,"col":0,"icon":"node_1"},"properties":{},"effects":[],"id":57},{"display_name":"Double Shots","desc":"Double Main Attack arrows, but they deal -30% damage per arrow (harder to hit far enemies)","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[60],"cost":1,"display":{"row":7,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-30,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":2},"display":"Total Damage"}],"id":58},{"display_name":"Triple Shots","desc":"Triple Main Attack arrows, but they deal -20% damage per arrow","archetype":"Boltslinger","archetype_req":0,"base_abil":999,"parents":[69,67],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Single Shot","multipliers":[-20,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":0,"target_part":"Total Damage","hits":{"Single Shot":1},"display":"Total Damage"}],"id":59},{"display_name":"Power Shots","desc":"Main Attack arrows have increased speed and knockback","archetype":"Sharpshooter","archetype_req":0,"base_abil":999,"parents":[1],"dependencies":[],"blockers":[58],"cost":1,"display":{"row":7,"col":6,"icon":"node_0"},"properties":{},"effects":[],"id":60},{"display_name":"Focus","desc":"When hitting an aggressive mob 5+ blocks away, gain +1 Focus (Max 3). Resets if you miss once","archetype":"Sharpshooter","archetype_req":2,"parents":[68],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":4,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","name":"damMult.Focus"},"scaling":[40],"slider_max":3}],"id":61},{"display_name":"More Focus","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"base_abil":61,"parents":[33,12],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","slider_max":2,"output":{"type":"stat","name":"damMult.Focus"},"scaling":[-5]}],"id":62},{"display_name":"More Focus (2)","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"base_abil":61,"parents":[25,28],"dependencies":[61],"blockers":[],"cost":1,"display":{"row":39,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","slider_max":2,"output":{"type":"stat","name":"damMult.Focus"},"scaling":[-5]}],"id":63},{"display_name":"Traveler","desc":"For every 1% Walk Speed you have from items, gain +1 Raw Spell Damage (Max 100)","parents":[42,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"spd"}],"output":{"type":"stat","name":"sdRaw"},"scaling":[1],"max":100}],"id":64},{"display_name":"Patient Hunter","desc":"Your Traps will deal +20% more damage for every second they are active (Max +80%)","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[40],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":8,"icon":"node_1"},"properties":{"max":80},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","slider_max":4,"output":{"type":"stat","name":"damMult.Basaltic:Basaltic Trap"},"slider_step":1,"scaling":[20]}],"id":65},{"display_name":"Stronger Patient Hunter","desc":"Add +80% Max Damage to Patient Hunter","archetype":"Trapper","archetype_req":0,"base_abil":2,"parents":[26],"dependencies":[65],"blockers":[],"cost":1,"display":{"row":38,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Trap Wait Time","slider_max":4},{"type":"raw_stat","bonuses":[{"type":"prop","abil":65,"name":"max","value":80}]}],"id":66},{"display_name":"Frenzy","desc":"Every time you hit an enemy, briefly gain +6% Walk Speed (Max 200%). Decay -40% of the bonus every second","archetype":"Boltslinger","archetype_req":0,"parents":[59,6],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","output":{"type":"stat","name":"spd"},"scaling":[6],"max":160}],"id":67},{"display_name":"Phantom Ray","desc":"Condense Arrow Storm into a single ray that damages enemies 10 times per second","archetype":"Sharpshooter","archetype_req":0,"base_abil":7,"parents":[37,4],"dependencies":[7],"blockers":[11,6,23],"cost":2,"display":{"row":16,"col":4,"icon":"node_2"},"properties":{},"effects":[{"type":"replace_spell","name":"Phantom Ray","base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[25,0,5,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Arrow":16}}]},{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":68},{"display_name":"Arrow Rain","desc":"When Arrow Shield loses its last charge, unleash 150 arrows raining down on enemies","base_abil":0,"parents":[6,38],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":15,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Per Arrow)","multipliers":[80,0,0,0,0,60]},{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain (Total)","hits":{"Arrow Rain (Per Arrow)":150}}],"id":69},{"display_name":"Decimator","desc":"Phantom Ray will increase its damage by 10% everytime you do not miss with it (Max 70%)","archetype":"Sharpshooter","archetype_req":0,"parents":[49,51],"dependencies":[68],"blockers":[],"cost":1,"display":{"row":34,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Phantom Ray hits","slider_max":7,"output":{"type":"stat","name":"damMult.Decimator:Single Arrow"},"scaling":10}],"id":70}],"Warrior":[{"display_name":"Bash","desc":"Violently bash the ground, dealing high damage in a large area","parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4,"icon":"node_4"},"properties":{"aoe":4,"range":3},"effects":[{"type":"replace_spell","name":"Bash","cost":45,"base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Hit","type":"damage","multipliers":[130,20,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Single Hit":1}}]}],"id":0},{"display_name":"Spear Proficiency 1","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":1},{"display_name":"Cheaper Bash","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[1],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-10}],"id":2},{"display_name":"Double Bash","desc":"Bash will hit a second time at a farther range","parents":[1],"base_abil":0,"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4,"icon":"node_1"},"properties":{"range":3},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","cost":0,"hits":{"Single Hit":1}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","cost":0,"multipliers":[-50,0,0,0,0,0]}],"id":3},{"display_name":"Charge","desc":"Charge forward at high speed (hold shift to cancel)","parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_4"},"properties":{},"effects":[{"type":"replace_spell","name":"Charge","cost":25,"base_spell":2,"spell_type":"damage","scaling":"spell","display":"","parts":[]}],"id":4},{"display_name":"Heavy Impact","desc":"After using Charge, violently crash down into the ground and deal damage","base_abil":4,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":1,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Heavy Impact","cost":0,"multipliers":[100,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Heavy Impact":1}}],"id":5},{"display_name":"Vehement","desc":"For every 1% or 1 Raw Main Attack Damage you have from items, gain +2% Walk Speed (Max 20%)","archetype":"Fallen","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[7],"cost":1,"display":{"row":6,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"mdPct"},{"type":"stat","name":"mdRaw"}],"output":{"type":"stat","name":"spd"},"scaling":[2,2],"max":20}],"id":6},{"display_name":"Tougher Skin","desc":"Harden your skin and become permanently +5% more resistant. For every 1% or 1 Raw Heath Regen you have from items, gain +10 Health (Max 100)","archetype":"Paladin","archetype_req":0,"parents":[4],"dependencies":[],"blockers":[6],"cost":1,"display":{"row":6,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"defMult.Base","value":5}]},{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hprRaw"},{"type":"stat","name":"hprPct"}],"output":{"type":"stat","name":"hpBonus"},"scaling":[10,10],"max":100}],"id":7},{"display_name":"Uppercut","desc":"Rocket enemies in the air and deal massive damage","parents":[6,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":2,"icon":"node_4"},"properties":{"aoe":3,"range":5},"effects":[{"type":"replace_spell","name":"Uppercut","cost":45,"base_spell":3,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Uppercut","multipliers":[200,40,40,0,0,0]},{"name":"Total Damage","hits":{"Uppercut":1}}]}],"id":8},{"display_name":"Cheaper Charge","desc":"Reduce the Mana cost of Charge","base_abil":4,"parents":[8,10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5}],"id":9},{"display_name":"War Scream","desc":"Emit a terrorizing roar that deals damage, pull nearby enemies, and add damage resistance to yourself and allies","parents":[7,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":8,"col":6,"icon":"node_4"},"properties":{"duration":30,"aoe":12,"defense_bonus":10},"effects":[{"type":"replace_spell","name":"War Scream","cost":35,"base_spell":4,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"War Scream","multipliers":[50,0,0,0,50,0]},{"name":"Total Damage","hits":{"War Scream":1}}]}],"id":10},{"display_name":"Earth Mastery","desc":"Increases base damage from all Earth attacks","archetype":"Fallen","archetype_req":0,"parents":[8],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDamAddMin","value":2},{"type":"stat","name":"eDamAddMax","value":4}]}],"id":11},{"display_name":"Thunder Mastery","desc":"Increases base damage from all Thunder attacks","archetype":"Fallen","archetype_req":0,"parents":[8,14,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDamAddMin","value":1},{"type":"stat","name":"tDamAddMax","value":8}]}],"id":12},{"display_name":"Water Mastery","desc":"Increases base damage from all Water attacks","archetype":"Battle Monk","archetype_req":0,"parents":[9,12,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":11,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDamAddMin","value":2},{"type":"stat","name":"wDamAddMax","value":4}]}],"id":13},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Battle Monk","archetype_req":0,"parents":[10,12,9],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDamAddMin","value":3},{"type":"stat","name":"aDamAddMax","value":4}]}],"id":14},{"display_name":"Fire Mastery","desc":"Increases base damage from all Fire attacks","archetype":"Paladin","archetype_req":0,"parents":[10],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDamAddMin","value":3},{"type":"stat","name":"fDamAddMax","value":5}]}],"id":15},{"display_name":"Quadruple Bash","desc":"Bash will hit 4 times at an even larger range","archetype":"Fallen","archetype_req":0,"base_abil":0,"parents":[11,17],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":0,"icon":"node_1"},"properties":{"range":6},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","hits":{"Single Hit":2}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[-20,0,0,0,0,0]}],"id":16},{"display_name":"Fireworks","desc":"Mobs hit by Uppercut will explode mid-air and receive additional damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[12,16],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fireworks","multipliers":[80,0,20,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Fireworks":1}}],"id":17},{"display_name":"Half-Moon Swipe","desc":"Uppercut will deal a footsweep attack at a longer and wider angle. All elemental conversions become Water","archetype":"Battle Monk","archetype_req":1,"base_abil":8,"parents":[13],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":13,"col":4,"icon":"node_1"},"properties":{"range":4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":-10,"multipliers":[-70,0,0,30,0,0]}],"id":18},{"display_name":"Flyby Jab","desc":"Damage enemies in your way when using Charge","base_abil":4,"parents":[14,20],"dependencies":[],"blockers":[],"cost":2,"display":{"row":12,"col":6,"icon":"node_1"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flyby Jab","multipliers":[20,0,0,0,0,40]},{"type":"add_spell_prop","base_spell":2,"target_part":"Contact Damage","display":"Contact Damage","hits":{"Flyby Jab":1}}],"id":19},{"display_name":"Flaming Uppercut","desc":"Uppercut will light mobs on fire, dealing damage every 0.6 seconds","archetype":"Paladin","archetype_req":0,"base_abil":8,"parents":[15,19],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":12,"col":8,"icon":"node_1"},"properties":{"duration":3,"tick":0.6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Flaming Uppercut","multipliers":[0,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Flaming Uppercut Total Damage","hits":{"Flaming Uppercut":5}},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","hits":{"Flaming Uppercut":5}}],"id":20},{"display_name":"Iron Lungs","desc":"War Scream deals more damage","archetype":"Paladin","archetype_req":0,"base_abil":10,"parents":[19,20],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"War Scream","cost":0,"multipliers":[30,0,0,0,0,30]}],"id":21},{"display_name":"Generalist","desc":"After casting 3 different spells in a row, your next spell will cost 5 mana","archetype":"Battle Monk","archetype_req":3,"parents":[23],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":2,"icon":"node_3"},"properties":{},"effects":[],"id":22},{"display_name":"Counter","desc":"When dodging a nearby enemy attack, get 30% chance to instantly attack back","archetype":"Battle Monk","archetype_req":0,"parents":[18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":15,"col":4,"icon":"node_1"},"properties":{"chance":30},"effects":[{"type":"replace_spell","name":"Counter","base_spell":5,"display":"Counter Damage","parts":[{"name":"Counter Damage","multipliers":[60,0,20,0,0,20]}]}],"id":23},{"display_name":"Mantle of the Bovemists","desc":"When casting War Scream, create a holy shield around you that reduces all incoming damage by 70% for 3 hits (20s cooldown)","archetype":"Paladin","archetype_req":3,"parents":[21],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":15,"col":7,"icon":"node_3"},"properties":{"mantle_charge":3},"effects":[{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"defMult.Mantle","value":70}]}],"id":24},{"display_name":"Bak'al's Grasp","desc":"After casting War Scream, become Corrupted (15s Cooldown). You cannot heal while in that state\n\nWhile Corrupted, every 2% of Health you lose will add +4 Raw Damage to your attacks (Max 120)","archetype":"Fallen","archetype_req":2,"parents":[16,17],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":16,"col":1,"icon":"node_3"},"properties":{"cooldown":15},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","slider_max":100,"slider_step":1,"output":{"type":"stat","name":"damRaw"},"scaling":[2]}],"id":25},{"display_name":"Spear Proficiency 2","desc":"Improve your Main Attack's damage and range w/ spear","base_abil":999,"parents":[25,27],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":0,"icon":"node_0"},"properties":{"melee_range":1},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"mdPct","value":5}]}],"id":26},{"display_name":"Cheaper Uppercut","desc":"Reduce the Mana Cost of Uppercut","base_abil":8,"parents":[26,28,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":27},{"display_name":"Aerodynamics","desc":"During Charge, you can steer and change direction","archetype":"Battle Monk","archetype_req":0,"base_abil":4,"parents":[27,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":17,"col":5,"icon":"node_1"},"properties":{},"effects":[],"id":28},{"display_name":"Provoke","desc":"Mobs damaged by War Scream will target only you for at least 5s \n\nReduce the Mana cost of War Scream","archetype":"Paladin","archetype_req":0,"base_abil":10,"parents":[28,24],"dependencies":[],"blockers":[],"cost":1,"display":{"row":17,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":29},{"display_name":"Precise Strikes","desc":"+30% Critical Hit Damage","parents":[27,26],"dependencies":[],"blockers":[],"cost":1,"display":{"row":18,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDamPct","value":30}]}],"id":30},{"display_name":"Air Shout","desc":"War Scream will fire a projectile that can go through walls and deal damage multiple times","base_abil":10,"parents":[28,29],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":18,"col":6,"icon":"node_1"},"properties":{"attackRate":2},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Air Shout","multipliers":[40,0,0,0,0,10]}],"id":31},{"display_name":"Enraged Blow","desc":"While Corriupted, every 1% of Health you lose will increase your damage by +3% (Max 300%)","archetype":"Fallen","archetype_req":0,"base_abil":25,"parents":[26],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":20,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider_name":"Corrupted","output":{"type":"stat","name":"damMult.Enraged"},"scaling":[3]}],"id":32},{"display_name":"Flying Kick","desc":"When using Charge, mobs hit will halt your momentum and get knocked back","archetype":"Battle Monk","archetype_req":1,"base_abil":4,"parents":[27,34],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":3,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick","multipliers":[150,0,0,20,0,30]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Flying Kick":1},"display":"Flying Kick Max Damage"}],"id":33},{"display_name":"Stronger Mantle","desc":"Add +2 additional charges to Mantle of the Bovemists","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[35,33],"dependencies":[24],"blockers":[],"cost":1,"display":{"row":20,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":24,"name":"mantle_charge","value":2}]}],"id":34},{"display_name":"Manachism","desc":"If you receive a hit that's less than 5% of your max HP, gain 10 Mana (1s Cooldown)","archetype":"Paladin","archetype_req":3,"parents":[34,29],"dependencies":[],"blockers":[],"cost":2,"display":{"row":20,"col":8,"icon":"node_2"},"properties":{"cooldown":1},"effects":[],"id":35},{"display_name":"Boiling Blood","desc":"Bash leaves a trail of boiling blood behind its first explosion, slowing down and damaging enemies above it every 0.4 seconds","base_abil":0,"parents":[32,37],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":0,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Boiling Blood","cost":0,"multipliers":[25,0,0,0,5,0]}],"id":36},{"display_name":"Ragnarokkr","desc":"War Scream become deafening, increasing its range and giving damage bonus to players","archetype":"Fallen","archetype_req":0,"base_abil":10,"parents":[36,33],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_2"},"properties":{"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":10},{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"damMult.Ragnarokkr","value":30}]}],"id":37},{"display_name":"Ambidextrous","desc":"Increase your chance to attack with Counter by +30%","base_abil":23,"parents":[33,34,39],"dependencies":[23],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":23,"name":"chance","value":30}]}],"id":38},{"display_name":"Burning Heart","desc":"For every 100 Health Bonus you have from item IDs, gain +2% Fire Damage (Max 100%)","archetype":"Paladin","archetype_req":0,"parents":[38,40],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":false,"inputs":[{"type":"stat","name":"hpBonus"}],"output":{"type":"stat","name":"fDamPct"},"scaling":[0.02],"max":100}],"id":39},{"display_name":"Stronger Bash","desc":"Increase the damage of Bash","base_abil":0,"parents":[39,35],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":8,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","multipliers":[30,0,0,0,0,0]}],"id":40},{"display_name":"Intoxicating Blood","desc":"After leaving Corrupted, gain 2% of the health lost back for each enemy killed while Corrupted","archetype":"Fallen","archetype_req":5,"base_abil":25,"parents":[37,36],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":23,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":41},{"display_name":"Comet","desc":"After being hit by Fireworks, enemies will crash into the ground and receive more damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[37],"dependencies":[17],"blockers":[],"cost":2,"display":{"row":24,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Comet","cost":0,"multipliers":[80,20,0,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"hits":{"Comet":1}}],"id":42},{"display_name":"Collide","desc":"Mobs thrown into walls from Flying Kick will explode and receive additonal damage","archetype":"Battle Monk","archetype_req":4,"base_abil":4,"parents":[38,39],"dependencies":[33],"blockers":[],"cost":2,"display":{"row":23,"col":5,"icon":"node_1"},"properties":{"aoe":4},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Collide","cost":0,"multipliers":[150,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":2,"target_part":"Flying Kick Max Damage","hits":{"Collide":1}}],"id":43},{"display_name":"Rejuvenating Skin","desc":"Regain back 30% of the damage you take as healing over 30s","archetype":"Paladin","archetype_req":5,"parents":[39,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":23,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":44},{"display_name":"Uncontainable Corruption","desc":"Reduce the cooldown of Bak'al's Grasp by -5s, and increase the raw damage gained for every 2% of health lost by +1","base_abil":25,"parents":[36,46],"dependencies":[25],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"stat","name":"damRaw"},"scaling":[0.5]},{"type":"raw_stat","bonuses":[{"type":"prop","abil":25,"name":"cooldown","value":-5}]}],"id":45},{"display_name":"Radiant Devotee","desc":"For every 4% Reflection you have from items, gain +1/5s Mana Regen (Max 10/5s)","archetype":"Battle Monk","archetype_req":1,"parents":[47,45],"dependencies":[],"blockers":[],"cost":1,"display":{"row":26,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"stat_scaling","inputs":[{"type":"stat","name":"ref"}],"output":{"type":"stat","name":"mr"},"scaling":[0.25],"max":10}],"id":46},{"display_name":"Whirlwind Strike","desc":"Uppercut will create a strong gust of air, launching you upward with enemies (Hold shift to stay grounded)","archetype":"Battle Monk","archetype_req":5,"base_abil":8,"parents":[38,46],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":26,"col":4,"icon":"node_1"},"properties":{"range":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","multipliers":[0,0,0,0,0,50]}],"id":47},{"display_name":"Mythril Skin","desc":"Gain +5% Base Resistance and become immune to knockback","archetype":"Paladin","archetype_req":6,"parents":[44],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":7,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"defMult.Base","value":5}]}],"id":48},{"display_name":"Armour Breaker","desc":"While Corrupted, losing 30% Health will make your next Uppercut destroy enemies' defense, rendering them weaker to damage","archetype":"Fallen","archetype_req":0,"base_abil":8,"parents":[45,46],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":27,"col":1,"icon":"node_2"},"properties":{"duration":5},"effects":[{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"damMult.ArmorBreaker","value":30}]}],"id":49},{"display_name":"Shield Strike","desc":"When your Mantle of the Bovemist loses all charges, deal damage around you for each Mantle individually lost","archetype":"Paladin","archetype_req":0,"base_abil":24,"parents":[48,51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"replace_spell","name":"Shield Strike","base_spell":6,"display":"Damage per Shield","parts":[{"name":"Damage per Shield","multipliers":[60,0,20,0,0,0]}]}],"id":50},{"display_name":"Sparkling Hope","desc":"Everytime you heal 5% of your max health, deal damage to all nearby enemies","archetype":"Paladin","archetype_req":0,"parents":[48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":8,"icon":"node_2"},"properties":{"aoe":6},"effects":[{"type":"replace_spell","name":"Sparkling Hope","base_spell":6,"display":"Damage Tick","parts":[{"name":"Damage Tick","multipliers":[10,0,5,0,0,0]}]}],"id":51},{"display_name":"Massive Bash","desc":"While Corrupted, every 3% Health you lose will add +1 AoE to Bash (Max 10)","archetype":"Fallen","archetype_req":8,"base_abil":0,"parents":[53,45],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":0,"icon":"node_2"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"prop","abil":0,"name":"aoe"},"scaling":[0.3333333333333333],"max":10}],"id":52},{"display_name":"Tempest","desc":"War Scream will ripple the ground and deal damage 3 times in a large area","archetype":"Battle Monk","archetype_req":0,"base_abil":10,"parents":[52,54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":2,"icon":"node_1"},"properties":{"aoe":16},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest","multipliers":[30,10,0,0,0,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest Total Damage","hits":{"Tempest":3}},{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","hits":{"Tempest":3}}],"id":53},{"display_name":"Spirit of the Rabbit","desc":"Reduce the Mana cost of Charge and increase your Walk Speed by +20%","archetype":"Battle Monk","archetype_req":5,"base_abil":4,"parents":[53,47],"dependencies":[],"blockers":[],"cost":1,"display":{"row":28,"col":4,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"cost":-5},{"type":"raw_stat","bonuses":[{"type":"stat","name":"spd","value":20}]}],"id":54},{"display_name":"Massacre","desc":"While Corrupted, if your effective attack speed is Slow or lower, hitting an enemy with your Main Attack will add +1% to your Corrupted bar","archetype":"Fallen","archetype_req":5,"base_abil":999,"parents":[53,52],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":1,"icon":"node_1"},"properties":{},"effects":[],"id":55},{"display_name":"Axe Kick","desc":"Increase the damage of Uppercut, but also increase its mana cost","base_abil":8,"parents":[53,54],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":3,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Uppercut","cost":10,"multipliers":[100,0,0,0,0,0]}],"id":56},{"display_name":"Radiance","desc":"Bash will buff your allies' positive IDs. (15s Cooldown)","archetype":"Paladin","archetype_req":2,"base_abil":0,"parents":[54,58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":29,"col":5,"icon":"node_2"},"properties":{"cooldown":15},"effects":[],"id":57},{"display_name":"Cheaper Bash 2","desc":"Reduce the Mana cost of Bash","base_abil":0,"parents":[57,50,51],"dependencies":[],"blockers":[],"cost":1,"display":{"row":29,"col":7,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":58},{"display_name":"Cheaper War Scream","desc":"Reduce the Mana cost of War Scream","base_abil":10,"parents":[52],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":0,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":-5}],"id":59},{"display_name":"Discombobulate","desc":"Every time you hit an enemy, briefly increase your elemental damage dealt to them by +3 (Additive, Max +80). This bonus decays -5 every second","archetype":"Battle Monk","archetype_req":11,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":2,"icon":"node_3"},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","slider_max":27,"output":[{"type":"stat","name":"eDamAddMin"},{"type":"stat","name":"eDamAddMax"},{"type":"stat","name":"tDamAddMin"},{"type":"stat","name":"tDamAddMax"},{"type":"stat","name":"wDamAddMin"},{"type":"stat","name":"wDamAddMax"},{"type":"stat","name":"fDamAddMin"},{"type":"stat","name":"fDamAddMax"},{"type":"stat","name":"aDamAddMin"},{"type":"stat","name":"aDamAddMax"}],"scaling":[3],"max":80}],"id":60},{"display_name":"Thunderclap","desc":"Bash will cast at the player's position and gain additional AoE.\n\n All elemental conversions become Thunder","archetype":"Battle Monk","archetype_req":8,"parents":[62],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":5,"icon":"node_1"},"properties":{},"effects":[{"type":"convert_spell_conv","target_part":"all","conversion":"Thunder"},{"type":"raw_stat","bonuses":[{"type":"prop","abil":0,"name":"aoe","value":3}]}],"id":61},{"display_name":"Cyclone","desc":"After casting War Scream, envelop yourself with a vortex that damages nearby enemies every 0.5s","archetype":"Battle Monk","archetype_req":0,"parents":[54],"dependencies":[],"blockers":[],"cost":2,"display":{"row":31,"col":4,"icon":"node_1"},"properties":{"aoe":4,"duration":20},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone","multipliers":[10,0,0,0,5,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone Total Damage","hits":{"Cyclone":40}}],"id":62},{"display_name":"Second Chance","desc":"When you receive a fatal blow, survive and regain 30% of your Health (10m Cooldown)","archetype":"Paladin","archetype_req":12,"parents":[58],"dependencies":[],"blockers":[],"cost":2,"display":{"row":32,"col":7,"icon":"node_3"},"properties":{},"effects":[],"id":63},{"display_name":"Blood Pact","desc":"If you do not have enough mana to cast a spell, spend health instead (0.6% health per mana)","archetype":"Fallen","archetype_req":10,"parents":[59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_3"},"properties":{"health_cost":0.6},"effects":[],"id":64},{"display_name":"Haemorrhage","desc":"Reduce Blood Pact's health cost. (0.3% health per mana)","archetype":"Fallen","archetype_req":0,"base_abil":64,"parents":[64],"dependencies":[64],"blockers":[],"cost":1,"display":{"row":35,"col":2,"icon":"node_1"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"prop","abil":64,"name":"health_cost","value":-0.3}]}],"id":65},{"display_name":"Brink of Madness","desc":"If your health is 25% full or less, gain +40% Resistance","parents":[64,67],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":4,"icon":"node_2"},"properties":{},"effects":[{"type":"raw_stat","toggle":true,"bonuses":[{"type":"stat","name":"defMult.Brink","value":40}]}],"id":66},{"display_name":"Cheaper Uppercut 2","desc":"Reduce the Mana cost of Uppercut","base_abil":8,"parents":[63,66],"dependencies":[],"blockers":[],"cost":1,"display":{"row":35,"col":6,"icon":"node_0"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"cost":-5}],"id":67},{"display_name":"Martyr","desc":"When you receive a fatal blow, all nearby allies become invincible","archetype":"Paladin","archetype_req":0,"parents":[63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":35,"col":8,"icon":"node_1"},"properties":{"duration":3,"aoe":12},"effects":[],"id":68}]}
\ No newline at end of file
diff --git a/js/build.js b/js/build.js
index fee67cf..03b4bc0 100644
--- a/js/build.js
+++ b/js/build.js
@@ -2,94 +2,9 @@
const classDefenseMultipliers = new Map([ ["relik",0.50], ["bow",0.60], ["wand", 0.80], ["dagger", 1.0], ["spear",1.20], ["sword", 1.10]]);
-/**
- * @description Error to catch items that don't exist.
- * @module ItemNotFound
+/*
+ * Class that represents a wynn player's build.
*/
-class ItemNotFound {
- /**
- * @class
- * @param {String} item the item name entered
- * @param {String} type the type of item
- * @param {Boolean} genElement whether to generate an element from inputs
- * @param {String} override override for item type
- */
- constructor(item, type, genElement, override) {
- /**
- * @public
- * @type {String}
- */
- this.message = `Cannot find ${override||type} named ${item}`;
- if (genElement)
- /**
- * @public
- * @type {Element}
- */
- this.element = document.getElementById(`${type}-choice`).parentElement.querySelectorAll("p.error")[0];
- else
- this.element = document.createElement("div");
- }
-}
-
-/**
- * @description Error to catch incorrect input.
- * @module IncorrectInput
- */
-class IncorrectInput {
- /**
- * @class
- * @param {String} input the inputted text
- * @param {String} format the correct format
- * @param {String} sibling the id of the error node's sibling
- */
- constructor(input, format, sibling) {
- /**
- * @public
- * @type {String}
- */
- this.message = `${input} is incorrect. Example: ${format}`;
- /**
- * @public
- * @type {String}
- */
- this.id = sibling;
- }
-}
-
-/**
- * @description Error that inputs an array of items to generate errors of.
- * @module ListError
- * @extends Error
- */
-class ListError extends Error {
- /**
- * @class
- * @param {Array} errors array of errors
- */
- constructor(errors) {
- let ret = [];
- if (typeof errors[0] == "string") {
- super(errors[0]);
- } else {
- super(errors[0].message);
- }
- for (let i of errors) {
- if (typeof i == "string") {
- ret.push(new Error(i));
- } else {
- ret.push(i);
- }
- }
- /**
- * @public
- * @type {Object[]}
- */
- this.errors = ret;
- }
-}
-
-/*Class that represents a wynn player's build.
-*/
class Build{
/**
@@ -168,7 +83,6 @@ class Build{
//Create a map of this build's stats
let statMap = new Map();
- statMap.set("defMultiplier", 1);
for (const staticID of staticIDs) {
statMap.set(staticID, 0);
@@ -198,8 +112,10 @@ class Build{
}
}
}
- statMap.set('damageMultiplier', 1 + (statMap.get('damMobs') / 100));
- statMap.set('defMultiplier', 1 - (statMap.get('defMobs') / 100));
+ statMap.set('damMult', new Map());
+ statMap.set('defMult', new Map());
+ statMap.get('damMult').set('tome', statMap.get('damMobs'))
+ statMap.get('defMult').set('tome', statMap.get('defMobs'))
statMap.set("activeMajorIDs", major_ids);
for (const [setName, count] of this.activeSetCounts) {
const bonus = sets.get(setName).bonuses[count-1];
diff --git a/js/build_utils.js b/js/build_utils.js
index 5e03b5b..927a265 100644
--- a/js/build_utils.js
+++ b/js/build_utils.js
@@ -16,8 +16,8 @@ function skillPointsToPercentage(skp){
}
// WYNN2: Skillpoint max scaling. Intel is cost reduction
-const skillpoint_final_mult = [1, 1, 0.5, 0.867, 0.951];
-// intel damage and water%
+const skillpoint_final_mult = [1, 1, 0.5/skillPointsToPercentage(150), 0.867, 0.951];
+// intel water%
const skillpoint_damage_mult = [1, 1, 1, 0.867, 0.951];
/*Turns the input amount of levels into skillpoints available.
@@ -74,17 +74,17 @@ let skpReqs = skp_order.map(x => x + "Req");
let item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rSdRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id", "majorIds", "damMobs", "defMobs",
// wynn2 damages.
-"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct"*/,"eDamRaw","eDamAddMin","eDamAddMax",
-"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct"*/,"tDamRaw","tDamAddMin","tDamAddMax",
-"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct"*/,"wDamRaw","wDamAddMin","wDamAddMax",
-"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct"*/,"fDamRaw","fDamAddMin","fDamAddMax",
-"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct"*/,"aDamRaw","aDamAddMin","aDamAddMax",
+"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw","eDamAddMin","eDamAddMax",
+"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct,"*/"tDamRaw","tDamAddMin","tDamAddMax",
+"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct,"*/"wDamRaw","wDamAddMin","wDamAddMax",
+"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct,"*/"fDamRaw","fDamAddMin","fDamAddMax",
+"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw","aDamAddMin","aDamAddMax",
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax", // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
"critDamPct"
];
-// Extra fake IDs (reserved for use in spell damage calculation) : damageMultiplier, defMultiplier, poisonPct, activeMajorIDs
+// Extra fake IDs (reserved for use in spell damage calculation) : damMult, defMult, poisonPct, activeMajorIDs
let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "type", "material", "drop", "quest", "restrict", "category", "atkSpd" ]
//File reading for ID translations for JSON purposes
@@ -169,11 +169,11 @@ let rolledIDs = [
"gXp",
"gSpd",
// wynn2 damages.
-"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct"*/,"eDamRaw","eDamAddMin","eDamAddMax",
-"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct"*/,"tDamRaw","tDamAddMin","tDamAddMax",
-"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct"*/,"wDamRaw","wDamAddMin","wDamAddMax",
-"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct"*/,"fDamRaw","fDamAddMin","fDamAddMax",
-"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct"*/,"aDamRaw","aDamAddMin","aDamAddMax",
+"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw","eDamAddMin","eDamAddMax",
+"tMdPct","tMdRaw","tSdPct","tSdRaw",/*"tDamPct,"*/"tDamRaw","tDamAddMin","tDamAddMax",
+"wMdPct","wMdRaw","wSdPct","wSdRaw",/*"wDamPct,"*/"wDamRaw","wDamAddMin","wDamAddMax",
+"fMdPct","fMdRaw","fSdPct","fSdRaw",/*"fDamPct,"*/"fDamRaw","fDamAddMin","fDamAddMax",
+"aMdPct","aMdRaw","aSdPct","aSdRaw",/*"aDamPct,"*/"aDamRaw","aDamAddMin","aDamAddMax",
"nMdPct","nMdRaw","nSdPct","nSdRaw","nDamPct","nDamRaw","nDamAddMin","nDamAddMax", // neutral which is now an element
/*"mdPct","mdRaw","sdPct","sdRaw",*/"damPct","damRaw","damAddMin","damAddMax", // These are the old ids. Become proportional.
"rMdPct","rMdRaw","rSdPct",/*"rSdRaw",*/"rDamPct","rDamRaw","rDamAddMin","rDamAddMax" // rainbow (the "element" of all minus neutral). rSdRaw is rainraw
@@ -300,3 +300,28 @@ function idRound(id){
return rounded;
}
}
+
+/**
+ * stupid stupid multiplicative stats
+ */
+function merge_stat(stats, name, value) {
+ const start = name.slice(0, 7);
+ if (start === 'damMult' || start === 'defMult') {
+ if (!stats.has(start)) {
+ stats.set(start, new Map());
+ }
+ const map = stats.get(start);
+ if (value instanceof Map) {
+ for (const [k, v] of value.entries()) {
+ merge_stat(map, k, v);
+ }
+ return;
+ }
+ merge_stat(map, name.slice(8), value);
+ return;
+ }
+ if (stats.has(name)) {
+ stats.set(name, stats.get(name) + value);
+ }
+ else { stats.set(name, value); }
+}
diff --git a/js/builder.js b/js/builder.js
index 3c8238c..c99e573 100644
--- a/js/builder.js
+++ b/js/builder.js
@@ -394,6 +394,18 @@ function init() {
for (const eq of equipment_keys) {
document.querySelector("#"+eq+"-tooltip").addEventListener("click", () => collapse_element('#'+eq+'-tooltip'));
}
+ for (let i = 0; i < 5; ++i) {
+ const powder_special = powderSpecialStats[i];
+ const elem_name = damageClasses[i+1]; // skip neutral
+ const elem_char = skp_elements[i]; // TODO: merge?
+ const skp_name = skp_order[i]; // TODO: merge?
+ const boost_parent = document.getElementById(skp_name+'-boost');
+ const slider_id = skp_name+'_boost_armor';
+ const label_name = "% " + elem_name + " Dmg Boost";
+ const slider_container = gen_slider_labeled({label_name: label_name, max: powder_special.cap, id: slider_id, color: elem_colors[i]});
+ boost_parent.appendChild(slider_container);
+ document.getElementById(slider_id).addEventListener("change", (_) => armor_powder_node.mark_dirty().update() );
+ }
// Masonry setup
let masonry = Macy({
diff --git a/js/builder_graph.js b/js/builder_graph.js
index 9687de5..bb0fb63 100644
--- a/js/builder_graph.js
+++ b/js/builder_graph.js
@@ -11,29 +11,7 @@ let armor_powder_node = new (class extends ComputeNode {
}
return statMap;
}
-})().update();
-
-/* Updates PASSIVE powder special boosts (armors)
-*/
-function update_armor_powder_specials(elem_id) {
- //we only update the powder special + external stats if the player has a build
- let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi
-
- //update the label associated w/ the slider
- let elem = document.getElementById(elem_id);
- let label = document.getElementById(elem_id + "_label");
- let value = elem.value;
-
- label.textContent = label.textContent.split(":")[0] + ": " + value
-
- //update the slider's graphics
- let bg_color = elem_colors[skp_order.indexOf(wynn_elem)];
- let pct = Math.round(100 * value / powderSpecialStats[skp_order.indexOf(wynn_elem)].cap);
- elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
-
- armor_powder_node.mark_dirty().update();
-}
-
+})();
let boosts_node = new (class extends ComputeNode {
constructor() { super('builder-boost-input'); }
@@ -50,8 +28,8 @@ let boosts_node = new (class extends ComputeNode {
}
}
let res = new Map();
- res.set('damageMultiplier', 1+damage_boost);
- res.set('defMultiplier', 1-def_boost);
+ res.set('damMult.Potion', 100*damage_boost);
+ res.set('defMult.Potion', 100*def_boost);
return res;
}
})().update();
@@ -511,7 +489,10 @@ function getDefenseStats(stats) {
defenseStats.push(totalHp);
//EHP
let ehp = [totalHp, totalHp];
- let defMult = (2 - stats.get("classDef")) * stats.get("defMultiplier");
+ let defMult = (2 - stats.get("classDef"));
+ for (const [k, v] of stats.get("defMult").entries()) {
+ defMult *= (1 - v/100);
+ }
// newehp = oldehp / [0.1 * A(x) + (1 - A(x)) * (1 - D(x))]
ehp[0] = ehp[0] / (0.1*agi_pct + (1-agi_pct) * (1-def_pct));
ehp[0] /= defMult;
@@ -558,7 +539,6 @@ class SpellDamageCalcNode extends ComputeNode {
const spell = spell_info[0];
const spell_parts = spell_info[1];
const stats = input_map.get('stats');
- const damage_mult = stats.get('damageMultiplier');
const skillpoints = [
stats.get('str'),
stats.get('dex'),
@@ -695,7 +675,7 @@ function getMeleeStats(stats, weapon) {
}
if (weapon_stats.get("type") === "relik") {
- stats.set('damageMultiplier', 0.99); // CURSE YOU WYNNCRAFT
+ merge_stat(stats, 'damMult.ShamanMelee', 0.99); // CURSE YOU WYNNCRAFT
//One day we will create WynnWynn and no longer have shaman 99% melee injustice.
//In all seriousness 99% is because wynn uses 0.33 to estimate dividing the damage by 3 to split damage between 3 beams.
}
@@ -863,18 +843,7 @@ class AggregateStatsNode extends ComputeNode {
const output_stats = new Map();
for (const [k, v] of input_map.entries()) {
for (const [k2, v2] of v.entries()) {
- if (output_stats.has(k2)) {
- // TODO: ugly AF
- if (k2 === 'damageMultiplier' || k2 === 'defMultiplier') {
- output_stats.set(k2, v2 * output_stats.get(k2));
- }
- else {
- output_stats.set(k2, v2 + output_stats.get(k2));
- }
- }
- else {
- output_stats.set(k2, v2);
- }
+ merge_stat(output_stats, k2, v2);
}
}
return output_stats;
@@ -1106,6 +1075,7 @@ function builder_graph_init() {
atree_merge.link_to(build_node, 'build');
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
.link_to(atree_collect_spells, 'spells');
+ atree_stats.link_to(build_node, 'build');
stat_agg_node.link_to(atree_stats, 'atree-stats');
build_encode_node.link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state');
@@ -1116,6 +1086,7 @@ function builder_graph_init() {
for (const input_node of item_nodes.concat(powder_nodes)) {
input_node.update();
}
+ armor_powder_node.update();
level_input.update();
// kinda janky, manually set atree and update. Some wasted compute here
diff --git a/js/damage_calc.js b/js/damage_calc.js
index f1416eb..c236f5d 100644
--- a/js/damage_calc.js
+++ b/js/damage_calc.js
@@ -26,7 +26,7 @@ function get_base_dps(item) {
}
-function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, ignore_speed=false) {
+function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, ignore_speed=false, part=undefined) {
// TODO: Roll all the loops together maybe
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
@@ -154,7 +154,14 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
let total_dam_norm = [0, 0];
let total_dam_crit = [0, 0];
let damages_results = [];
- const damage_mult = stats.get("damageMultiplier");
+ const mult_map = stats.get("damMult");
+ console.log(mult_map);
+ let damage_mult = 1;
+ for (const [k, v] of mult_map.entries()) {
+ damage_mult *= (1 + v/100);
+ }
+ console.log(damage_mult);
+
for (const damage of damages) {
const res = [
diff --git a/js/utils.js b/js/utils.js
index 052a784..c5d39fb 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -753,3 +753,112 @@ function deepcopy(obj) {
}
return ret;
}
+
+
+/**
+ *
+ */
+function gen_slider_labeled({label_name, label_classlist = [], min = 0, max = 100, step = 1, default_val = min, id = undefined, color = "#FFFFFF", classlist = []}) {
+ let slider_container = document.createElement("div");
+ slider_container.classList.add("row");
+
+ let buf_col = document.createElement("div");
+ buf_col.classList.add("col", "mx-1");
+
+ let label = document.createElement("div");
+ label.classList.add("col");
+ label.classList.add(...label_classlist);
+ label.textContent = label_name + ": " + default_val;
+
+ let slider = gen_slider(min, max, step, default_val, id, color, classlist, label);
+
+ //we set IDs here because the slider's id is potentially only meaningful after gen_slider() is called
+ label.id = slider.id + "-label";
+ slider_container.id = slider.id + "-container";
+
+ buf_col.append(slider, label);
+ slider_container.appendChild(buf_col);
+
+ return slider_container;
+}
+
+/** Creates a slider input (input type = range) given styling parameters
+ *
+ * @param {Number | String} min - The minimum value for the slider. defaults to 0
+ * @param {Number | String} max - The maximum value for the slider. defaults to 100
+ * @param {Number | String} step - The granularity between possible values. defaults to 1
+ * @param {Number | String} default_val - The default value to set the slider to.
+ * @param {String} id - The element ID to use for the slider. defaults to the current date time
+ * @param {String} color - The hex color to use for the slider. Needs the # character.
+ * @param {Array} classlist - A list of classes to add to the slider.
+ * @returns
+ */
+function gen_slider(min = 0, max = 100, step = 1, default_val = min, id = undefined, color = "#FFFFFF", classlist = [], label = undefined) {
+ //simple attribute vals
+ let slider = document.createElement("input");
+ slider.type = "range";
+ slider.min = min;
+ slider.max = max;
+ slider.step = step;
+ slider.value = default_val;
+ slider.autocomplete = "off";
+ if (id) {
+ if (document.getElementById(id)) {
+ throw new Error("ID " + id + " already exists within the DOM.")
+ } else {
+ slider.id = id;
+ }
+ } else {
+ slider.id = new Date().toLocaleTimeString();
+ }
+ slider.color = color;
+ slider.classList.add(...classlist); //special spread operator -
+ //necessary for display purposes
+ slider.style.webkitAppearance = "none";
+ slider.style.borderRadius = "30px";
+ slider.style.height = "0.5rem";
+ slider.classList.add("px-0", "slider");
+
+ //set up recoloring
+ slider.addEventListener("change", function(e) {
+ recolor_slider(slider, label);
+ });
+ //do recoloring for the default val
+ let pct = Math.round(100 * (parseInt(slider.value) - parseInt(slider.min)) / (parseInt(slider.max) - parseInt(slider.min)));
+ slider.style.background = `rgba(0, 0, 0, 0) linear-gradient(to right, ${color}, ${color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
+
+ //return slider
+ return slider;
+}
+
+/** Recolors a slider. If the corresponding label exists, also update that.
+ *
+ * @param {slider} slider - the slider element
+ * @param {label} label - the label element
+ */
+function recolor_slider(slider, label) {
+ let color = slider.color;
+ let pct = Math.round(100 * (parseInt(slider.value) - parseInt(slider.min)) / (parseInt(slider.max) - parseInt(slider.min)));
+ slider.style.background = `rgba(0, 0, 0, 0) linear-gradient(to right, ${color}, ${color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
+
+ if (label) {
+ //convention is that the number goes at the end... I parse by separating it at ':'
+ label.textContent = label.textContent.split(":")[0] + ": " + slider.value;
+ }
+}
+
+/**
+ * Shorthand for making an element in html.
+ *
+ * @param {String} type : type of element
+ * @param {List[String]} classlist : css classes for element
+ * @param {Map[String, String]} args : Properties for the element
+ */
+function make_elem(type, classlist = [], args = {}) {
+ const ret_elem = document.createElement(type);
+ ret_elem.classList.add(...classlist);
+ for (const i in args) {
+ ret_elem[i] = args[i];
+ }
+ return ret_elem;
+}
diff --git a/media/atree/node_0.png b/media/atree/node_0.png
index 538b41a..e436ef9 100644
Binary files a/media/atree/node_0.png and b/media/atree/node_0.png differ
diff --git a/media/atree/node_1.png b/media/atree/node_1.png
index 2e1ea97..beebbc4 100644
Binary files a/media/atree/node_1.png and b/media/atree/node_1.png differ
diff --git a/media/atree/node_2.png b/media/atree/node_2.png
index de7be26..c314cc8 100644
Binary files a/media/atree/node_2.png and b/media/atree/node_2.png differ
diff --git a/media/atree/node_3.png b/media/atree/node_3.png
index b55c896..54527df 100644
Binary files a/media/atree/node_3.png and b/media/atree/node_3.png differ
diff --git a/media/atree/node_4.png b/media/atree/node_4.png
index be8c287..d028c2a 100644
Binary files a/media/atree/node_4.png and b/media/atree/node_4.png differ
diff --git a/py_script/atree-generateID.py b/py_script/atree-generateID.py
index 0f06ce3..c720772 100644
--- a/py_script/atree-generateID.py
+++ b/py_script/atree-generateID.py
@@ -45,13 +45,14 @@ def translate_id(id_data, atree_data):
for _input in effect["inputs"]:
if "abil" in _input and _input["abil"] in id_data[_class]:
_input["abil"] = id_data[_class][_input["abil"]]
- if isinstance(effect["output"], list):
- for output in effect["output"]:
- if "abil" in output and output["abil"] in id_data[_class]:
- output["abil"] = id_data[_class][output["abil"]]
- else:
- if "abil" in effect["output"] and effect["output"]["abil"] in id_data[_class]:
- effect["output"]["abil"] = id_data[_class][effect["output"]["abil"]]
+ if "output" in effect:
+ if isinstance(effect["output"], list):
+ for output in effect["output"]:
+ if "abil" in output and output["abil"] in id_data[_class]:
+ output["abil"] = id_data[_class][output["abil"]]
+ else:
+ if "abil" in effect["output"] and effect["output"]["abil"] in id_data[_class]:
+ effect["output"]["abil"] = id_data[_class][effect["output"]["abil"]]
abilDict = {}
with open("atree_constants.js") as f: