diff --git a/js/atree.js b/js/atree.js
index c71140b..91836d9 100644
--- a/js/atree.js
+++ b/js/atree.js
@@ -1,9 +1,130 @@
let abil_points_current;
+/**
+ATreeNode spec:
+
+ATreeNode: {
+ children: List[ATreeNode] // nodes that this node can link to downstream (or sideways)
+ parents: List[ATreeNode] // nodes that can link to this one from upstream (or sideways)
+ ability: atree_node // raw data from atree json
+}
+
+atree_node: {
+ display_name: str
+ id: int
+ desc: str
+ archetype: Optional[str] // not present or empty string = no arch
+ archetype_req: Optional[int] // default: 0
+ base_abil: Optional[int] // Modify another abil? poorly defined...
+ parents: List[int]
+ dependencies: List[int] // Hard reqs
+ blockers: List[int] // If any in here are taken, i am invalid
+ cost: int // cost in AP
+ display: { // stuff for rendering ATree
+ row: int
+ col: int
+ icon: str
+ }
+ properties: Map[str, float] // Dynamic (modifiable) misc. properties; ex. AOE
+ effects: List[effect]
+}
+
+effect: replace_spell | add_spell_prop | convert_spell_conv | raw_stat | stat_scaling
+
+replace_spell: {
+ type: "replace_spell"
+ ... rest of fields are same as `spell` type (see: damage_calc.js)
+}
+
+add_spell_prop: {
+ type: "add_spell_prop"
+ base_spell: int // spell identifier
+ target_part: Optional[str] // Part of the spell to modify. Can be not present/empty for ex. cost modifier.
+ // If target part does not exist, a new part is created.
+ cost: Optional[int] // change to spellcost
+ multipliers: Optional[array[float, 6]] // Additive changes to spellmult (for damage spell)
+ power: Optional[float] // Additive change to healing power (for heal spell)
+ hits: Optional[Map[str, float]] // Additive changes to hits (for total entry)
+ display: Optional[str] // Optional change to the displayed entry. Replaces old
+}
+
+convert_spell_conv: {
+ "type": "convert_spell_conv",
+ "base_spell": int
+ "target_part": "all" | str,
+ "conversion": element_str
+}
+raw_stat: {
+ "type": "raw_stat",
+ "bonuses": list[stat_bonus]
+}
+stat_bonus: {
+ "type": "stat" | "prop",
+ "abil": Optional[int],
+ "name": str,
+ "value": float
+}
+stat_scaling: {
+ "type": "stat_scaling",
+ "slider": bool,
+ "slider_name": Optional[str],
+ "slider_step": Optional[float],
+ "inputs": Optional[list[scaling_target]],
+ "output": scaling_target,
+ "scaling": list[float],
+ "max": float
+}
+scaling_target: {
+ "type": "stat" | "prop",
+ "abil": Optional[int],
+ "name": str
+}
+*/
+
+// TODO: Range numbers
+const default_abils = {
+ wand: [{
+ display_name: "Mage Melee",
+ id: 999,
+ desc: "Mage basic attack.",
+ properties: {range: 5000},
+ effects: [default_spells.wand[0]]
+ }],
+ spear: [{
+ display_name: "Warrior Melee",
+ id: 999,
+ desc: "Warrior basic attack.",
+ properties: {range: 2},
+ effects: [default_spells.spear[0]]
+ }],
+ bow: [{
+ display_name: "Archer Melee",
+ id: 999,
+ desc: "Archer basic attack.",
+ properties: {range: 20},
+ effects: [default_spells.bow[0]]
+ }],
+ dagger: [{
+ display_name: "Assassin Melee",
+ id: 999,
+ desc: "Assassin basic attack.",
+ properties: {range: 2},
+ effects: [default_spells.dagger[0]]
+ }],
+ relik: [{
+ display_name: "Shaman Melee",
+ id: 999,
+ desc: "Shaman basic attack.",
+ properties: {range: 15, speed: 0},
+ effects: [default_spells.relik[0]]
+ }],
+};
+
+
/**
* Update ability tree internal representation. (topologically sorted node list)
*
- * Signature: AbilityTreeUpdateNode(build: Build) => ATree
+ * Signature: AbilityTreeUpdateNode(build: Build) => ATree (List of atree nodes in topological order)
*/
const atree_node = new (class extends ComputeNode {
constructor() { super('builder-atree-update'); }
@@ -18,7 +139,7 @@ const atree_node = new (class extends ComputeNode {
let atree_map = new Map();
let atree_head;
for (const i of atree_raw) {
- atree_map.set(i.id, {children: [], node: i});
+ atree_map.set(i.id, {children: [], ability: i});
if (i.parents.length == 0) {
// Assuming there is only one head.
atree_head = atree_map.get(i.id);
@@ -27,7 +148,7 @@ const atree_node = new (class extends ComputeNode {
for (const i of atree_raw) {
let node = atree_map.get(i.id);
let parents = [];
- for (const parent_id of node.node.parents) {
+ for (const parent_id of node.ability.parents) {
let parent_node = atree_map.get(parent_id);
parent_node.children.push(node);
parents.push(parent_node);
@@ -45,7 +166,7 @@ const atree_node = new (class extends ComputeNode {
/**
* Display ability tree from topologically sorted list.
*
- * Signature: AbilityTreeRenderNode(atree: ATree) => null
+ * Signature: AbilityTreeRenderNode(atree: ATree) => RenderedATree ( Map[id, RenderedATNode] )
*/
const atree_render = new (class extends ComputeNode {
constructor() { super('builder-atree-render'); this.fail_cb = true; }
@@ -55,16 +176,15 @@ const atree_render = new (class extends ComputeNode {
const [atree] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
//for some reason we have to cast to string
- if (atree) { render_AT(document.getElementById("atree-ui"), document.getElementById("atree-active"), atree); }
+ let ret = null;
+ if (atree) { ret = render_AT(document.getElementById("atree-ui"), document.getElementById("atree-active"), atree); }
- if (document.getElementById("toggle-atree").classList.contains("toggleOn")) {
- toggle_tab('atree-dropdown');
- toggleButton('toggle-atree');
- }
+ //Toggle on, previously was toggled off
+ toggle_tab('atree-dropdown'); toggleButton('toggle-atree');
+
+ return ret;
}
-})();
-
-atree_render.link_to(atree_node);
+})().link_to(atree_node);
/**
* Create a reverse topological sort of the tree in the result list.
@@ -86,15 +206,234 @@ function topological_sort_tree(tree, res, mark_state) {
res.push(tree);
}
// these cases are not needed. Case 1 does nothing, case 2 should never happen.
- // else if (state === true) {
- // // permanent mark.
- // return;
- // }
- // else if (state === false) {
- // // temporary mark.
- // }
+ // 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.
+ * I stg if wynn makes abils that modify multiple spells
+ * ... well we can extend this by making `base_abil` a list instead but annoy
+ *
+ * Signature: AbilityTreeMergeNode(atree: ATree, atree-state: RenderedATree) => Map[id, Ability]
+ */
+const atree_merge = new (class extends ComputeNode {
+ constructor() { super('builder-atree-merge'); }
+
+ compute_func(input_map) {
+ const build = input_map.get('build');
+ const atree_state = input_map.get('atree-state');
+ const atree_order = input_map.get('atree');
+
+ let abils_merged = new Map();
+ for (const abil of default_abils[build.weapon.statMap.get('type')]) {
+ let tmp_abil = deepcopy(abil);
+ if (!Array.isArray(tmp_abil.desc)) {
+ tmp_abil.desc = [tmp_abil.desc];
+ }
+ tmp_abil.subparts = [abil.id];
+ abils_merged.set(abil.id, tmp_abil);
+ }
+
+ for (const node of atree_order) {
+ const abil_id = node.ability.id;
+ if (!atree_state.get(abil_id).active) {
+ continue;
+ }
+ 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); }
+
+ 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];
+ }
+ }
+ else {
+ let tmp_abil = deepcopy(abil);
+ if (!Array.isArray(tmp_abil.desc)) {
+ tmp_abil.desc = [tmp_abil.desc];
+ }
+ tmp_abil.subparts = [abil.id];
+ abils_merged.set(abil_id, tmp_abil);
+ }
+ }
+ return abils_merged;
+ }
+})().link_to(atree_node, 'atree').link_to(atree_render, 'atree-state'); // TODO: THIS IS WRONG!!!!! Need one "collect" node...
+
+/**
+ * Collect spells from abilities.
+ *
+ * Signature: AbilityCollectSpellsNode(atree-merged: Map[id, Ability]) => List[Spell]
+ */
+const atree_collect_spells = new (class extends ComputeNode {
+ constructor() { super('atree-spell-collector'); }
+
+ compute_func(input_map) {
+ if (input_map.size !== 1) { throw "AbilityTreeCollectSpellsNode 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
+
+ let ret_spells = new Map();
+ for (const [abil_id, abil] of atree_merged.entries()) {
+ // TODO: Possibly, make a better way for detecting "spell abilities"?
+ if (abil.effects.length == 0 || abil.effects[0].type !== 'replace_spell') { continue; }
+
+ let ret_spell = deepcopy(abil.effects[0]); // NOTE: do not mutate results of previous steps!
+ const base_spell_id = ret_spell.base_spell;
+ for (const effect of abil.effects) {
+ switch (effect.type) {
+ case 'replace_spell':
+ // replace_spell just replaces all (defined) aspects.
+ for (const key in effect) {
+ ret_spell[key] = effect[key];
+ }
+ continue;
+ case 'add_spell_prop': {
+ const { base_spell, target_part = null, cost = 0} = effect;
+ if (base_spell !== base_spell_id) { continue; } // TODO: redundant? if we assume abils only affect one spell
+ ret_spell.cost += cost;
+
+ if (target_part === null) {
+ continue;
+ }
+
+ let found_part = false;
+ for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
+ if (part.name === target_part) {
+ if ('display' in effect) {
+ part.display = effect.display;
+ }
+ if ('multipliers' in effect) {
+ for (const [idx, v] of effect.multipliers.entries()) { // python: enumerate()
+ part.multipliers[idx] += v;
+ }
+ }
+ else if ('power' in effect) {
+ part.power += effect.power;
+ }
+ else if ('hits' in effect) {
+ for (const [idx, v] of Object.entries(effect.hits)) { // looks kinda similar to multipliers case... hmm... can we unify all of these three? (make healpower a list)
+ part.hits[idx] += v;
+ }
+ }
+ else {
+ throw "uhh invalid spell add effect";
+ }
+ found_part = true;
+ break;
+ }
+ }
+ if (!found_part) { // add part.
+ let spell_part = deepcopy(effect);
+ spell_part.name = target_part; // has some extra fields but whatever
+ ret_spell.parts.push(spell_part);
+ }
+ continue;
+ }
+ case 'convert_spell_conv':
+ const { base_spell, target_part, conversion } = effect;
+ if (base_spell !== base_spell_id) { continue; } // TODO: redundant? if we assume abils only affect one spell
+ const elem_idx = damageClasses.indexOf(conversion);
+ let filter = target_part === 'all';
+ for (let part of ret_spell.parts) { // TODO: replace with Map? to avoid this linear search... idk prolly good since its not more verbose to type in json
+ if (filter || part.name === target_part) {
+ if ('multipliers' in part) {
+ let total_conv = 0;
+ for (let i = 1; i < 6; ++i) { // skip neutral
+ total_conv += part.multipliers[i];
+ }
+ let new_conv = [part.multipliers[0], 0, 0, 0, 0, 0];
+ new_conv[elem_idx] = total_conv;
+ part.multipliers = new_conv;
+ }
+ }
+ }
+ continue;
+ }
+ }
+ ret_spells.set(base_spell_id, ret_spell);
+ }
+ return ret_spells;
+ }
+})().link_to(atree_merge, 'atree-merged');
+
+
+/**
+ * Construct compute nodes to link builder items and edit IDs to the appropriate display outputs.
+ * To make things a bit cleaner, the compute graph structure goes like
+ * [builder, build stats] -> [one agg node that is just a passthrough] -> all the spell calc nodes
+ * This way, when things have to be deleted i can just delete one node from the dependencies of builder/build stats...
+ * thats the idea anyway.
+ *
+ * Whenever this is updated, it forces an update of all the newly created spell nodes (if the build is clean).
+ *
+ * Signature: AbilityEnsureSpellsNodes(spells: Map[id, Spell]) => null
+ */
+class AbilityTreeEnsureNodesNode extends ComputeNode {
+
+ /**
+ * Kinda "hyper-node": Constructor takes nodes that should be linked to (build node and stat agg node)
+ */
+ constructor(build_node, stat_agg_node) {
+ super('atree-make-nodes');
+ this.build_node = build_node;
+ this.stat_agg_node = stat_agg_node;
+ // Slight amount of wasted compute to keep internal state non-changing.
+ this.passthrough = new PassThroughNode('atree-make-nodes_internal').link_to(this.build_node, 'build').link_to(this.stat_agg_node, 'stats');
+ this.spelldmg_nodes = []; // debugging use
+ this.spell_display_elem = document.getElementById("all-spells-display");
+ }
+
+ compute_func(input_map) {
+ console.log('atree make nodes');
+ this.passthrough.remove_link(this.build_node);
+ this.passthrough.remove_link(this.stat_agg_node);
+ this.passthrough = new PassThroughNode('atree-make-nodes_internal').link_to(this.build_node, 'build').link_to(this.stat_agg_node, 'stats');
+ this.spell_display_elem.textContent = "";
+ const build_node = this.passthrough.get_node('build'); // aaaaaaaaa performance... savings... help....
+ const stat_agg_node = this.passthrough.get_node('stats');
+
+ const spell_map = input_map.get('spells'); // TODO: is this gonna need more? idk...
+ // TODO shortcut update path for sliders
+
+ for (const [spell_id, spell] of spell_map.entries()) {
+ let spell_node = new SpellSelectNode(spell);
+ spell_node.link_to(build_node, 'build');
+
+ let calc_node = new SpellDamageCalcNode(spell.base_spell);
+ calc_node.link_to(build_node, 'build').link_to(stat_agg_node, 'stats')
+ .link_to(spell_node, 'spell-info');
+ this.spelldmg_nodes.push(calc_node);
+
+ let display_elem = document.createElement('div');
+ display_elem.classList.add("col", "pe-0");
+ // TODO: just pass these elements into the display node instead of juggling the raw IDs...
+ let spell_summary = document.createElement('div'); spell_summary.setAttribute('id', "spell"+spell.base_spell+"-infoAvg");
+ spell_summary.classList.add("col", "spell-display", "spell-expand", "dark-5", "rounded", "dark-shadow", "pt-2", "border", "border-dark");
+ let spell_detail = document.createElement('div'); spell_detail.setAttribute('id', "spell"+spell.base_spell+"-info");
+ spell_detail.classList.add("col", "spell-display", "dark-5", "rounded", "dark-shadow", "py-2");
+ spell_detail.style.display = "none";
+
+ display_elem.appendChild(spell_summary); display_elem.appendChild(spell_detail);
+
+ let display_node = new SpellDisplayNode(spell.base_spell);
+ display_node.link_to(stat_agg_node, 'stats');
+ display_node.link_to(spell_node, 'spell-info');
+ display_node.link_to(calc_node, 'spell-damage');
+
+ this.spell_display_elem.appendChild(display_elem);
+ }
+ this.passthrough.mark_dirty().update(); // Force update once.
+ }
+}
/** The main function for rendering an ability tree.
*
@@ -151,21 +490,21 @@ function render_AT(UI_elem, list_elem, tree) {
let atree_connectors_map = new Map()
let max_row = 0;
for (const i of tree) {
- atree_map.set(i.node.id, {node: i.node, connectors: new Map(), active: false});
- if (i.node.display.row > max_row) {
- max_row = i.node.display.row;
+ atree_map.set(i.ability.id, {ability: i.ability, connectors: new Map(), active: false});
+ if (i.ability.display.row > max_row) {
+ max_row = i.ability.display.row;
}
}
// Copy graph structure.
for (const i of tree) {
- let node_wrapper = atree_map.get(i.node.id);
+ let node_wrapper = atree_map.get(i.ability.id);
node_wrapper.parents = [];
node_wrapper.children = [];
for (const parent of i.parents) {
- node_wrapper.parents.push(atree_map.get(parent.node.id));
+ node_wrapper.parents.push(atree_map.get(parent.ability.id));
}
for (const child of i.children) {
- node_wrapper.children.push(atree_map.get(child.node.id));
+ node_wrapper.children.push(atree_map.get(child.ability.id));
}
}
@@ -184,51 +523,54 @@ function render_AT(UI_elem, list_elem, tree) {
}
for (const _node of tree) {
- let node_wrap = atree_map.get(_node.node.id);
- let node = _node.node;
+ let node_wrap = atree_map.get(_node.ability.id);
+ let ability = _node.ability;
// create connectors based on parent location
for (let parent of node_wrap.parents) {
node_wrap.connectors.set(parent, []);
- let parent_node = parent.node;
- const parent_id = parent_node.id;
+ let parent_abil = parent.ability;
+ const parent_id = parent_abil.id;
let connect_elem = document.createElement("div");
connect_elem.style = "background-size: cover; width: 100%; height: 100%;";
// connect up
- for (let i = node.display.row - 1; i > parent_node.display.row; i--) {
+ for (let i = ability.display.row - 1; i > parent_abil.display.row; i--) {
+ const coord = i + "," + ability.display.col;
let connector = connect_elem.cloneNode();
- node_wrap.connectors.get(parent).push(i + "," + node.display.col);
- resolve_connector(atree_connectors_map, i + "," + node.display.col, {connector: connector, connections: [0, 0, 1, 1]});
+ node_wrap.connectors.get(parent).push(coord);
+ resolve_connector(atree_connectors_map, coord, {connector: connector, connections: [0, 0, 1, 1]});
}
// connect horizontally
- let min = Math.min(parent_node.display.col, node.display.col);
- let max = Math.max(parent_node.display.col, node.display.col);
+ let min = Math.min(parent_abil.display.col, ability.display.col);
+ let max = Math.max(parent_abil.display.col, ability.display.col);
for (let i = min + 1; i < max; i++) {
+ const coord = parent_abil.display.row + "," + i;
let connector = connect_elem.cloneNode();
- node_wrap.connectors.get(parent).push(parent_node.display.row + "," + i);
- resolve_connector(atree_connectors_map, parent_node.display.row + "," + i, {connector: connector, connections: [1, 1, 0, 0]});
+ node_wrap.connectors.get(parent).push(coord);
+ resolve_connector(atree_connectors_map, coord, {connector: connector, connections: [1, 1, 0, 0]});
}
// connect corners
- if (parent_node.display.row != node.display.row && parent_node.display.col != node.display.col) {
+ if (parent_abil.display.row != ability.display.row && parent_abil.display.col != ability.display.col) {
+ const coord = parent_abil.display.row + "," + ability.display.col;
let connector = connect_elem.cloneNode();
- node_wrap.connectors.get(parent).push(parent_node.display.row + "," + node.display.col);
+ node_wrap.connectors.get(parent).push(coord);
let connections = [0, 0, 0, 1];
- if (parent_node.display.col > node.display.col) {
+ if (parent_abil.display.col > ability.display.col) {
connections[1] = 1;
}
else {// if (parent_node.display.col < node.display.col && (parent_node.display.row != node.display.row)) {
connections[0] = 1;
}
- resolve_connector(atree_connectors_map, parent_node.display.row + "," + node.display.col, {connector: connector, connections: connections});
+ resolve_connector(atree_connectors_map, coord, {connector: connector, connections: connections});
}
}
// create node
let node_elem = document.createElement('div');
- let icon = node.display.icon;
+ let icon = ability.display.icon;
if (icon === undefined) {
icon = "node";
}
@@ -263,15 +605,15 @@ function render_AT(UI_elem, list_elem, tree) {
let active_tooltip_title = document.createElement('b');
active_tooltip_title.classList.add("scaled-font");
- active_tooltip_title.innerHTML = node.display_name;
+ active_tooltip_title.innerHTML = ability.display_name;
let active_tooltip_desc = document.createElement('p');
active_tooltip_desc.classList.add("scaled-font-sm", "my-0", "mx-1", "text-wrap");
- active_tooltip_desc.textContent = node.desc;
+ active_tooltip_desc.textContent = ability.desc;
let active_tooltip_cost = document.createElement('p');
active_tooltip_cost.classList.add("scaled-font-sm", "my-0", "mx-1", "text-start");
- active_tooltip_cost.textContent = "Cost: " + node.cost + " AP";
+ active_tooltip_cost.textContent = "Cost: " + ability.cost + " AP";
active_tooltip.appendChild(active_tooltip_title);
active_tooltip.appendChild(active_tooltip_desc);
@@ -279,7 +621,7 @@ function render_AT(UI_elem, list_elem, tree) {
node_tooltip = active_tooltip.cloneNode(true);
- active_tooltip.id = "atree-ab-" + node.id;
+ active_tooltip.id = "atree-ab-" + ability.id;
node_tooltip.style.position = "absolute";
node_tooltip.style.zIndex = "100";
@@ -289,19 +631,21 @@ function render_AT(UI_elem, list_elem, tree) {
node_elem.addEventListener('click', function(e) {
if (e.target !== this && e.target!== this.children[0]) {return;}
- let tooltip = document.getElementById("atree-ab-" + node.id);
- if (tooltip.style.display == "block") {
+ let tooltip = document.getElementById("atree-ab-" + ability.id);
+ if (tooltip.style.display === "block") {
tooltip.style.display = "none";
this.classList.remove("atree-selected");
- abil_points_current -= node.cost;
+ abil_points_current -= ability.cost;
}
else {
tooltip.style.display = "block";
this.classList.add("atree-selected");
- abil_points_current += node.cost;
+ abil_points_current += ability.cost;
};
document.getElementById("active_AP_cost").textContent = abil_points_current;
atree_toggle_state(atree_connectors_map, node_wrap);
+ atree_merge.mark_dirty();
+ atree_merge.update();
});
// add tooltip
@@ -321,10 +665,12 @@ function render_AT(UI_elem, list_elem, tree) {
tooltip.style.display = "none";
});
- document.getElementById("atree-row-" + node.display.row).children[node.display.col].appendChild(node_elem);
+ 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;
};
// resolve connector conflict, when they occupy the same cell.
@@ -379,7 +725,6 @@ function atree_render_connection(atree_connectors_map) {
connector_img.style = "width: 100%; height: 100%;"
connector_elem.replaceChildren(connector_img);
connector_info.highlight = [0, 0, 0, 0];
- console.log(i + ", " + connector_info.type);
let target_elem = document.getElementById("atree-row-" + i.split(",")[0]).children[i.split(",")[1]];
if (target_elem.children.length != 0) {
// janky special case...
@@ -391,7 +736,6 @@ function atree_render_connection(atree_connectors_map) {
// toggle the state of a node.
function atree_toggle_state(atree_connectors_map, node_wrapper) {
- let node = node_wrapper.node;
const new_state = !node_wrapper.active;
node_wrapper.active = new_state
for (const parent of node_wrapper.parents) {
@@ -423,10 +767,10 @@ function atree_update_connector() {
function atree_set_edge(atree_connectors_map, parent, child, state) {
const connectors = child.connectors.get(parent);
- const parent_row = parent.node.display.row;
- const parent_col = parent.node.display.col;
- const child_row = child.node.display.row;
- const child_col = child.node.display.col;
+ const parent_row = parent.ability.display.row;
+ const parent_col = parent.ability.display.col;
+ const child_row = child.ability.display.row;
+ const child_col = child.ability.display.col;
let state_delta = (state ? 1 : -1);
let child_side_idx = (parent_col > child_col ? 0 : 1);
diff --git a/js/atree_constants.js b/js/atree_constants.js
index 93381bb..46997df 100644
--- a/js/atree_constants.js
+++ b/js/atree_constants.js
@@ -3,8 +3,6 @@ const atrees = {
{
"display_name": "Arrow Shield",
"desc": "Create a shield around you that deal damage and knockback mobs when triggered. (2 Charges)",
- "archetype": "",
- "archetype_req": 0,
"parents": ["Power Shots", "Cheaper Escape"],
"dependencies": [],
"blockers": [],
@@ -58,11 +56,10 @@ const atrees = {
"col": 4
},
"properties": {
- "aoe": 0,
- "range": 0
+ "aoe": 0,
+ "range": 0
},
- "effects": [
- {
+ "effects": [{
"type": "replace_spell",
"name": "Escape",
"cost": 25,
@@ -72,41 +69,30 @@ const atrees = {
"scaling": "spell",
"display": "Total Damage",
"parts": [
- {
- "name": "None",
- "type": "damage",
- "multipliers": [0, 0, 0, 0, 0, 0]
- },
- {
- "name": "Total Damage",
- "type": "total",
- "hits": {
- "None": 0
+ {
+ "name": "Total Damage",
+ "type": "total",
+ "hits": {}
}
- }
]
- }
- ]
+ }]
},
{
"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)",
- "archetype": "",
- "archetype_req": 0,
"parents": [],
"dependencies": [],
"blockers": [],
"cost": 1,
"display": {
- "row": 0,
- "col": 4
+ "row": 0,
+ "col": 4
},
"properties": {
- "aoe": 4.5,
- "range": 26
+ "aoe": 4.5,
+ "range": 26
},
- "effects": [
- {
+ "effects": [{
"type": "replace_spell",
"name": "Arrow Bomb",
"cost": 50,
@@ -116,48 +102,41 @@ const atrees = {
"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
+ {
+ "name": "Arrow Bomb",
+ "type": "damage",
+ "multipliers": [160, 0, 0, 0, 20, 0]
+ },
+ {
+ "name": "Total Damage",
+ "type": "total",
+ "hits": {
+ "Arrow Bomb": 1
+ }
}
- }
]
- }
- ]
+ }]
},
{
"display_name": "Heart Shatter",
"desc": "If you hit a mob directly with Arrow Bomb, shatter its heart and deal bonus damage.",
- "archetype": "",
- "archetype_req": 0,
+ "base_abil": "Arrow Bomb",
"parents": ["Bow Proficiency I"],
"dependencies": [],
"blockers": [],
"cost": 1,
"display": {
- "row": 4,
- "col": 4
+ "row": 4,
+ "col": 4
},
"properties": {},
- "effects": [
- {
+ "effects": [{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Arrow Bomb",
"cost": 0,
"multipliers": [100, 0, 0, 0, 0, 0]
- },
- {
-
- }
- ]
+ }]
},
{
"display_name": "Fire Creep",
@@ -377,17 +356,19 @@ const atrees = {
"col": 1
},
"properties": {
- "aoe": 8,
- "duration": 120
- },
- "type": "stat_bonus",
- "bonuses": [
- {
- "type": "stat",
- "name": "spd",
- "value": 20
- }
- ]
+ "aoe": 8,
+ "duration": 120
+ },
+ "effects": [{
+ "type": "stat_bonus",
+ "bonuses": [
+ {
+ "type": "stat",
+ "name": "spd",
+ "value": 20
+ }
+ ]
+ }]
},
{
"display_name": "Basaltic Trap",
@@ -642,7 +623,7 @@ const atrees = {
{
"type": "convert_spell_conv",
"target_part": "all",
- "conversion": "thunder"
+ "conversion": "Thunder"
}
]
},
@@ -714,15 +695,17 @@ const atrees = {
"properties": {
"focus": 1,
"timer": 5
- },
- "type": "stat_bonus",
- "bonuses": [
- {
- "type": "stat",
- "name": "damPct",
- "value": 50
- }
- ]
+ },
+ "effects": [{
+ "type": "stat_bonus",
+ "bonuses": [
+ {
+ "type": "stat",
+ "name": "damPct",
+ "value": 50
+ }
+ ]
+ }]
},
{
"display_name": "Call of the Hound",
@@ -825,7 +808,7 @@ const atrees = {
"base_spell": 5,
"spell_type": "damage",
"scaling": "spell",
- "display": "One Focus",
+ "display": "DPS",
"cost": 0,
"parts": [
@@ -835,7 +818,7 @@ const atrees = {
"multipliers": [10, 0, 0, 5, 0, 0]
},
{
- "name": "One Focus",
+ "name": "DPS",
"type": "total",
"hits": {
"Single Arrow": 20
@@ -845,7 +828,7 @@ const atrees = {
"name": "Total Damage",
"type": "total",
"hits": {
- "One Focus": 7
+ "DPS": 7
}
}
]
@@ -915,13 +898,14 @@ const atrees = {
"blockers": [],
"cost": 2,
"display": {
- "row": 39,
- "col": 2
+ "row": 39,
+ "col": 2
},
"properties": {
- "range": 2.5,
- "slowness": 0.3
- }
+ "range": 2.5,
+ "slowness": 0.3
+ },
+ "effects": []
},
{
"display_name": "All-Seeing Panoptes",
@@ -1263,10 +1247,11 @@ const atrees = {
"display": {
"row": 21,
"col": 7
- },
+ },
"properties": {
"shieldCharges": 2
- }
+ },
+ "effects": []
},
{
"display_name": "Stormy Feet",
@@ -1284,18 +1269,16 @@ const atrees = {
"properties": {
"duration": 60
},
- "effects": [
- {
- "type": "stat_bonus",
- "bonuses": [
- {
- "type": "stat",
- "name": "spdPct",
- "value": 20
- }
+ "effects": [{
+ "type": "stat_bonus",
+ "bonuses": [
+ {
+ "type": "stat",
+ "name": "spdPct",
+ "value": 20
+ }
]
- }
- ]
+ }]
},
{
"display_name": "Refined Gunpowder",
@@ -2028,8 +2011,6 @@ const atrees = {
{
"display_name": "Bash",
"desc": "Violently bash the ground, dealing high damage in a large area",
- "archetype": "",
- "archetype_req": 0,
"parents": [],
"dependencies": [],
"blockers": [],
@@ -2073,8 +2054,7 @@ const atrees = {
{
"display_name": "Spear Proficiency 1",
"desc": "Improve your Main Attack's damage and range w/ spear",
- "archetype": "",
- "archetype_req": 0,
+ "base_abil": 999,
"parents": ["Bash"],
"dependencies": [],
"blockers": [],
@@ -2104,8 +2084,7 @@ const atrees = {
{
"display_name": "Cheaper Bash",
"desc": "Reduce the Mana cost of Bash",
- "archetype": "",
- "archetype_req": 0,
+ "base_abil": "Bash",
"parents": ["Spear Proficiency 1"],
"dependencies": [],
"blockers": [],
@@ -2129,9 +2108,8 @@ const atrees = {
{
"display_name": "Double Bash",
"desc": "Bash will hit a second time at a farther range",
- "archetype": "",
- "archetype_req": 0,
"parents": ["Spear Proficiency 1"],
+ "base_abil": "Bash",
"dependencies": [],
"blockers": [],
"cost": 1,
@@ -2150,8 +2128,7 @@ const atrees = {
"target_part": "Total Damage",
"cost": 0,
"hits": {
- "name": "Single Hit",
- "value": 1
+ "Single Hit": 1
}
},
{
@@ -2167,8 +2144,6 @@ const atrees = {
{
"display_name": "Charge",
"desc": "Charge forward at high speed (hold shift to cancel)",
- "archetype": "",
- "archetype_req": 0,
"parents": ["Double Bash"],
"dependencies": [],
"blockers": [],
@@ -2178,34 +2153,23 @@ const atrees = {
"col": 4,
"icon": "node_4"
},
- "properties": {
- },
- "effects": [
- {
- "type": "replace_spell",
- "name": "Charge",
- "cost": 25,
- "display_text": "Total Damage Average",
- "base_spell": 2,
- "spell_type": "damage",
- "scaling": "spell",
- "display": "Total Damage",
- "parts": [
- {
- "name": "None",
- "type": "damage",
- "multipliers": [0, 0, 0, 0, 0, 0]
- },
- {
- "name": "Total Damage",
- "type": "total",
- "hits": {
- "None": 0
- }
- }
- ]
- }
- ]
+ "properties": {},
+ "effects": [{
+ "type": "replace_spell",
+ "name": "Charge",
+ "cost": 25,
+ "display_text": "Total Damage Average",
+ "base_spell": 2,
+ "spell_type": "damage",
+ "scaling": "spell",
+ "display": "Total Damage",
+ "parts": [
+ {
+ "name": "Total Damage",
+ "hits": {}
+ }
+ ]
+ }]
},
{
@@ -2329,9 +2293,7 @@ const atrees = {
{
"display_name": "Uppercut",
"desc": "Rocket enemies in the air and deal massive damage",
- "archetype": "",
- "archetype_req": 0,
- "parents": ["Vehement"],
+ "parents": ["Vehement", "Cheaper Charge"],
"dependencies": [],
"blockers": [],
"cost": 1,
@@ -2353,19 +2315,15 @@ const atrees = {
"base_spell": 3,
"spell_type": "damage",
"scaling": "spell",
- "display": "total",
+ "display": "Total Damage",
"parts": [
{
"name": "Uppercut",
- "type": "damage",
"multipliers": [150, 50, 50, 0, 0, 0]
},
{
"name": "Total Damage",
- "type": "total",
- "hits": {
- "Uppercut": 1
- }
+ "hits": { "Uppercut": 1 }
}
]
}
@@ -2375,8 +2333,7 @@ const atrees = {
{
"display_name": "Cheaper Charge",
"desc": "Reduce the Mana cost of Charge",
- "archetype": "",
- "archetype_req": 0,
+ "base_abil": "Charge",
"parents": ["Uppercut", "War Scream"],
"dependencies": [],
"blockers": [],
@@ -2400,9 +2357,7 @@ const atrees = {
{
"display_name": "War Scream",
"desc": "Emit a terrorizing roar that deals damage, pull nearby enemies, and add damage resistance to yourself and allies",
- "archetype": "",
- "archetype_req": 0,
- "parents": ["Tougher Skin"],
+ "parents": ["Tougher Skin", "Cheaper Charge"],
"dependencies": [],
"blockers": [],
"cost": 1,
@@ -2429,8 +2384,11 @@ const atrees = {
"parts": [
{
"name": "War Scream",
- "type": "damage",
"multipliers": [50, 0, 0, 0, 50, 0]
+ },
+ {
+ "name": "Total Damage Average",
+ "hits": { "War Scream": 1 }
}
]
}
@@ -2634,7 +2592,6 @@ const atrees = {
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Total Damage",
- "cost": 0,
"hits": {
"Single Hit": 2
}
@@ -2643,7 +2600,6 @@ const atrees = {
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Single Hit",
- "cost": 0,
"multipliers": [-20, 0, 0, 0, 0, 0]
}
]
@@ -2670,14 +2626,12 @@ const atrees = {
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Fireworks",
- "cost": 0,
"multipliers": [80, 0, 20, 0, 0, 0]
},
{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Total Damage",
- "cost": 0,
"hits": {
"Fireworks": 1
}
@@ -2713,7 +2667,7 @@ const atrees = {
{
"type": "convert_spell_conv",
"target_part": "all",
- "conversion": "water"
+ "conversion": "Water"
}
]
},
@@ -2740,7 +2694,6 @@ const atrees = {
"type": "add_spell_prop",
"base_spell": 2,
"target_part": "Flyby Jab",
- "cost": 0,
"multipliers": [20, 0, 0, 0, 0, 40]
}
]
@@ -2769,14 +2722,12 @@ const atrees = {
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Flaming Uppercut",
- "cost": 0,
"multipliers": [0, 0, 0, 0, 50, 0]
},
{
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Flaming Uppercut Total Damage",
- "cost": 0,
"hits": {
"Flaming Uppercut": 5
}
@@ -2785,7 +2736,6 @@ const atrees = {
"type": "add_spell_prop",
"base_spell": 3,
"target_part": "Total Damage",
- "cost": 0,
"hits": {
"Flaming Uppercut": 5
}
@@ -2807,8 +2757,7 @@ const atrees = {
"col": 7,
"icon": "node_0"
},
- "properties": {
- },
+ "properties": {},
"effects": [
{
"type": "add_spell_prop",
@@ -2834,11 +2783,8 @@ const atrees = {
"col": 2,
"icon": "node_3"
},
- "properties": {
- },
- "effects": [
-
- ]
+ "properties": {},
+ "effects": []
},
{
@@ -2860,11 +2806,17 @@ const atrees = {
},
"effects": [
{
- "type": "add_spell_prop",
+ "type": "replace_spell",
+ "name": "Counter",
+ "display_text": "Counter",
"base_spell": 5,
- "target_part": "Counter",
- "cost": 0,
- "multipliers": [60, 0, 20, 0, 0, 20]
+ "display": "Counter Damage",
+ "parts": [
+ {
+ "name": "Counter Damage",
+ "multipliers": [60, 0, 20, 0, 0, 20]
+ }
+ ]
}
]
},
@@ -3646,6 +3598,7 @@ const atrees = {
"desc": "While Corrupted, every 3% Health you lose will add +1 AoE to Bash (Max 10)",
"archetype": "Fallen",
"archetype_req": 8,
+ "base_abil": "Bak'al's Grasp",
"parents": ["Tempest", "Uncontainable Corruption"],
"dependencies": [],
"blockers": [],
@@ -3663,8 +3616,9 @@ const atrees = {
"slider": true,
"slider_name": "Corrupted",
"output": {
- "type": "stat",
- "name": "bashAoE"
+ "type": "prop",
+ "abil": "Bash",
+ "name": "aoe"
},
"scaling": [1],
"max": 10,
@@ -3924,7 +3878,7 @@ const atrees = {
{
"type": "convert_spell_conv",
"target_part": "all",
- "conversion": "thunder"
+ "conversion": "Thunder"
},
{
"type": "raw_stat",
diff --git a/js/atree_constants_min.js b/js/atree_constants_min.js
index f3825a2..628b0fc 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)","archetype":"","archetype_req":0,"parents":[60,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":6},"properties":{"duration":60},"effects":[{"type":"replace_spell","name":"Arrow Shield","cost":30,"display_text":"Max Damage","base_spell":4,"spell_type":"damage","scaling":"spell","display":"","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)","archetype":"","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":7,"col":4},"properties":{"aoe":0,"range":0},"effects":[{"type":"replace_spell","name":"Escape","cost":25,"display_text":"Max Damage","base_spell":2,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"None","type":"damage","multipliers":[0,0,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"None":0}}]}],"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)","archetype":"","archetype_req":0,"parents":[],"dependencies":[],"blockers":[],"cost":1,"display":{"row":0,"col":4},"properties":{"aoe":4.5,"range":26},"effects":[{"type":"replace_spell","name":"Arrow Bomb","cost":50,"display_text":"Average Damage","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.","archetype":"","archetype_req":0,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":4,"col":4},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","cost":0,"multipliers":[100,0,0,0,0,0]},{}],"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.","archetype":"","archetype_req":0,"parents":[68,39,5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":16,"col":6},"properties":{"aoe":0.8,"duration":6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","cost":0,"multipliers":[30,0,0,0,20,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"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.","archetype":"Trapper","archetype_req":1,"parents":[4,35],"dependencies":[7],"blockers":[],"cost":2,"display":{"row":16,"col":8},"properties":{"aoe":2,"duration":5,"slowness":0.4},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Bryophyte Roots","cost":0,"multipliers":[40,20,0,0,0,0]}],"id":5},{"display_name":"Nimble String","desc":"Arrow Storm throw out +6 arrows per stream and shoot twice as fast.","archetype":"","archetype_req":0,"parents":[36,69],"dependencies":[7],"blockers":[68],"cost":2,"display":{"row":15,"col":2},"properties":{"shootspeed":2},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","cost":0,"multipliers":[-15,0,0,0,0,0]},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Stream","cost":0,"hits":{"Single Arrow":6}}],"id":6},{"display_name":"Arrow Storm","desc":"Shoot two stream of 8 arrows, dealing significant damage to close mobs and pushing them back.","archetype":"","archetype_req":0,"parents":[58,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":2},"properties":{"aoe":0,"range":16},"effects":[{"type":"replace_spell","name":"Arrow Storm","cost":40,"display_text":"Max Damage","base_spell":1,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"Single Arrow","type":"damage","multipliers":[30,0,10,0,0,0]},{"name":"Single Stream","type":"total","hits":{"Single Arrow":8}},{"name":"Total Damage","type":"total","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,"parents":[59,67],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":19,"col":1},"properties":{"range":4,"duration":60,"shots":8,"count":2},"effects":[{"type":"replace_spell","name":"Guardian Angels","cost":30,"display_text":"Total Damage Average","base_spell":4,"spell_type":"damage","scaling":"spell","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","base_abil":"Escape","desc":"When casting Escape, give speed to yourself and nearby allies.","archetype":"","archetype_req":0,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":10,"col":1},"properties":{"aoe":8,"duration":120},"type":"stat_bonus","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,"parents":[5],"dependencies":[],"blockers":[],"cost":2,"display":{"row":19,"col":8},"properties":{"aoe":7,"traps":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","cost":0,"multipliers":[140,30,0,0,30,0]}],"id":10},{"display_name":"Windstorm","desc":"Arrow Storm shoot +1 stream of arrows, effectively doubling its damage.","archetype":"","archetype_req":0,"parents":[8,33],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":21,"col":1},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Single Arrow","cost":0,"multipliers":[-10,0,-2,0,0,2]},{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","cost":0,"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":"Escape","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},"properties":{"range":20},"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,"parents":[12,40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":22,"col":6},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","cost":0,"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},"properties":{"range":64,"focusReq":2},"effects":[{"type":"replace_spell","name":"Twain's Arc","cost":0,"display_text":"Twain's Arc","base_spell":5,"spell_type":"damage","scaling":"melee","display":"Twain's Arc Damage","parts":[{"name":"Twain's Arc Damage","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,"parents":[42,64],"dependencies":[],"blockers":[],"cost":2,"display":{"row":26,"col":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":"Total Damage","cost":0,"hits":{"Fierce Stomp":1}}],"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},"properties":{"duration":2,"aoe":0.4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Fire Creep","cost":0,"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},"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,"parents":[14,44,55],"dependencies":[2],"blockers":[],"cost":2,"display":{"row":28,"col":4},"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 4 Mana per second when you stay close to them.","archetype":"Trapper","archetype_req":5,"parents":[43,44],"dependencies":[4],"blockers":[],"cost":2,"display":{"row":28,"col":8},"properties":{"range":12,"manaRegen":4},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","cost":10,"multipliers":[0,0,0,0,0,0]}],"id":19},{"display_name":"Escape Artist","desc":"When casting Escape, release 100 arrows towards the ground.","archetype":"Boltslinger","archetype_req":0,"parents":[46,17],"dependencies":[],"blockers":[12],"cost":2,"display":{"row":31,"col":0},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Escape Artist","cost":0,"multipliers":[30,0,10,0,0,0]}],"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},"properties":{"focus":1,"timer":5},"type":"stat_bonus","bonuses":[{"type":"stat","name":"damPct","value":50}],"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,"parents":[21,47],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":32,"col":7},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Call of the Hound","cost":0,"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,"parents":[48,20],"dependencies":[],"blockers":[68],"cost":2,"display":{"row":33,"col":0},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"target_part":"Total Damage","cost":0,"hits":{"Single Stream":2}}],"id":23},{"display_name":"Geyser Stomp","desc":"Fierce Stomp will create geysers, dealing more damage and vertical knockback.","archetype":"","archetype_req":0,"parents":[56],"dependencies":[15],"blockers":[],"cost":2,"display":{"row":37,"col":1},"properties":{"aoe":1},"effects":[{"type":"add_spell_prop","base_spell":2,"target_part":"Fierce Stomp","cost":0,"multipliers":[0,0,0,50,0,0]}],"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},"properties":{"focusReq":5,"focusRegen":-1},"effects":[{"type":"replace_spell","name":"Crepuscular Ray","base_spell":5,"spell_type":"damage","scaling":"spell","display":"One Focus","cost":0,"parts":[{"name":"Single Arrow","type":"damage","multipliers":[10,0,0,5,0,0]},{"name":"One Focus","type":"total","hits":{"Single Arrow":20}},{"name":"Total Damage","type":"total","hits":{"One Focus":7}}]}],"id":25},{"display_name":"Grape Bomb","desc":"Arrow bomb will throw 3 additional smaller bombs when exploding.","archetype":"","archetype_req":0,"parents":[51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":37,"col":7},"properties":{"miniBombs":3,"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Grape Bomb","cost":0,"multipliers":[30,0,0,0,10,0]}],"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},"properties":{"attackSpeed":0.2},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Tangled Traps","cost":0,"multipliers":[20,0,0,0,0,20]}],"id":27},{"display_name":"Snow Storm","desc":"Enemies near you will be slowed down.","archetype":"","archetype_req":0,"parents":[24,63],"dependencies":[],"blockers":[],"cost":2,"display":{"row":39,"col":2},"properties":{"range":2.5,"slowness":0.3},"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,"parents":[28],"dependencies":[8],"blockers":[],"cost":2,"display":{"row":40,"col":1},"properties":{"range":8,"shots":5},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Arrow","cost":0,"multipliers":[0,0,0,0,10,0]},{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","cost":0,"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,"parents":[26,53],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":40,"col":7},"properties":{"aoe":-2,"traps":6},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Basaltic Trap","cost":0,"multipliers":[-80,0,0,0,0,0]}],"id":30},{"display_name":"Bow Proficiency I","desc":"Improve your Main Attack's damage and range when using a bow.","archetype":"","archetype_req":0,"parents":[2],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":4},"properties":{"mainAtk_range":6},"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.","archetype":"","archetype_req":0,"parents":[31],"dependencies":[],"blockers":[],"cost":1,"display":{"row":2,"col":6},"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.","archetype":"","archetype_req":0,"parents":[12,11,61],"dependencies":[],"blockers":[],"cost":1,"display":{"row":21,"col":3},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":33},{"display_name":"Cheaper Escape","desc":"Reduce the Mana cost of Escape.","archetype":"","archetype_req":0,"parents":[7,0],"dependencies":[],"blockers":[],"cost":1,"display":{"row":9,"col":4},"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},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"eDamPct","value":20},{"type":"stat","name":"eDam","value":[2,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},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"tDamPct","value":10},{"type":"stat","name":"tDam","value":[1,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},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"wDamPct","value":15},{"type":"stat","name":"wDam","value":[2,4]}]}],"id":37},{"display_name":"Air Mastery","desc":"Increases base damage from all Air attacks","archetype":"Battle Monk","archetype_req":0,"parents":[7],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":0},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"aDamPct","value":15},{"type":"stat","name":"aDam","value":[3,4]}]}],"id":38},{"display_name":"Fire Mastery","desc":"Increases base damage from all Earth attacks","archetype":"Sharpshooter","archetype_req":0,"parents":[36,0,34],"dependencies":[],"blockers":[],"cost":1,"display":{"row":13,"col":6},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"fDamPct","value":15},{"type":"stat","name":"fDam","value":[3,5]}]}],"id":39},{"display_name":"More Shields","desc":"Give +2 charges to Arrow Shield.","archetype":"","archetype_req":0,"parents":[12,10],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":21,"col":7},"properties":{"shieldCharges":2},"id":40},{"display_name":"Stormy Feet","desc":"Windy Feet will last longer and add more speed.","archetype":"","archetype_req":0,"parents":[11],"dependencies":[9],"blockers":[],"cost":1,"display":{"row":23,"col":1},"properties":{"duration":60},"effects":[{"type":"stat_bonus","bonuses":[{"type":"stat","name":"spdPct","value":20}]}],"id":41},{"display_name":"Refined Gunpowder","desc":"Increase the damage of Arrow Bomb.","archetype":"","archetype_req":0,"parents":[11],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":0},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Bomb","cost":0,"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":10,"parents":[54],"dependencies":[10],"blockers":[],"cost":1,"display":{"row":26,"col":8},"properties":{"traps":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,"parents":[19,18,14],"dependencies":[0],"blockers":[],"cost":1,"display":{"row":28,"col":6},"properties":{"aoe":1},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Arrow Shield","multipliers":[40,0,0,0,0,0]}],"id":44},{"display_name":"Better Leap","desc":"Reduce leap's cooldown by 1s.","archetype":"Boltslinger","archetype_req":0,"parents":[17,55],"dependencies":[17],"blockers":[],"cost":1,"display":{"row":29,"col":1},"properties":{"cooldown":-1},"id":45},{"display_name":"Better Guardian Angels","desc":"Your Guardian Angels can shoot +4 arrows before disappearing.","archetype":"Boltslinger","archetype_req":0,"parents":[20,55],"dependencies":[8],"blockers":[],"cost":1,"display":{"row":31,"col":2},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Single Bow","cost":0,"hits":{"Single Arrow":4}}],"id":46},{"display_name":"Cheaper Arrow Storm (2)","desc":"Reduce the Mana cost of Arrow Storm.","archetype":"","archetype_req":0,"parents":[21,19],"dependencies":[],"blockers":[],"cost":1,"display":{"row":31,"col":8},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":1,"cost":-5}],"id":47},{"display_name":"Precise Shot","desc":"+30% Critical Hit Damage","archetype":"","archetype_req":0,"parents":[46,49,23],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":2},"properties":{"mainAtk_range":6},"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.","archetype":"","archetype_req":0,"parents":[48,21],"dependencies":[],"blockers":[],"cost":1,"display":{"row":33,"col":4},"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.","archetype":"","archetype_req":0,"parents":[47,21],"dependencies":[2],"blockers":[],"cost":1,"display":{"row":33,"col":6},"properties":{},"id":50},{"display_name":"Cheaper Escape (2)","desc":"Reduce the Mana cost of Escape.","archetype":"","archetype_req":0,"parents":[22,70],"dependencies":[],"blockers":[],"cost":1,"display":{"row":34,"col":7},"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,"parents":[51],"dependencies":[12],"blockers":[],"cost":1,"display":{"row":35,"col":8},"properties":{"range":8},"id":52},{"display_name":"Cheaper Arrow Bomb (2)","desc":"Reduce the Mana cost of Arrow Bomb.","archetype":"","archetype_req":0,"parents":[63,30],"dependencies":[],"blockers":[],"cost":1,"display":{"row":40,"col":5},"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","archetype":"","archetype_req":0,"parents":[40],"dependencies":[],"blockers":[],"cost":2,"display":{"row":25,"col":7},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"hits":{"Arrow Bomb":2}}],"id":54},{"display_name":"Homing Shots","desc":"Your Main Attack arrows will follow nearby enemies and not be affected by gravity","archetype":"","archetype_req":0,"parents":[17,18],"dependencies":[],"blockers":[],"cost":2,"display":{"row":28,"col":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,"parents":[23,48],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":3,"target_part":"Shrapnel Bomb","cost":0,"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},"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,"parents":[1],"dependencies":[],"blockers":[60],"cost":1,"display":{"row":7,"col":2},"properties":{"arrow":2},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee Damage","cost":0,"multipliers":0.7}],"id":58},{"display_name":"Triple Shots","desc":"Triple Main Attack arrows, but they deal -20% damage per arrow","archetype":"Boltslinger","archetype_req":0,"parents":[69,67],"dependencies":[58],"blockers":[],"cost":1,"display":{"row":17,"col":0},"properties":{"arrow":2},"effects":[{"type":"add_spell_prop","base_spell":0,"target_part":"Melee Damage","cost":0,"multipliers":0.7}],"id":59},{"display_name":"Power Shots","desc":"Main Attack arrows have increased speed and knockback","archetype":"Sharpshooter","archetype_req":0,"parents":[1],"dependencies":[],"blockers":[58],"cost":1,"display":{"row":7,"col":6},"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},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","abil_name":"Focus","name":"damMult"},"scaling":[3],"max":3}],"id":61},{"display_name":"More Focus","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"parents":[33,12],"dependencies":[],"blockers":[],"cost":1,"display":{"row":22,"col":4},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","abil_name":"Focus","name":"damMult"},"scaling":[35],"max":5}],"id":62},{"display_name":"More Focus (2)","desc":"Add +2 max Focus","archetype":"Sharpshooter","archetype_req":0,"parents":[25,28],"dependencies":[],"blockers":[],"cost":1,"display":{"row":39,"col":4},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Focus","output":{"type":"stat","abil_name":"Focus","name":"damMult"},"scaling":[35],"max":7}],"id":63},{"display_name":"Traveler","desc":"For every 1% Walk Speed you have from items, gain +1 Raw Spell Damage (Max 100)","archetype":"","archetype_req":0,"parents":[42,14],"dependencies":[],"blockers":[],"cost":1,"display":{"row":25,"col":2},"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,"parents":[40],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":8},"properties":{"max":80},"effects":[],"id":65},{"display_name":"Stronger Patient Hunter","desc":"Add +80% Max Damage to Patient Hunter","archetype":"Trapper","archetype_req":0,"parents":[26],"dependencies":[65],"blockers":[],"cost":1,"display":{"row":38,"col":8},"properties":{"max":80},"effects":[],"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},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Hits dealt","output":{"type":"stat","name":"spd"},"scaling":[6],"max":200}],"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},"properties":{},"effects":[{"type":"replace_spell","name":"Phantom Ray","cost":40,"display_text":"Max Damage","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}}]}],"id":68},{"display_name":"Arrow Rain","desc":"When Arrow Shield loses its last charge, unleash 200 arrows raining down on enemies","archetype":"Trapper","archetype_req":0,"parents":[6,38],"dependencies":[0],"blockers":[],"cost":2,"display":{"row":15,"col":0},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Arrow Rain","cost":0,"multipliers":[120,0,0,0,0,80]}],"id":69},{"display_name":"Decimator","desc":"Phantom Ray will increase its damage by 10% everytime you do not miss with it (Max 50%)","archetype":"Sharpshooter","archetype_req":0,"parents":[49],"dependencies":[68],"blockers":[],"cost":1,"display":{"row":34,"col":5},"properties":{},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Phantom Ray hits","output":{"type":"stat","name":"PhRayDmg"},"scaling":10,"max":50}],"id":70}],"Warrior":[{"display_name":"Bash","desc":"Violently bash the ground, dealing high damage in a large area","archetype":"","archetype_req":0,"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,"display_text":"Total Damage Average","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","archetype":"","archetype_req":0,"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","archetype":"","archetype_req":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","archetype":"","archetype_req":0,"parents":[1],"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":{"name":"Single Hit","value":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)","archetype":"","archetype_req":0,"parents":[3],"dependencies":[],"blockers":[],"cost":1,"display":{"row":6,"col":4,"icon":"node_4"},"properties":{},"effects":[{"type":"replace_spell","name":"Charge","cost":25,"display_text":"Total Damage Average","base_spell":2,"spell_type":"damage","scaling":"spell","display":"Total Damage","parts":[{"name":"None","type":"damage","multipliers":[0,0,0,0,0,0]},{"name":"Total Damage","type":"total","hits":{"None":0}}]}],"id":4},{"display_name":"Heavy Impact","desc":"After using Charge, violently crash down into the ground and deal damage","archetype":"","archetype_req":0,"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]}],"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":[1,1],"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","archetype":"","archetype_req":0,"parents":[6],"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,"display_text":"Total Damage Average","base_spell":3,"spell_type":"damage","scaling":"spell","display":"total","parts":[{"name":"Uppercut","type":"damage","multipliers":[150,50,50,0,0,0]},{"name":"Total Damage","type":"total","hits":{"Uppercut":1}}]}],"id":8},{"display_name":"Cheaper Charge","desc":"Reduce the Mana cost of Charge","archetype":"","archetype_req":0,"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","archetype":"","archetype_req":0,"parents":[7],"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,"display_text":"War Scream","base_spell":4,"spell_type":"damage","scaling":"spell","display":"Total Damage Average","parts":[{"name":"War Scream","type":"damage","multipliers":[50,0,0,0,50,0]}]}],"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":"eDam","value":[2,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":"tDam","value":[1,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":"wDam","value":[2,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":"aDam","value":[3,4]}]}],"id":14},{"display_name":"Fire Mastery","desc":"Increases base damage from all Earth 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":"fDam","value":[3,5]}]}],"id":15},{"display_name":"Quadruple Bash","desc":"Bash will hit 4 times at an even larger range","archetype":"Fallen","archetype_req":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","cost":0,"hits":{"Single Hit":2}},{"type":"add_spell_prop","base_spell":1,"target_part":"Single Hit","cost":0,"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,"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","cost":0,"multipliers":[80,0,20,0,0,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"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,"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,0,0,0]},{"type":"convert_spell_conv","target_part":"all","conversion":"water"}],"id":18},{"display_name":"Flyby Jab","desc":"Damage enemies in your way when using Charge","archetype":"","archetype_req":0,"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","cost":0,"multipliers":[20,0,0,0,0,40]}],"id":19},{"display_name":"Flaming Uppercut","desc":"Uppercut will light mobs on fire, dealing damage every 0.6 seconds","archetype":"Paladin","archetype_req":0,"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","cost":0,"multipliers":[0,0,0,0,50,0]},{"type":"add_spell_prop","base_spell":3,"target_part":"Flaming Uppercut Total Damage","cost":0,"hits":{"Flaming Uppercut":5}},{"type":"add_spell_prop","base_spell":3,"target_part":"Total Damage","cost":0,"hits":{"Flaming Uppercut":5}}],"id":20},{"display_name":"Iron Lungs","desc":"War Scream deals more damage","archetype":"","archetype_req":0,"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":"add_spell_prop","base_spell":5,"target_part":"Counter","cost":0,"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":[],"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":"raw"},"scaling":[4],"slider_step":2,"max":120}],"id":25},{"display_name":"Spear Proficiency 2","desc":"Improve your Main Attack's damage and range w/ spear","archetype":"","archetype_req":0,"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","archetype":"","archetype_req":0,"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,"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,"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","archetype":"","archetype_req":0,"parents":[27,26],"dependencies":[],"blockers":[],"cost":1,"display":{"row":18,"col":2,"icon":"node_0"},"properties":{},"effects":[{"type":"raw_stat","bonuses":[{"type":"stat","name":"critDmg","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","archetype":"","archetype_req":0,"parents":[28,29],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":18,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Air Shout","cost":0,"multipliers":[20,0,0,0,0,5]}],"id":31},{"display_name":"Enraged Blow","desc":"While Corriupted, every 1% of Health you lose will increase your damage by +2% (Max 200%)","archetype":"Fallen","archetype_req":0,"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,"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","cost":0,"multipliers":[120,0,0,10,0,20]}],"id":33},{"display_name":"Stronger Mantle","desc":"Add +2 additional charges to Mantle of the Bovemists","archetype":"Paladin","archetype_req":0,"parents":[35,33],"dependencies":[24],"blockers":[],"cost":1,"display":{"row":20,"col":6,"icon":"node_0"},"properties":{"mantle_charge":2},"effects":[],"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","archetype":"","archetype_req":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,"parents":[36,33],"dependencies":[10],"blockers":[],"cost":2,"display":{"row":22,"col":2,"icon":"node_2"},"properties":{"damage_bonus":30,"aoe":2},"effects":[{"type":"add_spell_prop","base_spell":4,"cost":10}],"id":37},{"display_name":"Ambidextrous","desc":"Increase your chance to attack with Counter by +30%","archetype":"","archetype_req":0,"parents":[33,34,39],"dependencies":[23],"blockers":[],"cost":1,"display":{"row":22,"col":4,"icon":"node_0"},"properties":{"chance":30},"effects":[],"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,"slider_step":100}],"id":39},{"display_name":"Stronger Bash","desc":"Increase the damage of Bash","archetype":"","archetype_req":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","cost":0,"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,"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,"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,"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":[100,0,0,0,50,0]}],"id":43},{"display_name":"Rejuvenating Skin","desc":"Regain back 30% of the damage you take as healing over 30s","archetype":"Paladin","archetype_req":0,"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","archetype":"","archetype_req":0,"parents":[36,46],"dependencies":[25],"blockers":[],"cost":1,"display":{"row":26,"col":0,"icon":"node_0"},"properties":{"cooldown":-5},"effects":[{"type":"stat_scaling","slider":true,"slider_name":"Corrupted","output":{"type":"stat","name":"raw"},"scaling":[1],"slider_step":2,"max":50}],"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":[1],"max":10,"slider_step":4}],"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,"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","cost":0,"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,"parents":[45,46],"dependencies":[25],"blockers":[],"cost":2,"display":{"row":27,"col":1,"icon":"node_2"},"properties":{"duration":5},"effects":[],"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,"parents":[48,51],"dependencies":[],"blockers":[],"cost":2,"display":{"row":27,"col":6,"icon":"node_1"},"properties":{},"effects":[{"type":"add_spell_prop","base_spell":5,"target_part":"Shield Strike","cost":0,"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":"add_spell_prop","base_spell":5,"target_part":"Sparkling Hope","cost":0,"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,"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":"stat","name":"bashAoE"},"scaling":[1],"max":10,"slider_step":3}],"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,"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","cost":"0","multipliers":[30,10,0,0,0,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Tempest Total Damage","cost":"0","hits":{"Tempest":3}},{"type":"add_spell_prop","base_spell":4,"target_part":"Total Damage","cost":"0","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,"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,"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","archetype":"","archetype_req":0,"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,"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","archetype":"","archetype_req":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","archetype":"","archetype_req":0,"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":"rainrawButDifferent"},"scaling":[2],"max":50}],"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_name":"Bash","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":1,"display":{"row":31,"col":4,"icon":"node_1"},"properties":{"aoe":4,"duration":20},"effects":[{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone","cost":0,"multipliers":[10,0,0,0,5,10]},{"type":"add_spell_prop","base_spell":4,"target_part":"Cyclone Total Damage","cost":0,"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 (1% health per mana)","archetype":"","archetype_req":10,"parents":[59],"dependencies":[],"blockers":[],"cost":2,"display":{"row":34,"col":1,"icon":"node_3"},"properties":{},"effects":[],"id":64},{"display_name":"Haemorrhage","desc":"Reduce Blood Pact's health cost. (0.5% health per mana)","archetype":"Fallen","archetype_req":0,"parents":[64],"dependencies":[64],"blockers":[],"cost":1,"display":{"row":35,"col":2,"icon":"node_1"},"properties":{},"effects":[],"id":65},{"display_name":"Brink of Madness","desc":"If your health is 25% full or less, gain +40% Resistance","archetype":"","archetype_req":0,"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","archetype":"","archetype_req":0,"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},properties:{duration:60},effects:[{type:"replace_spell",name:"Arrow Shield",cost:30,display_text:"Max Damage",base_spell:4,spell_type:"damage",scaling:"spell",display:"",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)",archetype:"",archetype_req:0,parents:[3],dependencies:[],blockers:[],cost:1,display:{row:7,col:4},properties:{aoe:0,range:0},effects:[{type:"replace_spell",name:"Escape",cost:25,display_text:"Max Damage",base_spell:2,spell_type:"damage",scaling:"spell",display:"Total Damage",parts:[{name:"Total Damage",type:"total",hits:{}}]}],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},properties:{aoe:4.5,range:26},effects:[{type:"replace_spell",name:"Arrow Bomb",cost:50,display_text:"Average Damage",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},properties:{},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Arrow Bomb",cost:0,multipliers:[100,0,0,0,0,0]}],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.",archetype:"",archetype_req:0,parents:[68,39,5],dependencies:[],blockers:[],cost:2,display:{row:16,col:6},properties:{aoe:.8,duration:6},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Fire Creep",cost:0,multipliers:[30,0,0,0,20,0]},{type:"add_spell_prop",base_spell:3,target_part:"Total Damage",cost:0,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.",archetype:"Trapper",archetype_req:1,parents:[4,35],dependencies:[7],blockers:[],cost:2,display:{row:16,col:8},properties:{aoe:2,duration:5,slowness:.4},effects:[{type:"add_spell_prop",base_spell:1,target_part:"Bryophyte Roots",cost:0,multipliers:[40,20,0,0,0,0]}],id:5},{display_name:"Nimble String",desc:"Arrow Storm throw out +6 arrows per stream and shoot twice as fast.",archetype:"",archetype_req:0,parents:[36,69],dependencies:[7],blockers:[68],cost:2,display:{row:15,col:2},properties:{shootspeed:2},effects:[{type:"add_spell_prop",base_spell:1,target_part:"Single Arrow",cost:0,multipliers:[-15,0,0,0,0,0]},{type:"add_spell_prop",base_spell:1,target_part:"Single Stream",cost:0,hits:{"Single Arrow":6}}],id:6},{display_name:"Arrow Storm",desc:"Shoot two stream of 8 arrows, dealing significant damage to close mobs and pushing them back.",archetype:"",archetype_req:0,parents:[58,34],dependencies:[],blockers:[],cost:1,display:{row:9,col:2},properties:{aoe:0,range:16},effects:[{type:"replace_spell",name:"Arrow Storm",cost:40,display_text:"Max Damage",base_spell:1,spell_type:"damage",scaling:"spell",display:"Total Damage",parts:[{name:"Single Arrow",type:"damage",multipliers:[30,0,10,0,0,0]},{name:"Single Stream",type:"total",hits:{"Single Arrow":8}},{name:"Total Damage",type:"total",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,parents:[59,67],dependencies:[0],blockers:[],cost:2,display:{row:19,col:1},properties:{range:4,duration:60,shots:8,count:2},effects:[{type:"replace_spell",name:"Guardian Angels",cost:30,display_text:"Total Damage Average",base_spell:4,spell_type:"damage",scaling:"spell",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",base_abil:1,desc:"When casting Escape, give speed to yourself and nearby allies.",archetype:"",archetype_req:0,parents:[7],dependencies:[],blockers:[],cost:1,display:{row:10,col:1},properties:{aoe:8,duration:120},effects:[{type:"stat_bonus",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,parents:[5],dependencies:[],blockers:[],cost:2,display:{row:19,col:8},properties:{aoe:7,traps:2},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Basaltic Trap",cost:0,multipliers:[140,30,0,0,30,0]}],id:10},{display_name:"Windstorm",desc:"Arrow Storm shoot +1 stream of arrows, effectively doubling its damage.",archetype:"",archetype_req:0,parents:[8,33],dependencies:[],blockers:[68],cost:2,display:{row:21,col:1},properties:{},effects:[{type:"add_spell_prop",base_spell:1,target_part:"Single Arrow",cost:0,multipliers:[-10,0,-2,0,0,2]},{type:"add_spell_prop",base_spell:1,target_part:"Total Damage",cost:0,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},properties:{range:20},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,parents:[12,40],dependencies:[],blockers:[],cost:2,display:{row:22,col:6},properties:{},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Arrow Bomb",cost:0,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},properties:{range:64,focusReq:2},effects:[{type:"replace_spell",name:"Twain's Arc",cost:0,display_text:"Twain's Arc",base_spell:5,spell_type:"damage",scaling:"melee",display:"Twain's Arc Damage",parts:[{name:"Twain's Arc Damage",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,parents:[42,64],dependencies:[],blockers:[],cost:2,display:{row:26,col: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:"Total Damage",cost:0,hits:{"Fierce Stomp":1}}],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},properties:{duration:2,aoe:.4},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Fire Creep",cost:0,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},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,parents:[14,44,55],dependencies:[2],blockers:[],cost:2,display:{row:28,col:4},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 4 Mana per second when you stay close to them.",archetype:"Trapper",archetype_req:5,parents:[43,44],dependencies:[4],blockers:[],cost:2,display:{row:28,col:8},properties:{range:12,manaRegen:4},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Basaltic Trap",cost:10,multipliers:[0,0,0,0,0,0]}],id:19},{display_name:"Escape Artist",desc:"When casting Escape, release 100 arrows towards the ground.",archetype:"Boltslinger",archetype_req:0,parents:[46,17],dependencies:[],blockers:[12],cost:2,display:{row:31,col:0},properties:{},effects:[{type:"add_spell_prop",base_spell:2,target_part:"Escape Artist",cost:0,multipliers:[30,0,10,0,0,0]}],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},properties:{focus:1,timer:5},effects:[{type:"stat_bonus",bonuses:[{type:"stat",name:"damPct",value:50}]}],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,parents:[21,47],dependencies:[0],blockers:[],cost:2,display:{row:32,col:7},properties:{},effects:[{type:"add_spell_prop",base_spell:4,target_part:"Call of the Hound",cost:0,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,parents:[48,20],dependencies:[],blockers:[68],cost:2,display:{row:33,col:0},properties:{},effects:[{type:"add_spell_prop",base_spell:1,target_part:"Total Damage",cost:0,hits:{"Single Stream":2}}],id:23},{display_name:"Geyser Stomp",desc:"Fierce Stomp will create geysers, dealing more damage and vertical knockback.",archetype:"",archetype_req:0,parents:[56],dependencies:[15],blockers:[],cost:2,display:{row:37,col:1},properties:{aoe:1},effects:[{type:"add_spell_prop",base_spell:2,target_part:"Fierce Stomp",cost:0,multipliers:[0,0,0,50,0,0]}],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},properties:{focusReq:5,focusRegen:-1},effects:[{type:"replace_spell",name:"Crepuscular Ray",base_spell:5,spell_type:"damage",scaling:"spell",display:"DPS",cost:0,parts:[{name:"Single Arrow",type:"damage",multipliers:[10,0,0,5,0,0]},{name:"DPS",type:"total",hits:{"Single Arrow":20}},{name:"Total Damage",type:"total",hits:{DPS:7}}]}],id:25},{display_name:"Grape Bomb",desc:"Arrow bomb will throw 3 additional smaller bombs when exploding.",archetype:"",archetype_req:0,parents:[51],dependencies:[],blockers:[],cost:2,display:{row:37,col:7},properties:{miniBombs:3,aoe:2},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Grape Bomb",cost:0,multipliers:[30,0,0,0,10,0]}],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},properties:{attackSpeed:.2},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Tangled Traps",cost:0,multipliers:[20,0,0,0,0,20]}],id:27},{display_name:"Snow Storm",desc:"Enemies near you will be slowed down.",archetype:"",archetype_req:0,parents:[24,63],dependencies:[],blockers:[],cost:2,display:{row:39,col:2},properties:{range:2.5,slowness:.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,parents:[28],dependencies:[8],blockers:[],cost:2,display:{row:40,col:1},properties:{range:8,shots:5},effects:[{type:"add_spell_prop",base_spell:4,target_part:"Single Arrow",cost:0,multipliers:[0,0,0,0,10,0]},{type:"add_spell_prop",base_spell:4,target_part:"Single Bow",cost:0,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,parents:[26,53],dependencies:[10],blockers:[],cost:2,display:{row:40,col:7},properties:{aoe:-2,traps:6},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Basaltic Trap",cost:0,multipliers:[-80,0,0,0,0,0]}],id:30},{display_name:"Bow Proficiency I",desc:"Improve your Main Attack's damage and range when using a bow.",archetype:"",archetype_req:0,parents:[2],dependencies:[],blockers:[],cost:1,display:{row:2,col:4},properties:{mainAtk_range:6},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.",archetype:"",archetype_req:0,parents:[31],dependencies:[],blockers:[],cost:1,display:{row:2,col:6},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.",archetype:"",archetype_req:0,parents:[12,11,61],dependencies:[],blockers:[],cost:1,display:{row:21,col:3},properties:{},effects:[{type:"add_spell_prop",base_spell:1,cost:-5}],id:33},{display_name:"Cheaper Escape",desc:"Reduce the Mana cost of Escape.",archetype:"",archetype_req:0,parents:[7,0],dependencies:[],blockers:[],cost:1,display:{row:9,col:4},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},properties:{},effects:[{type:"raw_stat",bonuses:[{type:"stat",name:"eDamPct",value:20},{type:"stat",name:"eDam",value:[2,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},properties:{},effects:[{type:"raw_stat",bonuses:[{type:"stat",name:"tDamPct",value:10},{type:"stat",name:"tDam",value:[1,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},properties:{},effects:[{type:"raw_stat",bonuses:[{type:"stat",name:"wDamPct",value:15},{type:"stat",name:"wDam",value:[2,4]}]}],id:37},{display_name:"Air Mastery",desc:"Increases base damage from all Air attacks",archetype:"Battle Monk",archetype_req:0,parents:[7],dependencies:[],blockers:[],cost:1,display:{row:13,col:0},properties:{},effects:[{type:"raw_stat",bonuses:[{type:"stat",name:"aDamPct",value:15},{type:"stat",name:"aDam",value:[3,4]}]}],id:38},{display_name:"Fire Mastery",desc:"Increases base damage from all Earth attacks",archetype:"Sharpshooter",archetype_req:0,parents:[36,0,34],dependencies:[],blockers:[],cost:1,display:{row:13,col:6},properties:{},effects:[{type:"raw_stat",bonuses:[{type:"stat",name:"fDamPct",value:15},{type:"stat",name:"fDam",value:[3,5]}]}],id:39},{display_name:"More Shields",desc:"Give +2 charges to Arrow Shield.",archetype:"",archetype_req:0,parents:[12,10],dependencies:[0],blockers:[],cost:1,display:{row:21,col:7},properties:{shieldCharges:2},effects:[],id:40},{display_name:"Stormy Feet",desc:"Windy Feet will last longer and add more speed.",archetype:"",archetype_req:0,parents:[11],dependencies:[9],blockers:[],cost:1,display:{row:23,col:1},properties:{duration:60},effects:[{type:"stat_bonus",bonuses:[{type:"stat",name:"spdPct",value:20}]}],id:41},{display_name:"Refined Gunpowder",desc:"Increase the damage of Arrow Bomb.",archetype:"",archetype_req:0,parents:[11],dependencies:[],blockers:[],cost:1,display:{row:25,col:0},properties:{},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Arrow Bomb",cost:0,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:10,parents:[54],dependencies:[10],blockers:[],cost:1,display:{row:26,col:8},properties:{traps:2},id:43,effects:[]},{display_name:"Better Arrow Shield",desc:"Arrow Shield will gain additional area of effect, knockback and damage.",archetype:"Sharpshooter",archetype_req:0,parents:[19,18,14],dependencies:[0],blockers:[],cost:1,display:{row:28,col:6},properties:{aoe:1},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Arrow Shield",multipliers:[40,0,0,0,0,0]}],id:44},{display_name:"Better Leap",desc:"Reduce leap's cooldown by 1s.",archetype:"Boltslinger",archetype_req:0,parents:[17,55],dependencies:[17],blockers:[],cost:1,display:{row:29,col:1},properties:{cooldown:-1},id:45,effects:[]},{display_name:"Better Guardian Angels",desc:"Your Guardian Angels can shoot +4 arrows before disappearing.",archetype:"Boltslinger",archetype_req:0,parents:[20,55],dependencies:[8],blockers:[],cost:1,display:{row:31,col:2},properties:{},effects:[{type:"add_spell_prop",base_spell:4,target_part:"Single Bow",cost:0,hits:{"Single Arrow":4}}],id:46},{display_name:"Cheaper Arrow Storm (2)",desc:"Reduce the Mana cost of Arrow Storm.",archetype:"",archetype_req:0,parents:[21,19],dependencies:[],blockers:[],cost:1,display:{row:31,col:8},properties:{},effects:[{type:"add_spell_prop",base_spell:1,cost:-5}],id:47},{display_name:"Precise Shot",desc:"+30% Critical Hit Damage",archetype:"",archetype_req:0,parents:[46,49,23],dependencies:[],blockers:[],cost:1,display:{row:33,col:2},properties:{mainAtk_range:6},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.",archetype:"",archetype_req:0,parents:[48,21],dependencies:[],blockers:[],cost:1,display:{row:33,col:4},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.",archetype:"",archetype_req:0,parents:[47,21],dependencies:[2],blockers:[],cost:1,display:{row:33,col:6},properties:{},id:50,effects:[]},{display_name:"Cheaper Escape (2)",desc:"Reduce the Mana cost of Escape.",archetype:"",archetype_req:0,parents:[22,70],dependencies:[],blockers:[],cost:1,display:{row:34,col:7},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,parents:[51],dependencies:[12],blockers:[],cost:1,display:{row:35,col:8},properties:{range:8},id:52,effects:[]},{display_name:"Cheaper Arrow Bomb (2)",desc:"Reduce the Mana cost of Arrow Bomb.",archetype:"",archetype_req:0,parents:[63,30],dependencies:[],blockers:[],cost:1,display:{row:40,col:5},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",archetype:"",archetype_req:0,parents:[40],dependencies:[],blockers:[],cost:2,display:{row:25,col:7},properties:{},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Total Damage",cost:0,hits:{"Arrow Bomb":2}}],id:54},{display_name:"Homing Shots",desc:"Your Main Attack arrows will follow nearby enemies and not be affected by gravity",archetype:"",archetype_req:0,parents:[17,18],dependencies:[],blockers:[],cost:2,display:{row:28,col: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,parents:[23,48],dependencies:[],blockers:[],cost:2,display:{row:34,col:1},properties:{},effects:[{type:"add_spell_prop",base_spell:3,target_part:"Shrapnel Bomb",cost:0,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},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,parents:[1],dependencies:[],blockers:[60],cost:1,display:{row:7,col:2},properties:{arrow:2},effects:[{type:"add_spell_prop",base_spell:0,target_part:"Melee Damage",cost:0,multipliers:.7}],id:58},{display_name:"Triple Shots",desc:"Triple Main Attack arrows, but they deal -20% damage per arrow",archetype:"Boltslinger",archetype_req:0,parents:[69,67],dependencies:[58],blockers:[],cost:1,display:{row:17,col:0},properties:{arrow:2},effects:[{type:"add_spell_prop",base_spell:0,target_part:"Melee Damage",cost:0,multipliers:.7}],id:59},{display_name:"Power Shots",desc:"Main Attack arrows have increased speed and knockback",archetype:"Sharpshooter",archetype_req:0,parents:[1],dependencies:[],blockers:[58],cost:1,display:{row:7,col:6},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},properties:{},effects:[{type:"stat_scaling",slider:!0,slider_name:"Focus",output:{type:"stat",abil_name:"Focus",name:"damMult"},scaling:[3],max:3}],id:61},{display_name:"More Focus",desc:"Add +2 max Focus",archetype:"Sharpshooter",archetype_req:0,parents:[33,12],dependencies:[],blockers:[],cost:1,display:{row:22,col:4},properties:{},effects:[{type:"stat_scaling",slider:!0,slider_name:"Focus",output:{type:"stat",abil_name:"Focus",name:"damMult"},scaling:[35],max:5}],id:62},{display_name:"More Focus (2)",desc:"Add +2 max Focus",archetype:"Sharpshooter",archetype_req:0,parents:[25,28],dependencies:[],blockers:[],cost:1,display:{row:39,col:4},properties:{},effects:[{type:"stat_scaling",slider:!0,slider_name:"Focus",output:{type:"stat",abil_name:"Focus",name:"damMult"},scaling:[35],max:7}],id:63},{display_name:"Traveler",desc:"For every 1% Walk Speed you have from items, gain +1 Raw Spell Damage (Max 100)",archetype:"",archetype_req:0,parents:[42,14],dependencies:[],blockers:[],cost:1,display:{row:25,col:2},properties:{},effects:[{type:"stat_scaling",slider:!1,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,parents:[40],dependencies:[10],blockers:[],cost:2,display:{row:22,col:8},properties:{max:80},effects:[],id:65},{display_name:"Stronger Patient Hunter",desc:"Add +80% Max Damage to Patient Hunter",archetype:"Trapper",archetype_req:0,parents:[26],dependencies:[65],blockers:[],cost:1,display:{row:38,col:8},properties:{max:80},effects:[],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},properties:{},effects:[{type:"stat_scaling",slider:!0,slider_name:"Hits dealt",output:{type:"stat",name:"spd"},scaling:[6],max:200}],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},properties:{},effects:[{type:"replace_spell",name:"Phantom Ray",cost:40,display_text:"Max Damage",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}}]}],id:68},{display_name:"Arrow Rain",desc:"When Arrow Shield loses its last charge, unleash 200 arrows raining down on enemies",archetype:"Trapper",archetype_req:0,parents:[6,38],dependencies:[0],blockers:[],cost:2,display:{row:15,col:0},properties:{},effects:[{type:"add_spell_prop",base_spell:4,target_part:"Arrow Rain",cost:0,multipliers:[120,0,0,0,0,80]}],id:69},{display_name:"Decimator",desc:"Phantom Ray will increase its damage by 10% everytime you do not miss with it (Max 50%)",archetype:"Sharpshooter",archetype_req:0,parents:[49],dependencies:[68],blockers:[],cost:1,display:{row:34,col:5},properties:{},effects:[{type:"stat_scaling",slider:!0,slider_name:"Phantom Ray hits",output:{type:"stat",name:"PhRayDmg"},scaling:10,max:50}],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,display_text:"Total Damage Average",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,display_text:"Total Damage Average",base_spell:2,spell_type:"damage",scaling:"spell",display:"Total Damage",parts:[{name:"Total Damage",hits:{}}]}],id:4},{display_name:"Heavy Impact",desc:"After using Charge, violently crash down into the ground and deal damage",archetype:"",archetype_req:0,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]}],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:!1,inputs:[{type:"stat",name:"mdPct"},{type:"stat",name:"mdRaw"}],output:{type:"stat",name:"spd"},scaling:[1,1],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:!1,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,display_text:"Total Damage Average",base_spell:3,spell_type:"damage",scaling:"spell",display:"Total Damage",parts:[{name:"Uppercut",multipliers:[150,50,50,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,display_text:"War Scream",base_spell:4,spell_type:"damage",scaling:"spell",display:"Total Damage Average",parts:[{name:"War Scream",multipliers:[50,0,0,0,50,0]},{name:"Total Damage Average",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:"eDam",value:[2,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:"tDam",value:[1,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:"wDam",value:[2,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:"aDam",value:[3,4]}]}],id:14},{display_name:"Fire Mastery",desc:"Increases base damage from all Earth 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:"fDam",value:[3,5]}]}],id:15},{display_name:"Quadruple Bash",desc:"Bash will hit 4 times at an even larger range",archetype:"Fallen",archetype_req: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,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,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,0,0,0]},{type:"convert_spell_conv",target_part:"all",conversion:"Water"}],id:18},{display_name:"Flyby Jab",desc:"Damage enemies in your way when using Charge",archetype:"",archetype_req:0,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]}],id:19},{display_name:"Flaming Uppercut",desc:"Uppercut will light mobs on fire, dealing damage every 0.6 seconds",archetype:"Paladin",archetype_req:0,parents:[15,19],dependencies:[8],blockers:[],cost:2,display:{row:12,col:8,icon:"node_1"},properties:{duration:3,tick:.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:"",archetype_req:0,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",display_text:"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:[],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:!0,slider_name:"Corrupted",output:{type:"stat",name:"raw"},scaling:[4],slider_step:2,max:120}],id:25},{display_name:"Spear Proficiency 2",desc:"Improve your Main Attack's damage and range w/ spear",archetype:"",archetype_req:0,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",archetype:"",archetype_req:0,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,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,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",archetype:"",archetype_req:0,parents:[27,26],dependencies:[],blockers:[],cost:1,display:{row:18,col:2,icon:"node_0"},properties:{},effects:[{type:"raw_stat",bonuses:[{type:"stat",name:"critDmg",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",archetype:"",archetype_req:0,parents:[28,29],dependencies:[10],blockers:[],cost:2,display:{row:18,col:6,icon:"node_1"},properties:{},effects:[{type:"add_spell_prop",base_spell:4,target_part:"Air Shout",cost:0,multipliers:[20,0,0,0,0,5]}],id:31},{display_name:"Enraged Blow",desc:"While Corriupted, every 1% of Health you lose will increase your damage by +2% (Max 200%)",archetype:"Fallen",archetype_req:0,parents:[26],dependencies:[25],blockers:[],cost:2,display:{row:20,col:0,icon:"node_2"},properties:{},effects:[{type:"stat_scaling",slider:!1,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,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",cost:0,multipliers:[120,0,0,10,0,20]}],id:33},{display_name:"Stronger Mantle",desc:"Add +2 additional charges to Mantle of the Bovemists",archetype:"Paladin",archetype_req:0,parents:[35,33],dependencies:[24],blockers:[],cost:1,display:{row:20,col:6,icon:"node_0"},properties:{mantle_charge:2},effects:[],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",archetype:"",archetype_req: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,parents:[36,33],dependencies:[10],blockers:[],cost:2,display:{row:22,col:2,icon:"node_2"},properties:{damage_bonus:30,aoe:2},effects:[{type:"add_spell_prop",base_spell:4,cost:10}],id:37},{display_name:"Ambidextrous",desc:"Increase your chance to attack with Counter by +30%",archetype:"",archetype_req:0,parents:[33,34,39],dependencies:[23],blockers:[],cost:1,display:{row:22,col:4,icon:"node_0"},properties:{chance:30},effects:[],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:!1,inputs:[{type:"stat",name:"hpBonus"}],output:{type:"stat",name:"fDamPct"},scaling:[2],max:100,slider_step:100}],id:39},{display_name:"Stronger Bash",desc:"Increase the damage of Bash",archetype:"",archetype_req: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",cost:0,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,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,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,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:[100,0,0,0,50,0]}],id:43},{display_name:"Rejuvenating Skin",desc:"Regain back 30% of the damage you take as healing over 30s",archetype:"Paladin",archetype_req:0,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",archetype:"",archetype_req:0,parents:[36,46],dependencies:[25],blockers:[],cost:1,display:{row:26,col:0,icon:"node_0"},properties:{cooldown:-5},effects:[{type:"stat_scaling",slider:!0,slider_name:"Corrupted",output:{type:"stat",name:"raw"},scaling:[1],slider_step:2,max:50}],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:[1],max:10,slider_step:4}],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,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",cost:0,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,parents:[45,46],dependencies:[25],blockers:[],cost:2,display:{row:27,col:1,icon:"node_2"},properties:{duration:5},effects:[],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,parents:[48,51],dependencies:[],blockers:[],cost:2,display:{row:27,col:6,icon:"node_1"},properties:{},effects:[{type:"add_spell_prop",base_spell:5,target_part:"Shield Strike",cost:0,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:"add_spell_prop",base_spell:5,target_part:"Sparkling Hope",cost:0,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:25,parents:[53,45],dependencies:[],blockers:[],cost:2,display:{row:28,col:0,icon:"node_2"},properties:{},effects:[{type:"stat_scaling",slider:!0,slider_name:"Corrupted",output:{type:"prop",abil:0,name:"aoe"},scaling:[1],max:10,slider_step:3}],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,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",cost:"0",multipliers:[30,10,0,0,0,10]},{type:"add_spell_prop",base_spell:4,target_part:"Tempest Total Damage",cost:"0",hits:{Tempest:3}},{type:"add_spell_prop",base_spell:4,target_part:"Total Damage",cost:"0",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,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,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",archetype:"",archetype_req:0,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,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",archetype:"",archetype_req: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",archetype:"",archetype_req:0,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:!0,slider_name:"Hits dealt",output:{type:"stat",name:"rainrawButDifferent"},scaling:[2],max:50}],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_name:"Bash",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:1,display:{row:31,col:4,icon:"node_1"},properties:{aoe:4,duration:20},effects:[{type:"add_spell_prop",base_spell:4,target_part:"Cyclone",cost:0,multipliers:[10,0,0,0,5,10]},{type:"add_spell_prop",base_spell:4,target_part:"Cyclone Total Damage",cost:0,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 (1% health per mana)",archetype:"",archetype_req:10,parents:[59],dependencies:[],blockers:[],cost:2,display:{row:34,col:1,icon:"node_3"},properties:{},effects:[],id:64},{display_name:"Haemorrhage",desc:"Reduce Blood Pact's health cost. (0.5% health per mana)",archetype:"Fallen",archetype_req:0,parents:[64],dependencies:[64],blockers:[],cost:1,display:{row:35,col:2,icon:"node_1"},properties:{},effects:[],id:65},{display_name:"Brink of Madness",desc:"If your health is 25% full or less, gain +40% Resistance",archetype:"",archetype_req:0,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",archetype:"",archetype_req:0,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}]}
diff --git a/js/builder.js b/js/builder.js
index 5482506..3c8238c 100644
--- a/js/builder.js
+++ b/js/builder.js
@@ -149,17 +149,6 @@ function toggle_tab(tab) {
}
}
-// toggle spell arrow
-function toggle_spell_tab(tab) {
- let arrow_img = document.querySelector("#" + "arrow_" + tab + "Avg");
- if (document.querySelector("#"+tab).style.display == "none") {
- document.querySelector("#"+tab).style.display = "";
- arrow_img.src = arrow_img.src.replace("down", "up");
- } else {
- document.querySelector("#"+tab).style.display = "none";
- arrow_img.src = arrow_img.src.replace("up", "down");
- }
-}
function toggle_boost_tab(tab) {
for (const i of skp_order) {
@@ -396,16 +385,12 @@ function collapse_element(elmnt) {
document.querySelector(elmnt).style.removeProperty('display');
}
-// TODO: Learn and use await
function init() {
console.log("builder.js init");
init_autocomplete();
// Other "main" stuff
// Spell dropdowns
- for (const i of spell_disp) {
- document.querySelector("#"+i+"Avg").addEventListener("click", () => toggle_spell_tab(i));
- }
for (const eq of equipment_keys) {
document.querySelector("#"+eq+"-tooltip").addEventListener("click", () => collapse_element('#'+eq+'-tooltip'));
}
diff --git a/js/builder_graph.js b/js/builder_graph.js
index b371052..53a8d4d 100644
--- a/js/builder_graph.js
+++ b/js/builder_graph.js
@@ -468,41 +468,17 @@ class PowderInputNode extends InputNode {
* Signature: SpellSelectNode
(build: Build) => [Spell, SpellParts]
*/
class SpellSelectNode extends ComputeNode {
- // TODO: rewrite me entirely...
- constructor(spell_num) {
- super("builder-spell"+spell_num+"-select");
- this.spell_idx = spell_num;
+ constructor(spell) {
+ super("builder-spell"+spell.base_spell+"-select");
+ this.spell = spell;
}
compute_func(input_map) {
const build = input_map.get('build');
let stats = build.statMap;
+ // TODO: apply major ids... DOOM.....
- const i = this.spell_idx;
- const spells = default_spells[build.weapon.statMap.get("type")];
- let spell;
- for (const _spell of spells) {
- if (_spell.base_spell === i) {
- spell = _spell;
- break;
- }
- }
- if (spell === undefined) { return null; }
-
- let spell_parts;
- if (spell.parts) {
- spell_parts = spell.parts;
- }
- else {
- spell_parts = spell.variants.DEFAULT;
- for (const majorID of stats.get("activeMajorIDs")) {
- if (majorID in spell.variants) {
- spell_parts = spell.variants[majorID];
- break;
- }
- }
- }
- return [spell, spell_parts];
+ return [this.spell, this.spell.parts];
}
}
@@ -995,15 +971,15 @@ class SumNumberInputNode extends InputNode {
let item_nodes = [];
let powder_nodes = [];
-let spelldmg_nodes = [];
let edit_input_nodes = [];
let skp_inputs = [];
let build_node;
let stat_agg_node;
let edit_agg_node;
+let atree_graph_creator;
function builder_graph_init() {
- // Phase 1/2: Set up item input, propagate updates, etc.
+ // Phase 1/3: Set up item input, propagate updates, etc.
// Bind item input fields to input nodes, and some display stuff (for auto colorizing stuff).
for (const [eq, display_elem, none_item] of zip3(equipment_fields, build_fields, none_items)) {
@@ -1060,7 +1036,7 @@ function builder_graph_init() {
item_nodes[3].link_to(powder_nodes[3], 'powdering');
item_nodes[8].link_to(powder_nodes[4], 'powdering');
- // Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
+ // Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
let build_disp_node = new BuildDisplayNode()
build_disp_node.link_to(build_node, 'build');
@@ -1092,8 +1068,18 @@ function builder_graph_init() {
}
stat_agg_node.link_to(edit_agg_node);
build_disp_node.link_to(stat_agg_node, 'stats');
- atree_node.link_to(build_node, 'build');
+ // Phase 3/3: Set up atree stuff.
+
+ // These two are defined in `atree.js`
+ atree_node.link_to(build_node, 'build');
+ atree_merge.link_to(build_node, 'build');
+ atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
+ .link_to(atree_collect_spells, 'spells');
+
+ // ---------------------------------------------------------------
+ // Trigger the update cascade for build!
+ // ---------------------------------------------------------------
for (const input_node of item_nodes.concat(powder_nodes)) {
input_node.update();
}
@@ -1112,23 +1098,6 @@ function builder_graph_init() {
// Also do something similar for skill points
- //for (let i = 0; i < 4; ++i) { TODO: testing code
- for (let i = 0; i < 1; ++i) {
- let spell_node = new SpellSelectNode(i);
- spell_node.link_to(build_node, 'build');
- // TODO: link and rewrite spell_node to the stat agg node
- spell_node.link_to(stat_agg_node, 'stats')
-
- let calc_node = new SpellDamageCalcNode(i);
- calc_node.link_to(build_node, 'build').link_to(stat_agg_node, 'stats')
- .link_to(spell_node, 'spell-info');
- spelldmg_nodes.push(calc_node);
-
- let display_node = new SpellDisplayNode(i);
- display_node.link_to(stat_agg_node, 'stats'); // TODO: same here..
- display_node.link_to(spell_node, 'spell-info');
- display_node.link_to(calc_node, 'spell-damage');
- }
for (const node of edit_input_nodes) {
node.update();
}
diff --git a/js/computation_graph.js b/js/computation_graph.js
index a3903eb..d0d5b7a 100644
--- a/js/computation_graph.js
+++ b/js/computation_graph.js
@@ -112,7 +112,7 @@ class ComputeNode {
const idx = this.inputs.indexOf(parent_node); // Get idx
this.inputs.splice(idx, 1); // remove element
- this.input_translations.delete(parent_node.name);
+ this.input_translation.delete(parent_node.name);
const was_dirty = this.inputs_dirty.get(parent_node.name);
this.inputs_dirty.delete(parent_node.name);
if (was_dirty) {
@@ -173,3 +173,42 @@ class InputNode extends ComputeNode {
return this.input_field.value;
}
}
+
+/**
+ * Passthrough node for simple aggregation.
+ * Unfortunately if you use this too much you get layers and layers of maps...
+ *
+ * Signature: PassThroughNode(**kwargs) => Map[...]
+ */
+class PassThroughNode extends ComputeNode {
+ constructor(name) {
+ super(name);
+ this.breakout_nodes = new Map();
+ }
+
+ compute_func(input_map) {
+ return input_map;
+ }
+
+ /**
+ * Get a ComputeNode that will "break out" one part of this aggregation input.
+ * There is some overhead to this operation because ComputeNode is not exactly a free abstraction... oof
+ * Also you will recv updates whenever any input that is part of the aggregation changes even
+ * if the specific sub-input didn't change.
+ *
+ * Parameters:
+ * sub-input: The key to listen to
+ */
+ get_node(sub_input) {
+ if (this.breakout_nodes.has(sub_input)) {
+ return this.breakout_nodes.get(sub_input);
+ }
+ const _name = this.name;
+ const ret = new (class extends ComputeNode {
+ constructor() { super('passthrough-'+_name+'-'+sub_input); }
+ compute_func(input_map) { return input_map.get(_name).get(sub_input); }
+ })().link_to(this);
+ this.breakout_nodes.set(sub_input, ret);
+ return ret;
+ }
+}
diff --git a/js/damage_calc.js b/js/damage_calc.js
index 95d5f92..aae9737 100644
--- a/js/damage_calc.js
+++ b/js/damage_calc.js
@@ -61,7 +61,7 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
if (conversions[i] > 0) {
const conv_frac = conversions[i]/100;
damages[i][0] += conv_frac * weapon_min;
- damages[i][1] += conf_frac * weapon_max;
+ damages[i][1] += conv_frac * weapon_max;
present[i] = true;
total_convert += conv_frac
}
@@ -240,6 +240,7 @@ spell_heal: {
const default_spells = {
wand: [{
+ type: "replace_spell", // not needed but makes this usable as an "abil part"
name: "Wand Melee", // TODO: name for melee attacks?
display_text: "Mage basic attack",
base_spell: 0,
@@ -247,7 +248,7 @@ const default_spells = {
display: "Melee",
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}, {
- name: "Heal", // TODO: name for melee attacks?
+ name: "Heal", // TODO: name for melee attacks? // JUST FOR TESTING...
display_text: "Heal spell!",
base_spell: 1,
display: "Total Heal",
@@ -258,6 +259,7 @@ const default_spells = {
]
}],
spear: [{
+ type: "replace_spell", // not needed but makes this usable as an "abil part"
name: "Melee", // TODO: name for melee attacks?
display_text: "Warrior basic attack",
base_spell: 0,
@@ -266,6 +268,7 @@ const default_spells = {
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}],
bow: [{
+ type: "replace_spell", // not needed but makes this usable as an "abil part"
name: "Bow Shot", // TODO: name for melee attacks?
display_text: "Archer basic attack",
base_spell: 0,
@@ -274,6 +277,7 @@ const default_spells = {
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}],
dagger: [{
+ type: "replace_spell", // not needed but makes this usable as an "abil part"
name: "Melee", // TODO: name for melee attacks?
display_text: "Assassin basic attack",
base_spell: 0,
@@ -282,6 +286,7 @@ const default_spells = {
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}],
relik: [{
+ type: "replace_spell", // not needed but makes this usable as an "abil part"
name: "Relik Melee", // TODO: name for melee attacks?
display_text: "Shaman basic attack",
base_spell: 0,
diff --git a/js/display.js b/js/display.js
index c3e52e4..0e5ba09 100644
--- a/js/display.js
+++ b/js/display.js
@@ -33,9 +33,8 @@ function displaySetBonuses(parent_id,build) {
set_summary_elem.append(set_elem);
const bonus = active_set.bonuses[count-1];
- let mock_item = new Map();
- mock_item.set("fixID", true);
- mock_item.set("displayName", setName+" Set: "+count+"/"+sets.get(setName).items.length);
+ let mock_item = new Map([["fixID", true],
+ ["displayName", setName+" Set: "+count+"/"+sets.get(setName).items.length]]);
let mock_minRolls = new Map();
let mock_maxRolls = new Map();
mock_item.set("minRolls", mock_minRolls);
@@ -1216,7 +1215,7 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) {
critStats.append(critChance);
parent_elem.append(critStats);
- addClickableArrow(overallparent_elem);
+ addClickableArrow(overallparent_elem, parent_elem);
}
function displayDefenseStats(parent_elem, statMap, insertSummary){
@@ -1567,15 +1566,15 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
}
}
-function getSpellCost(stats, spellIdx, cost) {
- return Math.max(1, getBaseSpellCost(stats, spellIdx, cost));
+function getSpellCost(stats, spell) {
+ return Math.max(1, getBaseSpellCost(stats, spell));
}
-function getBaseSpellCost(stats, spellIdx, cost) {
- // old intelligence:
- cost = Math.ceil(cost * (1 - skillPointsToPercentage(stats.get('int'))));
- cost += stats.get("spRaw"+spellIdx);
- return Math.floor(cost * (1 + stats.get("spPct"+spellIdx) / 100));
+function getBaseSpellCost(stats, spell) {
+ // old intelligence:
+ let cost = spell.cost; //Math.ceil(spell.cost * (1 - skillPointsToPercentage(stats.get('int'))));
+ cost += stats.get("spRaw"+spell.base_spell);
+ return Math.floor(cost * (1 + stats.get("spPct"+spell.base_spell) / 100));
}
@@ -1591,12 +1590,12 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
if ('cost' in spell) {
let first = document.createElement("span");
- first.textContent = spell.title + " (";
+ first.textContent = spell.name + " (";
title_elem.appendChild(first.cloneNode(true)); //cloneNode is needed here.
title_elemavg.appendChild(first);
let second = document.createElement("span");
- second.textContent = getSpellCost(stats, spellIdx, spell.cost);
+ second.textContent = getSpellCost(stats, spell);
second.classList.add("Mana");
title_elem.appendChild(second.cloneNode(true));
@@ -1618,9 +1617,10 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
parent_elem.append(title_elem);
overallparent_elem.append(title_elemavg);
- if ('cost' in spell) {
- overallparent_elem.append(displayNextCosts(stats, spell, spellIdx));
- }
+ // if ('cost' in spell) {
+ // :( ...... ?
+ // overallparent_elem.append(displayNextCosts(stats, spell, spellIdx));
+ // }
let critChance = skillPointsToPercentage(stats.get('dex'));
@@ -1695,7 +1695,7 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
}
}
- addClickableArrow(overallparent_elem);
+ addClickableArrow(overallparent_elem, parent_elem);
}
/** Displays the ID costs of an item
@@ -2108,11 +2108,23 @@ function stringCDF(id,val,base,amp) {
document.getElementById(id + "-cdf").appendChild(b3);
}
-function addClickableArrow(elem) {
+function addClickableArrow(elem, target) {
//up and down arrow - done ugly
let arrow = document.createElement("img");
arrow.id = "arrow_" + elem.id;
arrow.style.maxWidth = document.body.clientWidth > 900 ? "3rem" : "10rem";
arrow.src = "../media/icons/" + (newIcons ? "new" : "old") + "/toggle_down.png";
elem.appendChild(arrow);
+ arrow.addEventListener("click", () => toggle_spell_tab(arrow, target));
+}
+
+// toggle arrow thinger
+function toggle_spell_tab(arrow_img, target) {
+ if (target.style.display == "none") {
+ target.style.display = "";
+ arrow_img.src = arrow_img.src.replace("down", "up");
+ } else {
+ target.style.display = "none";
+ arrow_img.src = arrow_img.src.replace("up", "down");
+ }
}
diff --git a/js/utils.js b/js/utils.js
index 4259582..fc9cf19 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -499,3 +499,17 @@ function assert_error(func_binding, msg) {
}
throw new Error(msg ? msg : "Function didn't throw an error.");
}
+
+/**
+ * Deep copy object/array of basic types.
+ */
+function deepcopy(obj) {
+ if (typeof(obj) !== 'object' || obj === null) { // null or value type
+ return obj;
+ }
+ let ret = Array.isArray(obj) ? [] : {};
+ for (let key in obj) {
+ ret[key] = deepcopy(obj[key]);
+ }
+ return ret;
+}
diff --git a/py_script/atree-convertID.py b/py_script/atree-convertID.py
index 74c40dd..c8ff4e5 100644
--- a/py_script/atree-convertID.py
+++ b/py_script/atree-convertID.py
@@ -14,16 +14,46 @@ with open("atree_constants.json") as f:
atree_data = json.loads(f.read())
for _class, info in atree_data.items():
+ def translate(path, ref):
+ ref_dict = info
+ for x in path:
+ ref_dict = ref_dict[x]
+ ref_dict[ref] = id_data[_class][ref_dict[ref]]
+
for abil in range(len(info)):
info[abil]["id"] = id_data[_class][info[abil]["display_name"]]
for ref in range(len(info[abil]["parents"])):
- info[abil]["parents"][ref] = id_data[_class][info[abil]["parents"][ref]]
+ translate([abil, "parents"], ref)
for ref in range(len(info[abil]["dependencies"])):
- info[abil]["dependencies"][ref] = id_data[_class][info[abil]["dependencies"][ref]]
+ translate([abil, "dependencies"], ref)
for ref in range(len(info[abil]["blockers"])):
- info[abil]["blockers"][ref] = id_data[_class][info[abil]["blockers"][ref]]
+ translate([abil, "blockers"], ref)
+
+ if "base_abil" in info[abil]:
+ base_abil_name = info[abil]["base_abil"]
+ if base_abil_name in id_data[_class]:
+ translate([abil], "base_abil")
+
+ if "effects" not in info[abil]:
+ print(info[abil])
+ info[abil]["effects"] = []
+ for effect in info[abil]["effects"]:
+ if effect["type"] == "raw_stat":
+ for bonus in effect["bonuses"]:
+ if "abil" in bonus:
+ bonus["abil"] = id_data[_class][bonus["abil"]]
+
+ elif effect["type"] == "stat_scaling":
+ if "inputs" in effect: # Might not exist for sliders
+ for _input in effect["inputs"]:
+ if "abil" in _input:
+ _input["abil"] = id_data[_class][_input["abil"]]
+
+ if "abil" in effect["output"]:
+ effect["output"]["abil"] = id_data[_class][effect["output"]["abil"]]
+
with open('atree_constants_idfied.json', 'w', encoding='utf-8') as abil_dest:
- json.dump(atree_data, abil_dest, ensure_ascii=False, indent=4)
\ No newline at end of file
+ json.dump(atree_data, abil_dest, ensure_ascii=False, indent=4)