main.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. * @licstart The following is the entire license notice for the JavaScript
  3. * code in this page.
  4. *
  5. * This file is part of gish-ap-calc.
  6. *
  7. * gish-ap-calc is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU Affero General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * gish-ap-calc is distributed in the hope that it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
  15. * for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with gish-ap-calc. If not, see <https://www.gnu.org/licenses/>.
  19. *
  20. * @licend The above is the entire license notice for the JavaScript code in
  21. * this page.
  22. */
  23. import { allocateAp } from "./lib.js";
  24. import { mDps, meleePeriod, mHitRate, psm, spellPeriod, wacc, wDps, wHitRate, } from "./mechanics.js";
  25. import { Monster, Spell, SpellType, Stats, Weapon, WeaponType, } from "./types.js";
  26. document.addEventListener("readystatechange", () => {
  27. if (document.readyState === "complete") {
  28. main();
  29. }
  30. });
  31. function main() {
  32. /**************** INPUTS ****************/
  33. // Base stats
  34. const strBaseInput = document.getElementById("str-base");
  35. const dexBaseInput = document.getElementById("dex-base");
  36. const intBaseInput = document.getElementById("int-base");
  37. const lukBaseInput = document.getElementById("luk-base");
  38. // Additional stats
  39. const strAdditionalInput = document.getElementById("str-additional");
  40. const dexAdditionalInput = document.getElementById("dex-additional");
  41. const intAdditionalInput = document.getElementById("int-additional");
  42. const lukAdditionalInput = document.getElementById("luk-additional");
  43. const waccAdditionalInput = document.getElementById("wacc-additional");
  44. const matkAdditionalInput = document.getElementById("matk-additional");
  45. // Total WATK
  46. const totalWatkInput = document.getElementById("total-watk");
  47. // AP available
  48. const apAvailableInput = document.getElementById("ap-available");
  49. // Character
  50. const levelInput = document.getElementById("level");
  51. // Weapon/spell
  52. const weaponTypeInput = document.getElementById("weapon-type");
  53. const speedInput = document.getElementById("speed");
  54. const spellInput = document.getElementById("spell");
  55. const spellBasicAtkInput = document.getElementById("spell-basic-atk");
  56. const spellLinesInput = document.getElementById("spell-lines");
  57. const masteryInput = document.getElementById("mastery");
  58. const spellBoosterInput = document.getElementById("spell-booster");
  59. // Elemental stuff
  60. const eleAmpInput = document.getElementById("ele-amp");
  61. const eleWepInput = document.getElementById("ele-wep");
  62. // Enemy
  63. const wdefInput = document.getElementById("enemy-wdef");
  64. const mdefInput = document.getElementById("enemy-mdef");
  65. const avoidInput = document.getElementById("enemy-avoid");
  66. const eleSusInput = document.getElementById("ele-sus");
  67. const enemyLevelInput = document.getElementById("enemy-level");
  68. const enemyCountInput = document.getElementById("enemy-count");
  69. // wDominanceFactor
  70. const wDominanceFactorInput = document.getElementById("w-dominance-factor");
  71. /**************** OUTPUTS ****************/
  72. // Stats
  73. const strOutput = document.getElementById("str-output");
  74. const strBaseOutput = document.getElementById("str-base-output");
  75. const strAdditionalOutput = document.getElementById("str-additional-output");
  76. const dexOutput = document.getElementById("dex-output");
  77. const dexBaseOutput = document.getElementById("dex-base-output");
  78. const dexAdditionalOutput = document.getElementById("dex-additional-output");
  79. const intOutput = document.getElementById("int-output");
  80. const intBaseOutput = document.getElementById("int-base-output");
  81. const intAdditionalOutput = document.getElementById("int-additional-output");
  82. const lukOutput = document.getElementById("luk-output");
  83. const lukBaseOutput = document.getElementById("luk-base-output");
  84. const lukAdditionalOutput = document.getElementById("luk-additional-output");
  85. // Combat
  86. const meleeDpsOutput = document.getElementById("melee-dps");
  87. const spellDpsOutput = document.getElementById("spell-dps");
  88. const meleeHitRateOutput = document.getElementById("melee-hit-rate");
  89. const spellHitRateOutput = document.getElementById("spell-hit-rate");
  90. function recalculate() {
  91. const initialBaseStats = new Stats(handleIntInput(strBaseInput, 4, 4), handleIntInput(dexBaseInput, 4, 4), handleIntInput(intBaseInput, 4, 4), handleIntInput(lukBaseInput, 4, 4));
  92. const level = handleIntInput(levelInput, 8, 30, 200);
  93. const wepTypeInt = handleIntInput(weaponTypeInput, 30, 30, 44);
  94. const wepType = (() => {
  95. if (!(wepTypeInt in WeaponType)) {
  96. weaponTypeInput.value = "30";
  97. return WeaponType.OneHandedSword;
  98. }
  99. return wepTypeInt;
  100. })();
  101. const wepSpeed = handleIntInput(speedInput, 6, 2, 9);
  102. const weapon = new Weapon(psm(wepType), meleePeriod(wepType, wepSpeed));
  103. const spellTypeInt = handleIntInput(spellInput, 0, 0, 2321008);
  104. const spellType = (() => {
  105. if (!(spellTypeInt in SpellType)) {
  106. spellInput.value = "0";
  107. return SpellType.Other;
  108. }
  109. return spellTypeInt;
  110. })();
  111. const spellT = (() => {
  112. let t = spellPeriod(handleIntInput(spellBoosterInput, 0, -2, 0), spellType, wepSpeed);
  113. if (t === undefined) {
  114. console.error(`spellPeriod(${handleIntInput(spellBoosterInput, 0, -2, 0)}, ${spellType}, ${wepSpeed}) is undefined`);
  115. t = 0.81;
  116. }
  117. return t;
  118. })();
  119. const spell = new Spell(handleIntInput(spellBasicAtkInput, 10, 1), handleIntInput(masteryInput, 15, 10, 90) / 100, spellT, handleIntInput(spellLinesInput, 1, 1));
  120. const totalWatk = handleIntInput(totalWatkInput, 1, 0);
  121. const rawMatk = handleIntInput(matkAdditionalInput, 0, 0);
  122. const rawWacc = handleIntInput(waccAdditionalInput, 0, 0);
  123. const monster = new Monster(handleIntInput(enemyLevelInput, 1, 1), handleIntInput(avoidInput, 1, 1), handleIntInput(wdefInput, 0), handleIntInput(mdefInput, 0));
  124. const [baseStats, totalStats] = allocateAp(handleIntInput(apAvailableInput, 1, 1), initialBaseStats, new Stats(handleIntInput(strAdditionalInput, 0, 0), handleIntInput(dexAdditionalInput, 0, 0), handleIntInput(intAdditionalInput, 0, 0), handleIntInput(lukAdditionalInput, 0, 0)).add(initialBaseStats), level, weapon, spell, totalWatk, rawMatk, rawWacc, monster, handleFloatInput(wDominanceFactorInput, 2, 1));
  125. const additionalStats = totalStats.clone().sub(baseStats);
  126. strOutput.textContent = "" + totalStats.str;
  127. dexOutput.textContent = "" + totalStats.dex;
  128. intOutput.textContent = "" + totalStats.int;
  129. lukOutput.textContent = "" + totalStats.luk;
  130. strBaseOutput.textContent = "" + baseStats.str;
  131. dexBaseOutput.textContent = "" + baseStats.dex;
  132. intBaseOutput.textContent = "" + baseStats.int;
  133. lukBaseOutput.textContent = "" + baseStats.luk;
  134. strAdditionalOutput.textContent = "" + additionalStats.str;
  135. dexAdditionalOutput.textContent = "" + additionalStats.dex;
  136. intAdditionalOutput.textContent = "" + additionalStats.int;
  137. lukAdditionalOutput.textContent = "" + additionalStats.luk;
  138. const d = Math.max(monster.level - level, 0);
  139. meleeDpsOutput.textContent = wDps(totalStats, rawWacc, totalWatk, weapon, monster, d).toFixed(3);
  140. spellDpsOutput.textContent = mDps(totalStats, rawMatk, spell, monster, d).toFixed(3);
  141. meleeHitRateOutput.textContent = (100 *
  142. wHitRate(wacc(totalStats.dex, totalStats.luk, rawWacc), monster.avoid, d)).toFixed(2);
  143. spellHitRateOutput.textContent = (100 * mHitRate(totalStats.int, totalStats.luk, monster.avoid, d)).toFixed(2);
  144. }
  145. for (const input of [
  146. strBaseInput,
  147. dexBaseInput,
  148. intBaseInput,
  149. lukBaseInput,
  150. strAdditionalInput,
  151. dexAdditionalInput,
  152. intAdditionalInput,
  153. lukAdditionalInput,
  154. waccAdditionalInput,
  155. matkAdditionalInput,
  156. totalWatkInput,
  157. apAvailableInput,
  158. levelInput,
  159. weaponTypeInput,
  160. speedInput,
  161. spellInput,
  162. spellBasicAtkInput,
  163. spellLinesInput,
  164. masteryInput,
  165. spellBoosterInput,
  166. eleAmpInput,
  167. eleWepInput,
  168. wdefInput,
  169. mdefInput,
  170. avoidInput,
  171. eleSusInput,
  172. enemyLevelInput,
  173. enemyCountInput,
  174. wDominanceFactorInput,
  175. ]) {
  176. input.addEventListener("change", recalculate);
  177. }
  178. recalculate();
  179. }
  180. function handleIntInput(input, def, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
  181. let x = Math.min(Math.max(parseInt(input.value, 10), min), max);
  182. if (!Number.isFinite(x)) {
  183. x = def;
  184. }
  185. input.value = "" + x;
  186. return x;
  187. }
  188. function handleFloatInput(input, def, min = Number.NEGATIVE_INFINITY, max = Number.POSITIVE_INFINITY) {
  189. let x = Math.min(Math.max(parseFloat(input.value), min), max);
  190. if (!Number.isFinite(x)) {
  191. x = def;
  192. }
  193. input.value = "" + x;
  194. return x;
  195. }