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