Merge pull request #63 from hppeng-wynn/smarter_compute_node
ComputeNode subclass that will defer updates if its value did not change
This commit is contained in:
commit
91d295ef92
3 changed files with 77 additions and 16 deletions
|
@ -124,16 +124,16 @@ const default_abils = {
|
|||
/**
|
||||
* Update ability tree internal representation. (topologically sorted node list)
|
||||
*
|
||||
* Signature: AbilityTreeUpdateNode(build: Build) => ATree (List of atree nodes in topological order)
|
||||
* Signature: AbilityTreeUpdateNode(player-class: str) => ATree (List of atree nodes in topological order)
|
||||
*/
|
||||
const atree_node = new (class extends ComputeNode {
|
||||
constructor() { super('builder-atree-update'); }
|
||||
|
||||
compute_func(input_map) {
|
||||
if (input_map.size !== 1) { throw "AbilityTreeUpdateNode accepts exactly one input (build)"; }
|
||||
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
|
||||
if (input_map.size !== 1) { throw "AbilityTreeUpdateNode accepts exactly one input (player-class)"; }
|
||||
const [player_class] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
|
||||
|
||||
const atree_raw = atrees[wep_to_class.get(build.weapon.statMap.get('type'))];
|
||||
const atree_raw = atrees[player_class];
|
||||
if (!atree_raw) return null;
|
||||
|
||||
let atree_map = new Map();
|
||||
|
|
|
@ -435,6 +435,17 @@ class BuildAssembleNode extends ComputeNode {
|
|||
}
|
||||
}
|
||||
|
||||
class PlayerClassNode extends ValueCheckComputeNode {
|
||||
constructor(name) { super(name); }
|
||||
|
||||
compute_func(input_map) {
|
||||
if (input_map.size !== 1) { throw "PlayerClassNode accepts exactly one input (build)"; }
|
||||
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
|
||||
|
||||
return wep_to_class.get(build.weapon.statMap.get('type'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an input field and parse into a list of powderings.
|
||||
* Every two characters makes one powder. If parsing fails, NULL is returned.
|
||||
|
@ -1085,8 +1096,9 @@ function builder_graph_init() {
|
|||
|
||||
// Phase 3/3: Set up atree stuff.
|
||||
|
||||
let class_node = new PlayerClassNode('builder-class').link_to(build_node);
|
||||
// These two are defined in `atree.js`
|
||||
atree_node.link_to(build_node, 'build');
|
||||
atree_node.link_to(class_node, 'player-class');
|
||||
atree_merge.link_to(build_node, 'build');
|
||||
atree_graph_creator = new AbilityTreeEnsureNodesNode(build_node, stat_agg_node)
|
||||
.link_to(atree_collect_spells, 'spells');
|
||||
|
|
|
@ -14,7 +14,10 @@ class ComputeNode {
|
|||
this.name = name;
|
||||
this.update_task = null;
|
||||
this.fail_cb = false; // Set to true to force updates even if parent failed.
|
||||
this.dirty = true;
|
||||
this.dirty = 2; // 3 states:
|
||||
// 2: dirty
|
||||
// 1: possibly dirty
|
||||
// 0: clean
|
||||
this.inputs_dirty = new Map();
|
||||
this.inputs_dirty_count = 0;
|
||||
all_nodes.push(this);
|
||||
|
@ -27,15 +30,17 @@ class ComputeNode {
|
|||
if (this.inputs_dirty_count != 0) {
|
||||
return;
|
||||
}
|
||||
if (!this.dirty) {
|
||||
if (this.dirty === 0) {
|
||||
return;
|
||||
}
|
||||
let calc_inputs = new Map();
|
||||
for (const input of this.inputs) {
|
||||
calc_inputs.set(this.input_translation.get(input.name), input.value);
|
||||
if (this.dirty == 2) {
|
||||
let calc_inputs = new Map();
|
||||
for (const input of this.inputs) {
|
||||
calc_inputs.set(this.input_translation.get(input.name), input.value);
|
||||
}
|
||||
this.value = this.compute_func(calc_inputs);
|
||||
}
|
||||
this.value = this.compute_func(calc_inputs);
|
||||
this.dirty = false;
|
||||
this.dirty = 0;
|
||||
for (const child of this.children) {
|
||||
child.mark_input_clean(this.name, this.value);
|
||||
}
|
||||
|
@ -64,12 +69,12 @@ class ComputeNode {
|
|||
}
|
||||
}
|
||||
|
||||
mark_dirty() {
|
||||
if (!this.dirty) {
|
||||
this.dirty = true;
|
||||
mark_dirty(dirty_state=2) {
|
||||
if (this.dirty < dirty_state) {
|
||||
this.dirty = dirty_state;
|
||||
for (const child of this.children) {
|
||||
child.mark_input_dirty(this.name);
|
||||
child.mark_dirty();
|
||||
child.mark_dirty(dirty_state);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
@ -125,6 +130,50 @@ class ComputeNode {
|
|||
}
|
||||
}
|
||||
|
||||
class ValueCheckComputeNode extends ComputeNode {
|
||||
constructor(name) { super(name); }
|
||||
|
||||
/**
|
||||
* Request update of this compute node. Pushes updates to children,
|
||||
* but only if this node's value changed.
|
||||
*/
|
||||
update() {
|
||||
if (this.inputs_dirty_count != 0) {
|
||||
return;
|
||||
}
|
||||
if (this.dirty === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let calc_inputs = new Map();
|
||||
for (const input of this.inputs) {
|
||||
calc_inputs.set(this.input_translation.get(input.name), input.value);
|
||||
}
|
||||
let val = this.compute_func(calc_inputs);
|
||||
if (val !== this.value) {
|
||||
this.mark_dirty(2);
|
||||
}
|
||||
else {
|
||||
console.log("soft update");
|
||||
}
|
||||
this.value = val;
|
||||
|
||||
this.dirty = 0;
|
||||
for (const child of this.children) {
|
||||
child.mark_input_clean(this.name, this.value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaulting to "dusty" state.
|
||||
*/
|
||||
mark_dirty(dirty_state="unused") {
|
||||
return super.mark_dirty(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a ComputeNode to be updated.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue