Fix damage calc... somewhat unify powder format

This commit is contained in:
hppeng 2022-06-26 03:14:31 -07:00
parent 24892c91fe
commit 295e8f3e36
4 changed files with 167 additions and 148 deletions

View file

@ -147,7 +147,6 @@ function toggle_tab(tab) {
} else {
document.querySelector("#"+tab).style.display = "none";
}
console.log(document.querySelector("#"+tab).style.display);
}
// toggle spell arrow

View file

@ -22,7 +22,6 @@ function update_armor_powder_specials(elem_id) {
//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
@ -86,23 +85,18 @@ let powder_special_input = new (class extends ComputeNode {
})();
function updatePowderSpecials(buttonId) {
let name = (buttonId).split("-")[0];
let power = (buttonId).split("-")[1]; // [1, 5]
let prefix = (buttonId).split("-")[0].replace(' ', '_') + '-';
let elem = document.getElementById(buttonId);
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
elem.classList.remove("toggleOn");
} else {
if (elem.classList.contains("toggleOn")) { elem.classList.remove("toggleOn"); }
else {
for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off
//name is same, power is i
if(document.getElementById(name.replace(" ", "_") + "-" + i).classList.contains("toggleOn")) {
document.getElementById(name.replace(" ", "_") + "-" + i).classList.remove("toggleOn");
}
const elem2 = document.getElementById(prefix + i);
if(elem2.classList.contains("toggleOn")) { elem2.classList.remove("toggleOn"); }
}
//toggle the pressed button on
elem.classList.add("toggleOn");
}
powder_special_input.mark_dirty().update();
}
@ -129,6 +123,7 @@ class PowderSpecialCalcNode extends ComputeNode {
}
class PowderSpecialDisplayNode extends ComputeNode {
// TODO: Refactor this entirely to be adding more spells to the spell list
constructor() {
super('builder-powder-special-display');
this.fail_cb = true;
@ -137,27 +132,11 @@ class PowderSpecialDisplayNode extends ComputeNode {
compute_func(input_map) {
const powder_specials = input_map.get('powder-specials');
const stats = input_map.get('stats');
const weapon = input_map.get('weapon');
const weapon = input_map.get('build').weapon;
displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap, true);
}
}
/**
* Apply armor powders.
* Encoding shortcut assumes that all powders give +def to one element
* and -def to the element "behind" it in cycle ETWFA, which is true
* as of now and unlikely to change in the near future.
*/
function applyArmorPowders(expandedItem, powders) {
for(const id of powders){
let powder = powderStats[id];
let name = powderNames.get(id).charAt(0);
let prevName = skp_elements[(skp_elements.indexOf(name) + 4 )% 5];
expandedItem.set(name+"Def", (expandedItem.get(name+"Def") || 0) + powder["defPlus"]);
expandedItem.set(prevName+"Def", (expandedItem.get(prevName+"Def") || 0) - powder["defMinus"]);
}
}
/**
* Node for getting an item's stats from an item input field.
*
@ -174,6 +153,11 @@ class ItemInputNode extends InputNode {
constructor(name, item_input_field, none_item) {
super(name, item_input_field);
this.none_item = new Item(none_item);
this.category = this.none_item.statMap.get('category');
if (this.category == 'armor' || this.category == 'weapon') {
this.none_item.statMap.set('powders', []);
apply_weapon_powders(this.none_item.statMap); // Needed to put in damagecalc zeros
}
this.none_item.statMap.set('NONE', true);
}
@ -197,17 +181,17 @@ class ItemInputNode extends InputNode {
item.statMap.set('powders', powdering);
}
let type_match;
if (this.none_item.statMap.get('category') === 'weapon') {
type_match = item.statMap.get('category') === 'weapon';
if (this.category == 'weapon') {
type_match = item.statMap.get('category') == 'weapon';
} else {
type_match = item.statMap.get('type') === this.none_item.statMap.get('type');
type_match = item.statMap.get('type') == this.none_item.statMap.get('type');
}
if (type_match) {
if (item.statMap.get('category') === 'armor') {
applyArmorPowders(item.statMap, powdering);
if (item.statMap.get('category') == 'armor') {
applyArmorPowders(item.statMap);
}
else if (item.statMap.get('category') === 'weapon') {
apply_weapon_powders(item.statMap, powdering);
else if (item.statMap.get('category') == 'weapon') {
apply_weapon_powders(item.statMap);
}
return item;
}
@ -579,7 +563,7 @@ class SpellDamageCalcNode extends ComputeNode {
}
compute_func(input_map) {
const weapon = new Map(input_map.get('weapon-input').statMap);
const weapon = input_map.get('build').weapon.statMap;
const spell_info = input_map.get('spell-info');
const spell_parts = spell_info[1];
const stats = input_map.get('stats');
@ -647,6 +631,7 @@ class SpellDisplayNode extends ComputeNode {
Returns an array in the order:
*/
function getMeleeStats(stats, weapon) {
stats = new Map(stats); // Shallow copy
const weapon_stats = weapon.statMap;
const skillpoints = [
stats.get('str'),
@ -665,9 +650,8 @@ function getMeleeStats(stats, weapon) {
adjAtkSpd = 0;
}
let damage_mult = stats.get("damageMultiplier");
if (weapon_stats.get("type") === "relik") {
damage_mult = 0.99; // CURSE YOU WYNNCRAFT
stats.set('damageMultiplier', 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.
}
@ -1076,7 +1060,7 @@ function builder_graph_init() {
// Powder specials.
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(item_nodes[8], 'weapon');
.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');
powder_special_input.update();
@ -1093,7 +1077,7 @@ function builder_graph_init() {
spell_node.link_to(stat_agg_node, 'stats')
let calc_node = new SpellDamageCalcNode(i);
calc_node.link_to(item_nodes[8], 'weapon-input').link_to(stat_agg_node, 'stats')
calc_node.link_to(build_node, 'build').link_to(stat_agg_node, 'stats')
.link_to(spell_node, 'spell-info');
spelldmg_nodes.push(calc_node);

View file

@ -1,7 +1,5 @@
const damageMultipliers = new Map([ ["allytotem", .15], ["yourtotem", .35], ["vanish", 0.80], ["warscream", 0.10], ["bash", 0.50] ]);
const damage_keys = [ "nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_" ];
const damage_present_key = 'damagePresent';
function get_base_dps(item) {
const attack_speed_mult = baseDamageMultiplier[attackSpeeds.indexOf(item.get("atkSpd"))];
//SUPER JANK @HPP PLS FIX
@ -27,107 +25,6 @@ function get_base_dps(item) {
}
}
// THIS MUTATES THE ITEM
function apply_weapon_powders(item) {
let present;
if (item.get("tier") !== "Crafted") {
let weapon_result = calc_weapon_powder(item);
let damages = weapon_result[0];
present = weapon_result[1];
for (const i in damage_keys) {
item.set(damage_keys[i], damages[i]);
}
} else {
let base_low = [item.get("nDamBaseLow"),item.get("eDamBaseLow"),item.get("tDamBaseLow"),item.get("wDamBaseLow"),item.get("fDamBaseLow"),item.get("aDamBaseLow")];
let results_low = calc_weapon_powder(item, base_low);
let damage_low = results_low[0];
let base_high = [item.get("nDamBaseHigh"),item.get("eDamBaseHigh"),item.get("tDamBaseHigh"),item.get("wDamBaseHigh"),item.get("fDamBaseHigh"),item.get("aDamBaseHigh")];
let results_high = calc_weapon_powder(item, base_high);
let damage_high = results_high[0];
present = results_high[1];
for (const i in damage_keys) {
item.set(damage_keys[i], [damage_low[i], damage_high[i]]);
}
}
console.log(item);
item.set(damage_present_key, present);
}
/**
* weapon: Weapon to apply powder to
* damageBases: used by crafted
*/
function calc_weapon_powder(weapon, damageBases) {
let powders = weapon.get("powders").slice();
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
let damages = [
weapon.get('nDam').split('-').map(Number),
weapon.get('eDam').split('-').map(Number),
weapon.get('tDam').split('-').map(Number),
weapon.get('wDam').split('-').map(Number),
weapon.get('fDam').split('-').map(Number),
weapon.get('aDam').split('-').map(Number)
];
// Applying spell conversions
let neutralBase = damages[0].slice();
let neutralRemainingRaw = damages[0].slice();
//powder application for custom crafted weapons is inherently fucked because there is no base. Unsure what to do.
//Powder application for Crafted weapons - this implementation is RIGHT YEAAAAAAAAA
//1st round - apply each as ingred, 2nd round - apply as normal
if (weapon.get("tier") === "Crafted" && !weapon.get("custom")) {
for (const p of powders.concat(weapon.get("ingredPowders"))) {
let powder = powderStats[p]; //use min, max, and convert
let element = Math.floor((p+0.01)/6); //[0,4], the +0.01 attempts to prevent division error
let diff = Math.floor(damageBases[0] * powder.convert/100);
damageBases[0] -= diff;
damageBases[element+1] += diff + Math.floor( (powder.min + powder.max) / 2 );
}
//update all damages
for (let i = 0; i < damages.length; i++) {
damages[i] = [Math.floor(damageBases[i] * 0.9), Math.floor(damageBases[i] * 1.1)];
}
neutralRemainingRaw = damages[0].slice();
neutralBase = damages[0].slice();
}
//apply powders to weapon
for (const powderID of powders) {
const powder = powderStats[powderID];
// Bitwise to force conversion to integer (integer division).
const element = (powderID/6) | 0;
let conversionRatio = powder.convert/100;
if (neutralRemainingRaw[1] > 0) {
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
let max_diff = Math.min(neutralRemainingRaw[1], conversionRatio * neutralBase[1]);
//damages[element+1][0] = Math.floor(round_near(damages[element+1][0] + min_diff));
//damages[element+1][1] = Math.floor(round_near(damages[element+1][1] + max_diff));
//neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
//neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
damages[element+1][0] += min_diff;
damages[element+1][1] += max_diff;
neutralRemainingRaw[0] -= min_diff;
neutralRemainingRaw[1] -= max_diff;
}
damages[element+1][0] += powder.min;
damages[element+1][1] += powder.max;
}
// The ordering of these two blocks decides whether neutral is present when converted away or not.
let present_elements = []
for (const damage of damages) {
present_elements.push(damage[1] > 0);
}
// The ordering of these two blocks decides whether neutral is present when converted away or not.
damages[0] = neutralRemainingRaw;
return [damages, present_elements];
}
function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, ignore_speed=false) {
// TODO: Roll all the loops together maybe
@ -227,10 +124,18 @@ function calculateSpellDamage(stats, weapon, conversions, use_spell_damage, igno
raw_boost += stats.get(damage_prefix+'Raw') + stats.get(damage_elements[i]+'DamRaw');
}
// Next, rainraw and propRaw
let new_min = damages_obj[0] + raw_boost + (damages_obj[0] / total_min) * prop_raw;
let new_max = damages_obj[1] + raw_boost + (damages_obj[1] / total_max) * prop_raw;
if (i != 0) { // rainraw
new_min += (damages_obj[0] / total_elem_min) * rainbow_raw;
let new_min = damages_obj[0] + raw_boost;
let new_max = damages_obj[1] + raw_boost;
if (total_max > 0) { // TODO: what about total negative all raw?
if (total_elem_min > 0) {
new_min += (damages_obj[0] / total_min) * prop_raw;
}
new_max += (damages_obj[1] / total_max) * prop_raw;
}
if (i != 0 && total_elem_max > 0) { // rainraw TODO above
if (total_elem_min > 0) {
new_min += (damages_obj[0] / total_elem_min) * rainbow_raw;
}
new_max += (damages_obj[1] / total_elem_max) * rainbow_raw;
}
damages_obj[0] = new_min;

View file

@ -61,3 +61,134 @@ let powderSpecialStats = [
_ps("Courage",new Map([ ["Duration", [6,6.5,7,7.5,8]],["Damage", [75,87.5,100,112.5,125]],["Damage Boost", [70,90,110,130,150]] ]),"Endurance",new Map([ ["Damage", [2,3,4,5,6]],["Duration", [8,8,8,8,8]],["Description", "Hit Taken"] ]),200), //f
_ps("Wind Prison",new Map([ ["Duration", [3,3.5,4,4.5,5]],["Damage Boost", [400,450,500,550,600]],["Knockback", [8,12,16,20,24]] ]),"Dodge",new Map([ ["Damage",[2,3,4,5,6]],["Duration",[2,3,4,5,6]],["Description","Near Mobs"] ]),150) //a
];
/**
* Apply armor powders.
* Encoding shortcut assumes that all powders give +def to one element
* and -def to the element "behind" it in cycle ETWFA, which is true
* as of now and unlikely to change in the near future.
*/
function applyArmorPowders(expandedItem) {
const powders = expandedItem.get('powders');
for(const id of powders){
let powder = powderStats[id];
let name = powderNames.get(id).charAt(0);
let prevName = skp_elements[(skp_elements.indexOf(name) + 4 )% 5];
expandedItem.set(name+"Def", (expandedItem.get(name+"Def") || 0) + powder["defPlus"]);
expandedItem.set(prevName+"Def", (expandedItem.get(prevName+"Def") || 0) - powder["defMinus"]);
}
}
const damage_keys = [ "nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_" ];
const damage_present_key = 'damagePresent';
/**
* Apply weapon powders. MUTATES THE ITEM!
* Adds entries for `damage_keys` and `damage_present_key`
* For normal items, `damage_keys` is 6x2 list (elem: [min, max])
* For crafted items, `damage_keys` is 6x2x2 list (elem: [minroll: [min, max], maxroll: [min, max]])
*/
function apply_weapon_powders(item) {
let present;
if (item.get("tier") !== "Crafted") {
let weapon_result = calc_weapon_powder(item);
let damages = weapon_result[0];
present = weapon_result[1];
for (const i in damage_keys) {
item.set(damage_keys[i], damages[i]);
}
} else {
let base_low = [item.get("nDamBaseLow"),item.get("eDamBaseLow"),item.get("tDamBaseLow"),item.get("wDamBaseLow"),item.get("fDamBaseLow"),item.get("aDamBaseLow")];
let results_low = calc_weapon_powder(item, base_low);
let damage_low = results_low[0];
let base_high = [item.get("nDamBaseHigh"),item.get("eDamBaseHigh"),item.get("tDamBaseHigh"),item.get("wDamBaseHigh"),item.get("fDamBaseHigh"),item.get("aDamBaseHigh")];
let results_high = calc_weapon_powder(item, base_high);
let damage_high = results_high[0];
present = results_high[1];
for (const i in damage_keys) {
item.set(damage_keys[i], [damage_low[i], damage_high[i]]);
}
}
item.set(damage_present_key, present);
}
/**
* Calculate weapon damage from powder.
*
* Params:
* weapon: Weapon to apply powder to
* damageBases: used by crafted
*
* Return:
* [damages, damage_present]
*/
function calc_weapon_powder(weapon, damageBases) {
let powders = weapon.get("powders").slice();
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
let damages = [
weapon.get('nDam').split('-').map(Number),
weapon.get('eDam').split('-').map(Number),
weapon.get('tDam').split('-').map(Number),
weapon.get('wDam').split('-').map(Number),
weapon.get('fDam').split('-').map(Number),
weapon.get('aDam').split('-').map(Number)
];
// Applying spell conversions
let neutralBase = damages[0].slice();
let neutralRemainingRaw = damages[0].slice();
//powder application for custom crafted weapons is inherently fucked because there is no base. Unsure what to do.
//Powder application for Crafted weapons - this implementation is RIGHT YEAAAAAAAAA
//1st round - apply each as ingred, 2nd round - apply as normal
if (weapon.get("tier") === "Crafted" && !weapon.get("custom")) {
for (const p of powders.concat(weapon.get("ingredPowders"))) {
let powder = powderStats[p]; //use min, max, and convert
let element = Math.floor((p+0.01)/6); //[0,4], the +0.01 attempts to prevent division error
let diff = Math.floor(damageBases[0] * powder.convert/100);
damageBases[0] -= diff;
damageBases[element+1] += diff + Math.floor( (powder.min + powder.max) / 2 );
}
//update all damages
for (let i = 0; i < damages.length; i++) {
damages[i] = [Math.floor(damageBases[i] * 0.9), Math.floor(damageBases[i] * 1.1)];
}
neutralRemainingRaw = damages[0].slice();
neutralBase = damages[0].slice();
}
//apply powders to weapon
for (const powderID of powders) {
const powder = powderStats[powderID];
// Bitwise to force conversion to integer (integer division).
const element = (powderID/6) | 0;
let conversionRatio = powder.convert/100;
if (neutralRemainingRaw[1] > 0) {
let min_diff = Math.min(neutralRemainingRaw[0], conversionRatio * neutralBase[0]);
let max_diff = Math.min(neutralRemainingRaw[1], conversionRatio * neutralBase[1]);
//damages[element+1][0] = Math.floor(round_near(damages[element+1][0] + min_diff));
//damages[element+1][1] = Math.floor(round_near(damages[element+1][1] + max_diff));
//neutralRemainingRaw[0] = Math.floor(round_near(neutralRemainingRaw[0] - min_diff));
//neutralRemainingRaw[1] = Math.floor(round_near(neutralRemainingRaw[1] - max_diff));
damages[element+1][0] += min_diff;
damages[element+1][1] += max_diff;
neutralRemainingRaw[0] -= min_diff;
neutralRemainingRaw[1] -= max_diff;
}
damages[element+1][0] += powder.min;
damages[element+1][1] += powder.max;
}
// The ordering of these two blocks decides whether neutral is present when converted away or not.
let present_elements = []
for (const damage of damages) {
present_elements.push(damage[1] > 0);
}
// The ordering of these two blocks decides whether neutral is present when converted away or not.
damages[0] = neutralRemainingRaw;
return [damages, present_elements];
}