Patch Water Mastery fluid healing interaction

according to zeer, all stat scaling is applied after total stats
This commit is contained in:
hppeng 2022-07-23 12:30:59 -07:00
parent f9e783e56e
commit 7919647372
2 changed files with 58 additions and 62 deletions

View file

@ -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<str, slider_info>, Map<str, button_info>]) => 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,12 +740,22 @@ 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.
let total = 0;
const {round = true, slider = false, scaling = [0]} = effect;
if (slider) {
const slider_val = slider_map.get(effect.slider_name).slider.value;
const {round = true} = effect;
let total = parseInt(slider_val) * effect.scaling[0];
total = parseInt(slider_val) * scaling[0];
}
else {
// TODO: type: prop?
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; }
if (Array.isArray(effect.output)) {
for (const output of effect.output) {
@ -761,32 +770,32 @@ const atree_stats = new (class extends ComputeNode {
}
}
}
}
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);
}
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') {
merge_stat(ret_effects, output.name, total);
}
}
}
else {
if (effect.output.type === 'stat') {
merge_stat(ret_effects, effect.output.name, total);
}
}
}
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');

View file

@ -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