added TAS calculations
This commit is contained in:
parent
52d6cf3a9e
commit
c54dc3fb31
@ -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));
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user