Merge pull request #49 from hppeng-wynn/spell_reformat

Spell reformat
This commit is contained in:
hppeng-wynn 2022-06-27 22:40:35 -07:00 committed by GitHub
commit 3c21f9801a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 304 additions and 148 deletions

View file

@ -468,6 +468,7 @@ class PowderInputNode extends InputNode {
* Signature: SpellSelectNode<int>(build: Build) => [Spell, SpellParts] * Signature: SpellSelectNode<int>(build: Build) => [Spell, SpellParts]
*/ */
class SpellSelectNode extends ComputeNode { class SpellSelectNode extends ComputeNode {
// TODO: rewrite me entirely...
constructor(spell_num) { constructor(spell_num) {
super("builder-spell"+spell_num+"-select"); super("builder-spell"+spell_num+"-select");
this.spell_idx = spell_num; this.spell_idx = spell_num;
@ -475,10 +476,18 @@ class SpellSelectNode extends ComputeNode {
compute_func(input_map) { compute_func(input_map) {
const build = input_map.get('build'); const build = input_map.get('build');
let stats = build.statMap;
const i = this.spell_idx; const i = this.spell_idx;
let spell = spell_table[build.weapon.statMap.get("type")][i]; const spells = default_spells[build.weapon.statMap.get("type")];
let stats = build.statMap; let spell;
for (const _spell of spells) {
if (_spell.base_spell === i) {
spell = _spell;
break;
}
}
if (spell === undefined) { return null; }
let spell_parts; let spell_parts;
if (spell.parts) { if (spell.parts) {
@ -551,6 +560,7 @@ class SpellDamageCalcNode extends ComputeNode {
compute_func(input_map) { compute_func(input_map) {
const weapon = input_map.get('build').weapon.statMap; const weapon = input_map.get('build').weapon.statMap;
const spell_info = input_map.get('spell-info'); const spell_info = input_map.get('spell-info');
const spell = spell_info[0];
const spell_parts = spell_info[1]; const spell_parts = spell_info[1];
const stats = input_map.get('stats'); const stats = input_map.get('stats');
const damage_mult = stats.get('damageMultiplier'); const damage_mult = stats.get('damageMultiplier');
@ -562,23 +572,67 @@ class SpellDamageCalcNode extends ComputeNode {
stats.get('agi') stats.get('agi')
]; ];
let spell_results = [] let spell_results = []
let spell_result_map = new Map();
const use_speed = (('use_atkspd' in spell) ? spell.use_atkspd : true);
const use_spell = (('scaling' in spell) ? spell.scaling === 'spell' : true);
// TODO: move preprocessing to separate node/node chain
for (const part of spell_parts) { for (const part of spell_parts) {
if (part.type === "damage") { let spell_result;
let tmp_conv = []; if ('multipliers' in part) { // damage type spell
for (let i in part.conversion) { let results = calculateSpellDamage(stats, weapon, part.multipliers, use_spell, !use_speed);
tmp_conv.push(part.conversion[i] * part.multiplier/100); spell_result = {
type: "damage",
normal_min: results[2].map(x => x[0]),
normal_max: results[2].map(x => x[1]),
normal_total: results[0],
crit_min: results[2].map(x => x[2]),
crit_max: results[2].map(x => x[3]),
crit_total: results[1],
} }
let results = calculateSpellDamage(stats, weapon, tmp_conv, true); } else if ('power' in part) {
spell_results.push(results);
} else if (part.type === "heal") {
// TODO: wynn2 formula // TODO: wynn2 formula
let heal_amount = (part.strength * getDefenseStats(stats)[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2); let _heal_amount = (part.power * getDefenseStats(stats)[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100)));
spell_results.push(heal_amount); spell_result = {
} else if (part.type === "total") { type: "heal",
// TODO: remove "total" type heal_amount: _heal_amount
spell_results.push(null);
} }
} else if ('hits' in part) {
spell_result = {
normal_min: [0, 0, 0, 0, 0, 0],
normal_max: [0, 0, 0, 0, 0, 0],
normal_total: [0, 0],
crit_min: [0, 0, 0, 0, 0, 0],
crit_max: [0, 0, 0, 0, 0, 0],
crit_total: [0, 0],
heal_amount: 0
}
const dam_res_keys = ['normal_min', 'normal_max', 'normal_total', 'crit_min', 'crit_max', 'crit_total'];
for (const [subpart_name, hits] of Object.entries(part.hits)) {
const subpart = spell_result_map.get(subpart_name);
if (spell_result.type) {
if (subpart.type !== spell_result.type) {
throw "SpellCalc total subpart type mismatch";
}
}
else {
spell_result.type = subpart.type;
}
if (spell_result.type === 'damage') {
for (const key of dam_res_keys) {
for (let i in spell_result.normal_min) {
spell_result[key][i] += subpart[key][i] * hits;
}
}
}
else {
spell_result.heal_amount += subpart.heal_amount;
}
}
}
spell_result.name = part.name;
spell_results.push(spell_result);
spell_result_map.set(part.name, spell_result);
} }
return spell_results; return spell_results;
} }
@ -604,12 +658,11 @@ class SpellDisplayNode extends ComputeNode {
const spell_info = input_map.get('spell-info'); const spell_info = input_map.get('spell-info');
const damages = input_map.get('spell-damage'); const damages = input_map.get('spell-damage');
const spell = spell_info[0]; const spell = spell_info[0];
const spell_parts = spell_info[1];
const i = this.spell_idx; const i = this.spell_idx;
let parent_elem = document.getElementById("spell"+i+"-info"); let parent_elem = document.getElementById("spell"+i+"-info");
let overallparent_elem = document.getElementById("spell"+i+"-infoAvg"); let overallparent_elem = document.getElementById("spell"+i+"-infoAvg");
displaySpellDamage(parent_elem, overallparent_elem, stats, spell, i+1, spell_parts, damages); displaySpellDamage(parent_elem, overallparent_elem, stats, spell, i+1, damages);
} }
} }
@ -833,14 +886,13 @@ class AggregateEditableIDNode extends ComputeNode {
compute_func(input_map) { compute_func(input_map) {
const build = input_map.get('build'); input_map.delete('build'); const build = input_map.get('build'); input_map.delete('build');
const weapon = input_map.get('weapon'); input_map.delete('weapon');
const output_stats = new Map(build.statMap); const output_stats = new Map(build.statMap);
for (const [k, v] of input_map.entries()) { for (const [k, v] of input_map.entries()) {
output_stats.set(k, v); output_stats.set(k, v);
} }
output_stats.set('classDef', classDefenseMultipliers.get(weapon.statMap.get("type"))); output_stats.set('classDef', classDefenseMultipliers.get(build.weapon.statMap.get("type")));
return output_stats; return output_stats;
} }
} }
@ -946,6 +998,9 @@ let powder_nodes = [];
let spelldmg_nodes = []; let spelldmg_nodes = [];
let edit_input_nodes = []; let edit_input_nodes = [];
let skp_inputs = []; let skp_inputs = [];
let build_node;
let stat_agg_node;
let edit_agg_node;
function builder_graph_init() { function builder_graph_init() {
// Phase 1/2: Set up item input, propagate updates, etc. // Phase 1/2: Set up item input, propagate updates, etc.
@ -980,7 +1035,7 @@ function builder_graph_init() {
let level_input = new InputNode('level-input', document.getElementById('level-choice')); let level_input = new InputNode('level-input', document.getElementById('level-choice'));
// "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display. // "Build" now only refers to equipment and level (no powders). Powders are injected before damage calculation / stat display.
let build_node = new BuildAssembleNode(); build_node = new BuildAssembleNode();
for (const input of item_nodes) { for (const input of item_nodes) {
build_node.link_to(input); build_node.link_to(input);
} }
@ -1011,9 +1066,9 @@ function builder_graph_init() {
build_disp_node.link_to(build_node, 'build'); build_disp_node.link_to(build_node, 'build');
// 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) // 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)
let stat_agg_node = new AggregateStatsNode(); stat_agg_node = new AggregateStatsNode();
let edit_agg_node = new AggregateEditableIDNode(); edit_agg_node = new AggregateEditableIDNode();
edit_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon'); edit_agg_node.link_to(build_node, 'build');
for (const field of editable_item_fields) { for (const field of editable_item_fields) {
// Create nodes that listens to each editable id input, the node name should match the "id" // Create nodes that listens to each editable id input, the node name should match the "id"
const elem = document.getElementById(field); const elem = document.getElementById(field);
@ -1057,7 +1112,8 @@ function builder_graph_init() {
// Also do something similar for skill points // Also do something similar for skill points
for (let i = 0; i < 4; ++i) { //for (let i = 0; i < 4; ++i) { TODO: testing code
for (let i = 0; i < 1; ++i) {
let spell_node = new SpellSelectNode(i); let spell_node = new SpellSelectNode(i);
spell_node.link_to(build_node, 'build'); spell_node.link_to(build_node, 'build');
// TODO: link and rewrite spell_node to the stat agg node // TODO: link and rewrite spell_node to the stat agg node
@ -1080,8 +1136,6 @@ function builder_graph_init() {
let skp_output = new SkillPointSetterNode(edit_input_nodes); let skp_output = new SkillPointSetterNode(edit_input_nodes);
skp_output.link_to(build_node); skp_output.link_to(build_node);
edit_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon');
let build_warnings_node = new DisplayBuildWarningsNode(); let build_warnings_node = new DisplayBuildWarningsNode();
build_warnings_node.link_to(build_node, 'build'); build_warnings_node.link_to(build_node, 'build');
for (const [skp_input, skp] of zip2(skp_inputs, skp_order)) { for (const [skp_input, skp] of zip2(skp_inputs, skp_order)) {

View file

@ -89,6 +89,9 @@ class ComputeNode {
throw "no compute func specified"; throw "no compute func specified";
} }
/**
* Add link to a parent compute node, optionally with an alias.
*/
link_to(parent_node, link_name) { link_to(parent_node, link_name) {
this.inputs.push(parent_node) this.inputs.push(parent_node)
link_name = (link_name !== undefined) ? link_name : parent_node.name; link_name = (link_name !== undefined) ? link_name : parent_node.name;
@ -100,6 +103,26 @@ class ComputeNode {
parent_node.children.push(this); parent_node.children.push(this);
return this; return this;
} }
/**
* Delete a link to a parent node.
* TODO: time complexity of list deletion (not super relevant but it hurts my soul)
*/
remove_link(parent_node) {
const idx = this.inputs.indexOf(parent_node); // Get idx
this.inputs.splice(idx, 1); // remove element
this.input_translations.delete(parent_node.name);
const was_dirty = this.inputs_dirty.get(parent_node.name);
this.inputs_dirty.delete(parent_node.name);
if (was_dirty) {
this.inputs_dirty_count -= 1;
}
const idx2 = parent_node.children.indexOf(this);
parent_node.children.splice(idx2, 1);
return this;
}
} }
/** /**

View file

@ -56,11 +56,14 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
// 2.2. Next, apply elemental conversions using damage computed in step 1.1. // 2.2. Next, apply elemental conversions using damage computed in step 1.1.
// Also, track which elements are present. (Add onto those present in the weapon itself.) // Also, track which elements are present. (Add onto those present in the weapon itself.)
let total_convert = 0; //TODO get confirmation that this is how raw works.
for (let i = 1; i <= 5; ++i) { for (let i = 1; i <= 5; ++i) {
if (conversions[i] > 0) { if (conversions[i] > 0) {
damages[i][0] += conversions[i]/100 * weapon_min; const conv_frac = conversions[i]/100;
damages[i][1] += conversions[i]/100 * weapon_max; damages[i][0] += conv_frac * weapon_min;
damages[i][1] += conf_frac * weapon_max;
present[i] = true; present[i] = true;
total_convert += conv_frac
} }
} }
@ -125,22 +128,22 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
raw_boost += stats.get(damage_prefix+'Raw') + stats.get(damage_elements[i]+'DamRaw'); raw_boost += stats.get(damage_prefix+'Raw') + stats.get(damage_elements[i]+'DamRaw');
} }
// Next, rainraw and propRaw // Next, rainraw and propRaw
let new_min = damages_obj[0] + raw_boost; let min_boost = raw_boost;
let new_max = damages_obj[1] + raw_boost; let max_boost = raw_boost;
if (total_max > 0) { // TODO: what about total negative all raw? if (total_max > 0) { // TODO: what about total negative all raw?
if (total_elem_min > 0) { if (total_elem_min > 0) {
new_min += (damages_obj[0] / total_min) * prop_raw; min_boost += (damages_obj[0] / total_min) * prop_raw;
} }
new_max += (damages_obj[1] / total_max) * prop_raw; max_boost += (damages_obj[1] / total_max) * prop_raw;
} }
if (i != 0 && total_elem_max > 0) { // rainraw TODO above if (i != 0 && total_elem_max > 0) { // rainraw TODO above
if (total_elem_min > 0) { if (total_elem_min > 0) {
new_min += (damages_obj[0] / total_elem_min) * rainbow_raw; min_boost += (damages_obj[0] / total_elem_min) * rainbow_raw;
} }
new_max += (damages_obj[1] / total_elem_max) * rainbow_raw; max_boost += (damages_obj[1] / total_elem_max) * rainbow_raw;
} }
damages_obj[0] = new_min; damages_obj[0] += min_boost * total_convert;
damages_obj[1] = new_max; damages_obj[1] += max_boost * total_convert;
} }
// 6. Strength boosters // 6. Strength boosters
@ -173,6 +176,125 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
return [total_dam_norm, total_dam_crit, damages_results]; return [total_dam_norm, total_dam_crit, damages_results];
} }
/*
Spell schema:
spell: {
name: str internal string name for the spell. Unique identifier
cost: Optional[int] ignored for spells that are not id 1-4
display_text: str short description of the spell, ex. Bash, Meteor, Arrow Shield
base_spell: int spell index. 0-4 are reserved (0 is melee, 1-4 is common 4 spells)
spell_type: str [TODO: DEPRECATED/REMOVE] "healing" or "damage"
scaling: Optional[str] [DEFAULT: "spell"] "melee" or "spell"
use_atkspd: Optional[bool] [DEFAULT: true] true to factor attack speed, false otherwise.
display: Optional[str] [DEFAULT: "total"] "total" to sum all parts. Or, the name of a spell part
parts: List[part] Parts of this spell (different stuff the spell does basically)
}
NOTE: when using `replace_spell` on an existing spell, all fields become optional.
Specified fields overwrite existing fields; unspecified fields are left unchanged.
There are three possible spell "part" types: damage, heal, and total.
part: spell_damage | spell_heal | spell_total
spell_damage: {
name: str != "total" Name of the part.
type: "damage" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
multipliers: array[num, 6] floating point spellmults (though supposedly wynn only supports integer mults)
}
spell_heal: {
name: str != "total" Name of the part.
type: "heal" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
power: num floating point healing power (1 is 100% of max hp).
}
spell_total: {
name: str != "total" Name of the part.
type: "total" [TODO: DEPRECATED/REMOVE] flag signaling what type of part it is. Can infer from fields
hits: Map[str, num] Keys are other part names, numbers are the multipliers. Undefined behavior if subparts
are not the same type of spell. Can only pull from spells defined before it.
}
Before passing to display, use the following structs.
NOTE: total is collapsed into damage or healing.
spell_damage: {
type: "damage" Internal use
name: str Display name of part. Should be human readable
normal_min: array[num, 6] floating point damages (no crit, min), can be less than zero. Order: NETWFA
normal_max: array[num, 6] floating point damages (no crit, max)
normal_total: array[num, 2] (min, max) noncrit total damage (not negative)
crit_min: array[num, 6] floating point damages (crit, min), can be less than zero. Order: NETWFA
crit_max: array[num, 6] floating point damages (crit, max)
crit_total: array[num, 2] (min, max) crit total damage (not negative)
}
spell_heal: {
type: "heal" Internal use
name: str Display name of part. Should be human readable
heal_amount: num floating point HP healed (self)
}
*/
const default_spells = {
wand: [{
name: "Wand Melee", // TODO: name for melee attacks?
display_text: "Mage basic attack",
base_spell: 0,
scaling: "melee", use_atkspd: false,
display: "Melee",
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}, {
name: "Heal", // TODO: name for melee attacks?
display_text: "Heal spell!",
base_spell: 1,
display: "Total Heal",
parts: [
{ name: "First Pulse", power: 0.12 },
{ name: "Second and Third Pulses", power: 0.06 },
{ name: "Total Heal", hits: { "First Pulse": 1, "Second and Third Pulses": 2 } }
]
}],
spear: [{
name: "Melee", // TODO: name for melee attacks?
display_text: "Warrior basic attack",
base_spell: 0,
scaling: "melee", use_atkspd: false,
display: "Melee",
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}],
bow: [{
name: "Bow Shot", // TODO: name for melee attacks?
display_text: "Archer basic attack",
base_spell: 0,
scaling: "melee", use_atkspd: false,
display: "Melee",
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}],
dagger: [{
name: "Melee", // TODO: name for melee attacks?
display_text: "Assassin basic attack",
base_spell: 0,
scaling: "melee", use_atkspd: false,
display: "Melee",
parts: [{ name: "Melee", multipliers: [100, 0, 0, 0, 0, 0] }]
}],
relik: [{
name: "Relik Melee", // TODO: name for melee attacks?
display_text: "Shaman basic attack",
base_spell: 0,
spell_type: "damage",
scaling: "melee", use_atkspd: false,
display: "Total",
parts: [
{ name: "Single Beam", multipliers: [33, 0, 0, 0, 0, 0] },
{ name: "Total", hits: { "Single Beam": 3 } }
]
}]
};
const spell_table = { const spell_table = {
"wand": [ "wand": [
{ title: "Heal", cost: 6, parts: [ { title: "Heal", cost: 6, parts: [

View file

@ -1476,9 +1476,10 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
let tmp_conv = []; let tmp_conv = [];
for (let i in part.conversion) { for (let i in part.conversion) {
tmp_conv.push(part.conversion[i] * part.multiplier[power-1]); tmp_conv.push(part.conversion[i] * part.multiplier[power-1] / 100);
} }
let _results = calculateSpellDamage(stats, weapon, tmp_conv, false); console.log(tmp_conv);
let _results = calculateSpellDamage(stats, weapon, tmp_conv, false, true);
let critChance = skillPointsToPercentage(skillpoints[1]); let critChance = skillPointsToPercentage(skillpoints[1]);
let save_damages = []; let save_damages = [];
@ -1578,7 +1579,7 @@ function getBaseSpellCost(stats, spellIdx, cost) {
} }
function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spellIdx, spell_parts, damages) { function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spellIdx, spell_results) {
// TODO: remove spellIdx (just used to flag melee and cost) // TODO: remove spellIdx (just used to flag melee and cost)
// TODO: move cost calc out // TODO: move cost calc out
parent_elem.textContent = ""; parent_elem.textContent = "";
@ -1588,7 +1589,7 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
overallparent_elem.textContent = ""; overallparent_elem.textContent = "";
let title_elemavg = document.createElement("b"); let title_elemavg = document.createElement("b");
if (spellIdx != 0) { if ('cost' in spell) {
let first = document.createElement("span"); let first = document.createElement("span");
first.textContent = spell.title + " ("; first.textContent = spell.title + " (";
title_elem.appendChild(first.cloneNode(true)); //cloneNode is needed here. title_elem.appendChild(first.cloneNode(true)); //cloneNode is needed here.
@ -1610,44 +1611,48 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
title_elemavg.appendChild(third_summary); title_elemavg.appendChild(third_summary);
} }
else { else {
title_elem.textContent = spell.title; title_elem.textContent = spell.name;
title_elemavg.textContent = spell.title; title_elemavg.textContent = spell.name;
} }
parent_elem.append(title_elem); parent_elem.append(title_elem);
overallparent_elem.append(title_elemavg); overallparent_elem.append(title_elemavg);
if ('cost' in spell) {
overallparent_elem.append(displayNextCosts(stats, spell, spellIdx)); overallparent_elem.append(displayNextCosts(stats, spell, spellIdx));
}
let critChance = skillPointsToPercentage(stats.get('dex')); let critChance = skillPointsToPercentage(stats.get('dex'));
let save_damages = [];
let part_divavg = document.createElement("p"); let part_divavg = document.createElement("p");
overallparent_elem.append(part_divavg); overallparent_elem.append(part_divavg);
for (let i = 0; i < spell_parts.length; ++i) { function _summary(text, val, fmt) {
const part = spell_parts[i]; let overallaverageLabel = document.createElement("p");
const damage = damages[i]; let first = document.createElement("span");
let second = document.createElement("span");
first.textContent = text;
second.textContent = val.toFixed(2);
overallaverageLabel.appendChild(first);
overallaverageLabel.appendChild(second);
second.classList.add(fmt);
part_divavg.append(overallaverageLabel);
}
for (let i = 0; i < spell_results.length; ++i) {
const spell_info = spell_results[i];
let part_div = document.createElement("p"); let part_div = document.createElement("p");
parent_elem.append(part_div); parent_elem.append(part_div);
let subtitle_elem = document.createElement("p"); let subtitle_elem = document.createElement("p");
subtitle_elem.textContent = part.subtitle; subtitle_elem.textContent = spell_info.name
part_div.append(subtitle_elem); part_div.append(subtitle_elem);
if (part.type === "damage") { if (spell_info.type === "damage") {
let _results = damage; let totalDamNormal = spell_info.normal_total;
let totalDamNormal = _results[0]; let totalDamCrit = spell_info.crit_total;
let totalDamCrit = _results[1];
let results = _results[2];
for (let i = 0; i < 6; ++i) {
for (let j in results[i]) {
results[i][j] = results[i][j].toFixed(2);
}
}
let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0; let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0;
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0; let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0; let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
@ -1658,83 +1663,35 @@ function displaySpellDamage(parent_elem, overallparent_elem, stats, spell, spell
part_div.append(averageLabel); part_div.append(averageLabel);
if (part.summary == true) { if (spell_info.name === spell.display) {
let overallaverageLabel = document.createElement("p"); _summary(spell_info.name+ " Average: ", averageDamage, "Damage");
let first = document.createElement("span");
let second = document.createElement("span");
first.textContent = part.subtitle + " Average: ";
second.textContent = averageDamage.toFixed(2);
overallaverageLabel.appendChild(first);
overallaverageLabel.appendChild(second);
second.classList.add("Damage");
part_divavg.append(overallaverageLabel);
} }
function _damage_display(label_text, average, result_idx) { function _damage_display(label_text, average, dmg_min, dmg_max) {
let label = document.createElement("p"); let label = document.createElement("p");
label.textContent = label_text+average.toFixed(2); label.textContent = label_text+average.toFixed(2);
part_div.append(label); part_div.append(label);
let arrmin = [];
let arrmax = [];
for (let i = 0; i < 6; i++){ for (let i = 0; i < 6; i++){
if (results[i][1] != 0){ if (dmg_max[i] != 0){
let p = document.createElement("p"); let p = document.createElement("p");
p.classList.add(damageClasses[i]); p.classList.add(damageClasses[i]);
p.textContent = results[i][result_idx] + " \u2013 " + results[i][result_idx + 1]; p.textContent = dmg_min[i].toFixed(2)+" \u2013 "+dmg_max[i].toFixed(2);
arrmin.push(results[i][result_idx]);
arrmax.push(results[i][result_idx + 1]);
part_div.append(p); part_div.append(p);
} }
} }
} }
_damage_display("Non-Crit Average: ", nonCritAverage, 0); _damage_display("Non-Crit Average: ", nonCritAverage, spell_info.normal_min, spell_info.normal_max);
_damage_display("Crit Average: ", critAverage, 2); _damage_display("Crit Average: ", critAverage, spell_info.crit_min, spell_info.crit_max);
} else if (spell_info.type === "heal") {
save_damages.push(averageDamage); let heal_amount = spell_info.heal_amount;
} else if (part.type === "heal") {
let heal_amount = damage;
let healLabel = document.createElement("p"); let healLabel = document.createElement("p");
healLabel.textContent = heal_amount; healLabel.textContent = heal_amount;
// healLabel.classList.add("damagep"); // healLabel.classList.add("damagep");
part_div.append(healLabel); part_div.append(healLabel);
if (part.summary == true) { if (spell_info.name === spell.display) {
let overallhealLabel = document.createElement("p"); _summary(spell_info.name+ ": ", heal_amount, "Set");
let first = document.createElement("span");
let second = document.createElement("span");
first.textContent = part.subtitle + ": ";
second.textContent = heal_amount;
overallhealLabel.appendChild(first);
second.classList.add("Set");
overallhealLabel.appendChild(second);
part_divavg.append(overallhealLabel);
} }
} else if (part.type === "total") {
let total_damage = 0;
for (let i in part.factors) {
total_damage += save_damages[i] * part.factors[i];
}
let dmgarr = part.factors.slice();
dmgarr = dmgarr.map(x => "(" + x + " * " + save_damages[dmgarr.indexOf(x)].toFixed(2) + ")");
let averageLabel = document.createElement("p");
averageLabel.textContent = "Average: "+total_damage.toFixed(2);
averageLabel.classList.add("damageSubtitle");
part_div.append(averageLabel);
let overallaverageLabel = document.createElement("p");
let overallaverageLabelFirst = document.createElement("span");
let overallaverageLabelSecond = document.createElement("span");
overallaverageLabelFirst.textContent = "Average: ";
overallaverageLabelSecond.textContent = total_damage.toFixed(2);
overallaverageLabelSecond.classList.add("Damage");
overallaverageLabel.appendChild(overallaverageLabelFirst);
overallaverageLabel.appendChild(overallaverageLabelSecond);
part_divavg.append(overallaverageLabel);
} }
} }