diff --git a/js/atree.js b/js/atree.js index e2249da..84fb8bd 100644 --- a/js/atree.js +++ b/js/atree.js @@ -696,7 +696,7 @@ const atree_make_interactives = new (class extends ComputeNode { let slider_container = gen_slider_labeled(slider_info); document.getElementById("boost-sliders").appendChild(slider_container); slider_info.slider = document.getElementById(slider_info.id); - slider_info.slider.addEventListener("change", (e) => atree_stats.mark_dirty().update()); + slider_info.slider.addEventListener("change", (e) => atree_scaling.mark_dirty().update()); } for (const [button_name, button_info] of button_map.entries()) { let button = make_elem('button', ["button-boost", "border-0", "text-white", "dark-8u", "dark-shadow-sm", "m-1"], { @@ -709,7 +709,7 @@ const atree_make_interactives = new (class extends ComputeNode { } else { button.classList.add("toggleOn"); } - atree_stats.mark_dirty().update() + atree_scaling.mark_dirty().update() }); button_info.button = button; document.getElementById("boost-toggles").appendChild(button); @@ -718,20 +718,19 @@ const atree_make_interactives = new (class extends ComputeNode { } })().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) + * Scaling stats from ability tree. + * Return StatMap of added stats, * - * Signature: AbilityTreeStatsNode(atree-merged: MergedATree, build: Build, + * Signature: AbilityTreeScalingNode(atree-merged: MergedATree, scale-scats: StatMap, * atree-interactive: [Map, Map]) => StatMap */ -const atree_stats = new (class extends ComputeNode { - constructor() { super('atree-stats-collector'); } +const atree_scaling = new (class extends ComputeNode { + constructor() { super('atree-scaling-collector'); } compute_func(input_map) { const atree_merged = input_map.get('atree-merged'); - const item_stats = input_map.get('build').statMap; + const pre_scale_stats = input_map.get('scale-stats'); const [slider_map, button_map] = input_map.get('atree-interactive'); let ret_effects = new Map(); @@ -741,41 +740,26 @@ const atree_stats = new (class extends ComputeNode { for (const effect of abil.effects) { switch (effect.type) { case 'stat_scaling': - if (effect.slider) { - if ('output' in effect) { // sometimes nodes will modify slider without having effect. - const slider_val = slider_map.get(effect.slider_name).slider.value; - const {round = true} = effect; - let total = parseInt(slider_val) * effect.scaling[0]; - if (round) { total = Math.floor(round_near(total)); } - if ('max' in effect && total > effect.max) { total = effect.max; } - if (Array.isArray(effect.output)) { - for (const output of effect.output) { - if (output.type === 'stat') { // TODO: prop - merge_stat(ret_effects, output.name, total); - } - } - } - else { - if (effect.output.type === 'stat') { - merge_stat(ret_effects, effect.output.name, total); - } - } - } + let total = 0; + const {round = true, slider = false, scaling = [0]} = effect; + if (slider) { + const slider_val = slider_map.get(effect.slider_name).slider.value; + total = parseInt(slider_val) * scaling[0]; } else { // TODO: type: prop? - let total = 0; - const {round = true} = effect; - for (const [scaling, input] of zip2(effect.scaling, effect.inputs)) { - total += scaling * item_stats.get(input.name); + for (const [_scaling, input] of zip2(scaling, effect.inputs)) { + total += _scaling * pre_scale_stats.get(input.name); } + } + + if ('output' in effect) { // sometimes nodes will modify slider without having effect. if (round) { total = Math.floor(round_near(total)); } if (total < 0) { total = 0; } // Normal stat scaling will not go negative. if ('max' in effect && total > effect.max) { total = effect.max; } - // TODO: output (list...) if (Array.isArray(effect.output)) { for (const output of effect.output) { - if (output.type === 'stat') { + if (output.type === 'stat') { // TODO: prop merge_stat(ret_effects, output.name, total); } } @@ -787,6 +771,31 @@ const atree_stats = new (class extends ComputeNode { } } continue; + } + } + } + return ret_effects; + } +})().link_to(atree_merge, 'atree-merged').link_to(atree_make_interactives, 'atree-interactive'); + +/** + * Collect stats from ability tree. + * Return StatMap of added stats. + * + * Signature: AbilityTreeStatsNode(atree-merged: MergedATree) => StatMap + */ +const atree_stats = new (class extends ComputeNode { + constructor() { super('atree-stats-collector'); } + + compute_func(input_map) { + const atree_merged = input_map.get('atree-merged'); + + let ret_effects = new Map(); + for (const [abil_id, abil] of atree_merged.entries()) { + if (abil.effects.length == 0) { continue; } + + for (const effect of abil.effects) { + switch (effect.type) { case 'raw_stat': // TODO: toggles... if (effect.toggle) { @@ -801,27 +810,12 @@ const atree_stats = new (class extends ComputeNode { } } continue; - case 'add_spell_prop': - continue; - // TODO unjankify.... - // costs are converted to raw cost ID - // const { base_spell, cost = 0} = effect; - // if (cost) { - // const key = "spRaw"+base_spell; - // if (ret_effects.has(key)) { ret_effects.set(key, ret_effects.get(key) + cost); } - // else { ret_effects.set(key, cost); } - // } - // continue; } } } - 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_make_interactives, 'atree-interactive'); - +})().link_to(atree_merge, 'atree-merged'); /** * Construct compute nodes to link builder items and edit IDs to the appropriate display outputs. @@ -844,16 +838,15 @@ class AbilityTreeEnsureNodesNode extends ComputeNode { this.build_node = build_node; this.stat_agg_node = stat_agg_node; // Slight amount of wasted compute to keep internal state non-changing. - this.passthrough = new PassThroughNode('atree-make-nodes_internal').link_to(this.build_node, 'build').link_to(this.stat_agg_node, 'stats'); + this.passthrough = new PassThroughNode('spell-calc-buffer').link_to(this.build_node, 'build').link_to(this.stat_agg_node, 'stats'); this.spelldmg_nodes = []; // debugging use this.spell_display_elem = document.getElementById("all-spells-display"); } compute_func(input_map) { - console.log('atree make nodes'); this.passthrough.remove_link(this.build_node); this.passthrough.remove_link(this.stat_agg_node); - this.passthrough = new PassThroughNode('atree-make-nodes_internal').link_to(this.build_node, 'build').link_to(this.stat_agg_node, 'stats'); + this.passthrough = new PassThroughNode('spell-calc-buffer').link_to(this.build_node, 'build').link_to(this.stat_agg_node, 'stats'); this.spell_display_elem.textContent = ""; const build_node = this.passthrough.get_node('build'); // aaaaaaaaa performance... savings... help.... const stat_agg_node = this.passthrough.get_node('stats'); diff --git a/js/builder_graph.js b/js/builder_graph.js index 093f367..b38f467 100644 --- a/js/builder_graph.js +++ b/js/builder_graph.js @@ -794,7 +794,7 @@ class DisplayBuildWarningsNode extends ComputeNode { * Signature: AggregateStatsNode(*args) => StatMap */ class AggregateStatsNode extends ComputeNode { - constructor() { super("builder-aggregate-stats"); } + constructor(name) { super(name); } compute_func(input_map) { const output_stats = new Map(); @@ -997,7 +997,8 @@ function builder_graph_init() { // Phase 2/3: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage // Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap) - stat_agg_node = new AggregateStatsNode(); + pre_scale_agg_node = new AggregateStatsNode('pre-scale-stats'); + stat_agg_node = new AggregateStatsNode('final-stats'); edit_agg_node = new AggregateEditableIDNode(); edit_agg_node.link_to(build_node, 'build'); for (const field of editable_item_fields) { @@ -1021,7 +1022,7 @@ function builder_graph_init() { edit_input_nodes.push(node); skp_inputs.push(node); } - stat_agg_node.link_to(edit_agg_node); + pre_scale_agg_node.link_to(edit_agg_node); // Phase 3/3: Set up atree stuff. @@ -1029,8 +1030,10 @@ function builder_graph_init() { // These two are defined in `atree.js` atree_node.link_to(class_node, 'player-class'); atree_merge.link_to(class_node, 'player-class'); - atree_stats.link_to(build_node, 'build'); - stat_agg_node.link_to(atree_stats, 'atree-stats'); + pre_scale_agg_node.link_to(atree_stats, 'atree-raw-stats'); + atree_scaling.link_to(pre_scale_agg_node, 'scale-stats'); + stat_agg_node.link_to(pre_scale_agg_node, 'pre-scaling'); + stat_agg_node.link_to(atree_scaling, 'atree-scaling'); build_encode_node.link_to(atree_node, 'atree').link_to(atree_state_node, 'atree-state'); @@ -1062,12 +1065,12 @@ function builder_graph_init() { let powder_special_calc = new PowderSpecialCalcNode().link_to(powder_special_input, 'powder-specials'); new PowderSpecialDisplayNode().link_to(powder_special_input, 'powder-specials') .link_to(stat_agg_node, 'stats').link_to(build_node, 'build'); - stat_agg_node.link_to(powder_special_calc, 'powder-boost'); - stat_agg_node.link_to(armor_powder_node, 'armor-powder'); + pre_scale_agg_node.link_to(powder_special_calc, 'powder-boost'); + pre_scale_agg_node.link_to(armor_powder_node, 'armor-powder'); powder_special_input.update(); // Potion boost. - stat_agg_node.link_to(boosts_node, 'potion-boost'); + pre_scale_agg_node.link_to(boosts_node, 'potion-boost'); // Also do something similar for skill points