From c54dc3fb315a565a83c10e49cf816872e1f83c66 Mon Sep 17 00:00:00 2001 From: tmont Date: Tue, 16 Mar 2021 13:16:28 -0700 Subject: [PATCH] added TAS calculations --- web/static/calc.js | 81 ++++++++++++++++++++++++++- web/static/saga.js | 125 ++++++++++++++++++++++++++++++++++++++---- web/views/enemies.pug | 23 ++++++-- web/views/master.pug | 10 +++- 4 files changed, 222 insertions(+), 17 deletions(-) diff --git a/web/static/calc.js b/web/static/calc.js index e0ae798..eb3ae01 100644 --- a/web/static/calc.js +++ b/web/static/calc.js @@ -131,16 +131,41 @@ }; exports.effectSpellHitRate = ( + attackerMagicInnate, + targetMagicInnate, targetResistanceInnate, targetResistanceArmor, targetResistanceAccessory, + options = {}, ) => { if (targetResistanceInnate === null) { // e.g. "Class 1" enemies return 0; } - return Math.max(5, 100 - targetResistanceInnate - targetResistanceArmor - targetResistanceAccessory); + const targetResistance = targetResistanceInnate + targetResistanceArmor + targetResistanceAccessory; + let attackerMagic = attackerMagicInnate; + let targetMagic = targetMagicInnate; + if (options.attackerMagicUp) { + attackerMagic += 40; + } + if (options.targetMagicUp) { + targetMagic += 40; + } + + if (options.tas) { + const magicCalc = 256 * (attackerMagic - targetMagic) / attackerMagic; + let magicResult; + if (magicCalc > targetResistance) { + magicResult = 0; + } else { + magicResult = magicCalc; + } + + return Math.max(0, 100 - (targetResistance - magicResult)); + } + + return Math.max(5, 100 - targetResistance); }; exports.hitRate = ( @@ -158,6 +183,36 @@ targetSpeed += 30; } + if (options.tas) { + const diff = attackerSpeed - targetSpeed; + let hitRate; + if (diff <= -89) { + hitRate = 26; + } else if (diff <= -59) { + hitRate = 77; + } else if (diff <= -29) { + hitRate = 128; + } else if (diff <= -19) { + hitRate = 154; + } else if (diff <= -9) { + hitRate = 181; + } else if (diff <= -4) { + hitRate = 203; + } else if (diff <= 2) { + hitRate = 213; + } else if (diff <= 6) { + hitRate = 218; + } else if (diff <= 11) { + hitRate = 231; + } else if (diff <= 21) { + hitRate = 244; + } else { + hitRate = 251; + } + + return (hitRate / 256) * 100; + } + const hitRate = 85 + (0.8 * (attackerSpeed - targetSpeed)); return Math.max(10, Math.min(98, hitRate)); }; @@ -177,6 +232,30 @@ targetSpeed += 30; } + if (options.tas) { + const diff = attackerSpeed - targetSpeed; + if (diff <= -42) { + return 2; + } + if (diff <= -12) { + return 5; + } + if (diff <= -7) { + return 10; + } + if (diff <= 3) { + return 30; + } + if (diff <= 18) { + return 40; + } + if (diff <= 48) { + return 60; + } + + return 80; + } + const runRate = 25 + (1.6 * (attackerSpeed - targetSpeed)); return Math.max(10, Math.min(80, runRate)); }; diff --git a/web/static/saga.js b/web/static/saga.js index 6f6e46b..05f79f2 100644 --- a/web/static/saga.js +++ b/web/static/saga.js @@ -18,8 +18,6 @@ window.Cookies.set('charStats', JSON.stringify(charStats), { sameSite: 'strict', }); - } else { - } }; @@ -374,7 +372,14 @@ } }); + $enemyInfoModal.find('.apply-tas-form input[type="checkbox"]').on('change', () => { + refreshModal(); + }); + const refreshModal = () => { + const defaultCalcOptions = { + tas: $enemyInfoModal.find('.apply-tas-form input[type="checkbox"]').get(0).checked, + }; rowData.spells = Array.isArray(rowData.spells) ? rowData.spells : rowData.spells.split(','); const calc = window.saga.calc; @@ -458,6 +463,8 @@ if (charStats.magic === null) { $enemyInfoModal.find('[class^="magic-"]').text('n/a'); + $enemyInfoModal.find('.debuff-rate').text('n/a'); + $enemyInfoModal.find('.vacuum-rate').text('n/a'); } else { const charMagic = charStats.magic; const enemyMagic = rowData.magic; @@ -515,6 +522,7 @@ const elementalRes = 0; const resArmor = charStats.armor ? charStats.armor.resistance[spell.element.toLowerCase()] : 0; const resAccessory = charStats.accessory ? charStats.accessory.resistance[spell.element.toLowerCase()] : 0; + let def = calc.magicalAttack(enemyMagic, charMagic, elementalRes, resArmor, resAccessory, spell.power); let magicUp = calc.magicalAttack(enemyMagic, charMagic, elementalRes, resArmor, resAccessory, spell.power, { targetMagicUp: true, @@ -543,16 +551,112 @@ prefix = '.magic-enemy-dmg-MPCatcher'; $enemyInfoModal.find(prefix).text(magicDmg(def)); $enemyInfoModal.find(prefix + '-magic-up').text(magicDmg(magicUp)); + + // character effect spell hit rate + const enemyDebuffRes = rowData.resDebuff === 100 ? null : rowData.resDebuff; + const enemyVacuumRes = rowData.resVacuum === 100 ? null : rowData.resVacuum; + const debuffRate = calc.effectSpellHitRate( + charMagic, + enemyMagic, + enemyDebuffRes, + 0, + 0, + {...defaultCalcOptions}, + ); + const debuffRateMagicUp = calc.effectSpellHitRate( + charMagic, + enemyMagic, + enemyDebuffRes, + 0, + 0, + { + ...defaultCalcOptions, + attackerMagicUp: true, + }, + ); + const vacuumRate = calc.effectSpellHitRate( + charMagic, + enemyMagic, + enemyVacuumRes, + 0, + 0, + {...defaultCalcOptions}, + ); + const vacuumRateMagicUp = calc.effectSpellHitRate( + charMagic, + enemyMagic, + enemyVacuumRes, + 0, + 0, + { + ...defaultCalcOptions, + attackerMagicUp: true, + }, + ); + $enemyInfoModal.find('.debuff-rate').text(toPer(debuffRate)); + $enemyInfoModal.find('.debuff-rate-magic-up').text(toPer(debuffRateMagicUp)); + $enemyInfoModal.find('.vacuum-rate').text(toPer(vacuumRate)); + $enemyInfoModal.find('.vacuum-rate-magic-up').text(toPer(vacuumRateMagicUp)); + + // enemy effect spell hit rate + const resDebuffArmor = charStats.armor ? charStats.armor.resistance.debuff : 0; + const resDebuffAccessory = charStats.accessory ? charStats.accessory.resistance.debuff : 0; + const resVacuumArmor = charStats.armor ? charStats.armor.resistance.vacuum : 0; + const resVacuumAccessory = charStats.accessory ? charStats.accessory.resistance.vacuum : 0; + const enemyDebuffRate = calc.effectSpellHitRate( + enemyMagic, + charMagic, + 0, + resDebuffArmor, + resDebuffAccessory, + {...defaultCalcOptions}, + ); + const enemyDebuffRateMagicUp = calc.effectSpellHitRate( + enemyMagic, + charMagic, + 0, + resDebuffArmor, + resDebuffAccessory, + { + ...defaultCalcOptions, + targetMagicUp: true, + }, + ); + const enemyVacuumRate = calc.effectSpellHitRate( + enemyMagic, + charMagic, + 0, + resVacuumArmor, + resVacuumAccessory, + {...defaultCalcOptions}, + ); + const enemyVacuumRateMagicUp = calc.effectSpellHitRate( + enemyMagic, + charMagic, + 0, + resVacuumArmor, + resVacuumAccessory, + { + ...defaultCalcOptions, + targetMagicUp: true, + }, + ); + $enemyInfoModal.find('.hit-rate-enemy-debuff').text(toPer(enemyDebuffRate)); + $enemyInfoModal.find('.hit-rate-enemy-debuff-magic-up').text(toPer(enemyDebuffRateMagicUp)); + $enemyInfoModal.find('.hit-rate-enemy-vacuum').text(toPer(enemyVacuumRate)); + $enemyInfoModal.find('.hit-rate-enemy-vacuum-magic-up').text(toPer(enemyVacuumRateMagicUp)); } if (charStats.speed !== null) { - const hitRate = calc.hitRate(charStats.speed, rowData.speed); + const hitRate = calc.hitRate(charStats.speed, rowData.speed, { ...defaultCalcOptions }); const hitRateSpeed = calc.hitRate(charStats.speed, rowData.speed, { attackerSpeedUp: true, + ...defaultCalcOptions, }); - const runRate = rowData.cls === 1 ? 0 : calc.runRate(charStats.speed, rowData.speed); + const runRate = rowData.cls === 1 ? 0 : calc.runRate(charStats.speed, rowData.speed, { ...defaultCalcOptions }); const runRateSpeed = rowData.cls === 1 ? 0 : calc.runRate(charStats.speed, rowData.speed, { attackerSpeedUp: true, + ...defaultCalcOptions, }); $enemyInfoModal.find('.run-rate').text(toPer(runRate)); @@ -560,16 +664,19 @@ $enemyInfoModal.find('.hit-rate').text(toPer(hitRate)); $enemyInfoModal.find('.hit-rate-speed-up').text(toPer(hitRateSpeed)); - const enemyHitRate = calc.hitRate(rowData.speed, charStats.speed); + const enemyHitRate = calc.hitRate(rowData.speed, charStats.speed, { ...defaultCalcOptions }); const enemyHitRateSpeed = calc.hitRate(rowData.speed, charStats.speed, { attackerSpeedUp: true, + ...defaultCalcOptions, }); const enemyHitRateCharSpeed = calc.hitRate(rowData.speed, charStats.speed, { targetSpeedUp: true, + ...defaultCalcOptions, }); const enemyHitRateCharSpeedSpeed = calc.hitRate(rowData.speed, charStats.speed, { attackerSpeedUp: true, targetSpeedUp: true, + ...defaultCalcOptions, }); $enemyInfoModal.find('.hit-rate-enemy').text(toPer(enemyHitRate)); @@ -577,15 +684,11 @@ $enemyInfoModal.find('.hit-rate-enemy-char-speed-up').text(toPer(enemyHitRateCharSpeed)); $enemyInfoModal.find('.hit-rate-enemy-char-speed-up-speed-up').text(toPer(enemyHitRateCharSpeedSpeed)); } - - const debuffRate = calc.effectSpellHitRate(rowData.resDebuff === 100 ? null : rowData.resDebuff, 0, 0); - const vacuumRate = calc.effectSpellHitRate(rowData.resVacuum === 100 ? null : rowData.resVacuum, 0, 0); - $enemyInfoModal.find('.debuff-rate').text(toPer(debuffRate)); - $enemyInfoModal.find('.vacuum-rate').text(toPer(vacuumRate)); }; $enemyInfoModal.on('hide.bs.modal', () => { $enemyInfoModal.find('.apply-runes-form').hide().find('input').prop('checked', false); + $enemyInfoModal.find('.apply-tas-form input').prop('checked', true); }); $table.find('tbody.data td').on('click', (e) => { @@ -594,7 +697,7 @@ if (rowData.name === 'Gorsia') { $enemyInfoModal.find('.apply-runes-form').show().prop('checked', false); } else { - $enemyInfoModal.find('.apply-runes-form').hide().find('input').prop('checked', false); + $enemyInfoModal.find('.apply-runes-form').hide().find('input').prop('checked', true); } refreshModal(); diff --git a/web/views/enemies.pug b/web/views/enemies.pug index 820e643..dfffc64 100644 --- a/web/views/enemies.pug +++ b/web/views/enemies.pug @@ -79,6 +79,9 @@ block tab-content div.form-check-inline.ml-4.apply-runes-form(style="font-size: 75%") input.form-check-input#apply-runes(type="checkbox" autocomplete="off") label.form-check-label(for="apply-runes") Apply runes + div.form-check-inline.ml-2.apply-tas-form(style="font-size: 75%") + input.form-check-input#apply-tas(type="checkbox" autocomplete="off" checked) + label.form-check-label(for="apply-tas") TAS calculations div | #[span.gold]/#[span.exp] div.modal-body(style="font-size: 85%") @@ -146,6 +149,18 @@ block tab-content th Char. speed up td: code.hit-rate-enemy-char-speed-up td: code.hit-rate-enemy-char-speed-up-speed-up + tr + th + th + th Magic up + tr + th Debuff + td: code.hit-rate-enemy-debuff + td: code.hit-rate-enemy-debuff-magic-up + tr + th Vacuum + td: code.hit-rate-enemy-vacuum + td: code.hit-rate-enemy-vacuum-magic-up div.card.bg-light.mt-2 div.card-header Enemy magic @@ -203,13 +218,13 @@ block tab-content tr th Debuff rate td: code.debuff-rate - th - td + th w/ magic up + td: code.debuff-rate-magic-up tr th Vacuum rate td: code.vacuum-rate - th - td + th w/ magic up + td: code.vacuum-rate-magic-up div.col-4: div.card.bg-light div.card-header Character magic diff --git a/web/views/master.pug b/web/views/master.pug index ea7ab69..9ef4f3f 100644 --- a/web/views/master.pug +++ b/web/views/master.pug @@ -87,7 +87,15 @@ html div.col-8: select#char-armor.form-control(autocomplete="off") option(value="") Choose armor each armor in charArmor - option(value=armor.name data-defense=armor.defense) + option( + value=armor.name + data-defense=armor.defense + data-res-fire=armor.resistance.fire + data-res-ice=armor.resistance.ice + data-res-thunder=armor.resistance.thunder + data-res-vacuum=armor.resistance.vacuum + data-res-debuff=armor.resistance.debuff + ) = armor.name + ' (' + armor.defense + ')' div.form-row.form-group label.col-form-label-sm.col-4(for="char-armor") Accessory