added TAS calculations
This commit is contained in:
parent
52d6cf3a9e
commit
c54dc3fb31
@ -131,16 +131,41 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.effectSpellHitRate = (
|
exports.effectSpellHitRate = (
|
||||||
|
attackerMagicInnate,
|
||||||
|
targetMagicInnate,
|
||||||
targetResistanceInnate,
|
targetResistanceInnate,
|
||||||
targetResistanceArmor,
|
targetResistanceArmor,
|
||||||
targetResistanceAccessory,
|
targetResistanceAccessory,
|
||||||
|
options = {},
|
||||||
) => {
|
) => {
|
||||||
if (targetResistanceInnate === null) {
|
if (targetResistanceInnate === null) {
|
||||||
// e.g. "Class 1" enemies
|
// e.g. "Class 1" enemies
|
||||||
return 0;
|
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 = (
|
exports.hitRate = (
|
||||||
@ -158,6 +183,36 @@
|
|||||||
targetSpeed += 30;
|
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));
|
const hitRate = 85 + (0.8 * (attackerSpeed - targetSpeed));
|
||||||
return Math.max(10, Math.min(98, hitRate));
|
return Math.max(10, Math.min(98, hitRate));
|
||||||
};
|
};
|
||||||
@ -177,6 +232,30 @@
|
|||||||
targetSpeed += 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));
|
const runRate = 25 + (1.6 * (attackerSpeed - targetSpeed));
|
||||||
return Math.max(10, Math.min(80, runRate));
|
return Math.max(10, Math.min(80, runRate));
|
||||||
};
|
};
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
window.Cookies.set('charStats', JSON.stringify(charStats), {
|
window.Cookies.set('charStats', JSON.stringify(charStats), {
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -374,7 +372,14 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$enemyInfoModal.find('.apply-tas-form input[type="checkbox"]').on('change', () => {
|
||||||
|
refreshModal();
|
||||||
|
});
|
||||||
|
|
||||||
const 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(',');
|
rowData.spells = Array.isArray(rowData.spells) ? rowData.spells : rowData.spells.split(',');
|
||||||
const calc = window.saga.calc;
|
const calc = window.saga.calc;
|
||||||
|
|
||||||
@ -458,6 +463,8 @@
|
|||||||
|
|
||||||
if (charStats.magic === null) {
|
if (charStats.magic === null) {
|
||||||
$enemyInfoModal.find('[class^="magic-"]').text('n/a');
|
$enemyInfoModal.find('[class^="magic-"]').text('n/a');
|
||||||
|
$enemyInfoModal.find('.debuff-rate').text('n/a');
|
||||||
|
$enemyInfoModal.find('.vacuum-rate').text('n/a');
|
||||||
} else {
|
} else {
|
||||||
const charMagic = charStats.magic;
|
const charMagic = charStats.magic;
|
||||||
const enemyMagic = rowData.magic;
|
const enemyMagic = rowData.magic;
|
||||||
@ -515,6 +522,7 @@
|
|||||||
const elementalRes = 0;
|
const elementalRes = 0;
|
||||||
const resArmor = charStats.armor ? charStats.armor.resistance[spell.element.toLowerCase()] : 0;
|
const resArmor = charStats.armor ? charStats.armor.resistance[spell.element.toLowerCase()] : 0;
|
||||||
const resAccessory = charStats.accessory ? charStats.accessory.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 def = calc.magicalAttack(enemyMagic, charMagic, elementalRes, resArmor, resAccessory, spell.power);
|
||||||
let magicUp = calc.magicalAttack(enemyMagic, charMagic, elementalRes, resArmor, resAccessory, spell.power, {
|
let magicUp = calc.magicalAttack(enemyMagic, charMagic, elementalRes, resArmor, resAccessory, spell.power, {
|
||||||
targetMagicUp: true,
|
targetMagicUp: true,
|
||||||
@ -543,16 +551,112 @@
|
|||||||
prefix = '.magic-enemy-dmg-MPCatcher';
|
prefix = '.magic-enemy-dmg-MPCatcher';
|
||||||
$enemyInfoModal.find(prefix).text(magicDmg(def));
|
$enemyInfoModal.find(prefix).text(magicDmg(def));
|
||||||
$enemyInfoModal.find(prefix + '-magic-up').text(magicDmg(magicUp));
|
$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) {
|
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, {
|
const hitRateSpeed = calc.hitRate(charStats.speed, rowData.speed, {
|
||||||
attackerSpeedUp: true,
|
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, {
|
const runRateSpeed = rowData.cls === 1 ? 0 : calc.runRate(charStats.speed, rowData.speed, {
|
||||||
attackerSpeedUp: true,
|
attackerSpeedUp: true,
|
||||||
|
...defaultCalcOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
$enemyInfoModal.find('.run-rate').text(toPer(runRate));
|
$enemyInfoModal.find('.run-rate').text(toPer(runRate));
|
||||||
@ -560,16 +664,19 @@
|
|||||||
$enemyInfoModal.find('.hit-rate').text(toPer(hitRate));
|
$enemyInfoModal.find('.hit-rate').text(toPer(hitRate));
|
||||||
$enemyInfoModal.find('.hit-rate-speed-up').text(toPer(hitRateSpeed));
|
$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, {
|
const enemyHitRateSpeed = calc.hitRate(rowData.speed, charStats.speed, {
|
||||||
attackerSpeedUp: true,
|
attackerSpeedUp: true,
|
||||||
|
...defaultCalcOptions,
|
||||||
});
|
});
|
||||||
const enemyHitRateCharSpeed = calc.hitRate(rowData.speed, charStats.speed, {
|
const enemyHitRateCharSpeed = calc.hitRate(rowData.speed, charStats.speed, {
|
||||||
targetSpeedUp: true,
|
targetSpeedUp: true,
|
||||||
|
...defaultCalcOptions,
|
||||||
});
|
});
|
||||||
const enemyHitRateCharSpeedSpeed = calc.hitRate(rowData.speed, charStats.speed, {
|
const enemyHitRateCharSpeedSpeed = calc.hitRate(rowData.speed, charStats.speed, {
|
||||||
attackerSpeedUp: true,
|
attackerSpeedUp: true,
|
||||||
targetSpeedUp: true,
|
targetSpeedUp: true,
|
||||||
|
...defaultCalcOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
$enemyInfoModal.find('.hit-rate-enemy').text(toPer(enemyHitRate));
|
$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').text(toPer(enemyHitRateCharSpeed));
|
||||||
$enemyInfoModal.find('.hit-rate-enemy-char-speed-up-speed-up').text(toPer(enemyHitRateCharSpeedSpeed));
|
$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.on('hide.bs.modal', () => {
|
||||||
$enemyInfoModal.find('.apply-runes-form').hide().find('input').prop('checked', false);
|
$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) => {
|
$table.find('tbody.data td').on('click', (e) => {
|
||||||
@ -594,7 +697,7 @@
|
|||||||
if (rowData.name === 'Gorsia') {
|
if (rowData.name === 'Gorsia') {
|
||||||
$enemyInfoModal.find('.apply-runes-form').show().prop('checked', false);
|
$enemyInfoModal.find('.apply-runes-form').show().prop('checked', false);
|
||||||
} else {
|
} else {
|
||||||
$enemyInfoModal.find('.apply-runes-form').hide().find('input').prop('checked', false);
|
$enemyInfoModal.find('.apply-runes-form').hide().find('input').prop('checked', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshModal();
|
refreshModal();
|
||||||
|
@ -79,6 +79,9 @@ block tab-content
|
|||||||
div.form-check-inline.ml-4.apply-runes-form(style="font-size: 75%")
|
div.form-check-inline.ml-4.apply-runes-form(style="font-size: 75%")
|
||||||
input.form-check-input#apply-runes(type="checkbox" autocomplete="off")
|
input.form-check-input#apply-runes(type="checkbox" autocomplete="off")
|
||||||
label.form-check-label(for="apply-runes") Apply runes
|
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
|
div
|
||||||
| #[span.gold]/#[span.exp]
|
| #[span.gold]/#[span.exp]
|
||||||
div.modal-body(style="font-size: 85%")
|
div.modal-body(style="font-size: 85%")
|
||||||
@ -146,6 +149,18 @@ block tab-content
|
|||||||
th Char. speed up
|
th Char. speed up
|
||||||
td: code.hit-rate-enemy-char-speed-up
|
td: code.hit-rate-enemy-char-speed-up
|
||||||
td: code.hit-rate-enemy-char-speed-up-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.bg-light.mt-2
|
||||||
div.card-header Enemy magic
|
div.card-header Enemy magic
|
||||||
@ -203,13 +218,13 @@ block tab-content
|
|||||||
tr
|
tr
|
||||||
th Debuff rate
|
th Debuff rate
|
||||||
td: code.debuff-rate
|
td: code.debuff-rate
|
||||||
th
|
th w/ magic up
|
||||||
td
|
td: code.debuff-rate-magic-up
|
||||||
tr
|
tr
|
||||||
th Vacuum rate
|
th Vacuum rate
|
||||||
td: code.vacuum-rate
|
td: code.vacuum-rate
|
||||||
th
|
th w/ magic up
|
||||||
td
|
td: code.vacuum-rate-magic-up
|
||||||
|
|
||||||
div.col-4: div.card.bg-light
|
div.col-4: div.card.bg-light
|
||||||
div.card-header Character magic
|
div.card-header Character magic
|
||||||
|
@ -87,7 +87,15 @@ html
|
|||||||
div.col-8: select#char-armor.form-control(autocomplete="off")
|
div.col-8: select#char-armor.form-control(autocomplete="off")
|
||||||
option(value="") Choose armor
|
option(value="") Choose armor
|
||||||
each armor in charArmor
|
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 + ')'
|
= armor.name + ' (' + armor.defense + ')'
|
||||||
div.form-row.form-group
|
div.form-row.form-group
|
||||||
label.col-form-label-sm.col-4(for="char-armor") Accessory
|
label.col-form-label-sm.col-4(for="char-armor") Accessory
|
||||||
|
Loading…
Reference in New Issue
Block a user