123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- /*
- * @licstart The following is the entire license notice for the JavaScript
- * code in this page.
- *
- * This file is part of gish-ap-calc.
- *
- * gish-ap-calc is free software: you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * gish-ap-calc is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with gish-ap-calc. If not, see <https://www.gnu.org/licenses/>.
- *
- * @licend The above is the entire license notice for the JavaScript code in
- * this page.
- */
- import { allocateAp } from "./lib.js";
- import {
- mDps,
- meleePeriod,
- mHitRate,
- psm,
- spellPeriod,
- wacc,
- wDps,
- wHitRate,
- } from "./mechanics.js";
- import {
- Monster,
- Speed,
- Spell,
- SpellType,
- Stats,
- Weapon,
- WeaponType,
- } from "./types.js";
- document.addEventListener("readystatechange", () => {
- if (document.readyState === "complete") {
- main();
- }
- });
- function main(): void {
- /**************** INPUTS ****************/
- // Base stats
- const strBaseInput = document.getElementById(
- "str-base",
- ) as HTMLInputElement;
- const dexBaseInput = document.getElementById(
- "dex-base",
- ) as HTMLInputElement;
- const intBaseInput = document.getElementById(
- "int-base",
- ) as HTMLInputElement;
- const lukBaseInput = document.getElementById(
- "luk-base",
- ) as HTMLInputElement;
- // Additional stats
- const strAdditionalInput = document.getElementById(
- "str-additional",
- ) as HTMLInputElement;
- const dexAdditionalInput = document.getElementById(
- "dex-additional",
- ) as HTMLInputElement;
- const intAdditionalInput = document.getElementById(
- "int-additional",
- ) as HTMLInputElement;
- const lukAdditionalInput = document.getElementById(
- "luk-additional",
- ) as HTMLInputElement;
- const waccAdditionalInput = document.getElementById(
- "wacc-additional",
- ) as HTMLInputElement;
- const matkAdditionalInput = document.getElementById(
- "matk-additional",
- ) as HTMLInputElement;
- // Total WATK
- const totalWatkInput = document.getElementById(
- "total-watk",
- ) as HTMLInputElement;
- // AP available
- const apAvailableInput = document.getElementById(
- "ap-available",
- ) as HTMLInputElement;
- // Character
- const levelInput = document.getElementById("level") as HTMLInputElement;
- // Weapon/spell
- const weaponTypeInput = document.getElementById(
- "weapon-type",
- ) as HTMLSelectElement;
- const speedInput = document.getElementById("speed") as HTMLSelectElement;
- const spellInput = document.getElementById("spell") as HTMLSelectElement;
- const spellBasicAtkInput = document.getElementById(
- "spell-basic-atk",
- ) as HTMLInputElement;
- const spellLinesInput = document.getElementById(
- "spell-lines",
- ) as HTMLSelectElement;
- const masteryInput = document.getElementById(
- "mastery",
- ) as HTMLInputElement;
- const spellBoosterInput = document.getElementById(
- "spell-booster",
- ) as HTMLInputElement;
- // Elemental stuff
- const eleAmpInput = document.getElementById("ele-amp") as HTMLInputElement;
- const eleWepInput = document.getElementById("ele-wep") as HTMLInputElement;
- // Enemy
- const wdefInput = document.getElementById(
- "enemy-wdef",
- ) as HTMLInputElement;
- const mdefInput = document.getElementById(
- "enemy-mdef",
- ) as HTMLInputElement;
- const avoidInput = document.getElementById(
- "enemy-avoid",
- ) as HTMLInputElement;
- const eleSusInput = document.getElementById(
- "ele-sus",
- ) as HTMLSelectElement;
- const enemyLevelInput = document.getElementById(
- "enemy-level",
- ) as HTMLInputElement;
- const enemyCountInput = document.getElementById(
- "enemy-count",
- ) as HTMLInputElement;
- // wDominanceFactor
- const wDominanceFactorInput = document.getElementById(
- "w-dominance-factor",
- ) as HTMLInputElement;
- /**************** OUTPUTS ****************/
- // Stats
- const strOutput = document.getElementById("str-output") as HTMLSpanElement;
- const strBaseOutput = document.getElementById(
- "str-base-output",
- ) as HTMLSpanElement;
- const strAdditionalOutput = document.getElementById(
- "str-additional-output",
- ) as HTMLSpanElement;
- const dexOutput = document.getElementById("dex-output") as HTMLSpanElement;
- const dexBaseOutput = document.getElementById(
- "dex-base-output",
- ) as HTMLSpanElement;
- const dexAdditionalOutput = document.getElementById(
- "dex-additional-output",
- ) as HTMLSpanElement;
- const intOutput = document.getElementById("int-output") as HTMLSpanElement;
- const intBaseOutput = document.getElementById(
- "int-base-output",
- ) as HTMLSpanElement;
- const intAdditionalOutput = document.getElementById(
- "int-additional-output",
- ) as HTMLSpanElement;
- const lukOutput = document.getElementById("luk-output") as HTMLSpanElement;
- const lukBaseOutput = document.getElementById(
- "luk-base-output",
- ) as HTMLSpanElement;
- const lukAdditionalOutput = document.getElementById(
- "luk-additional-output",
- ) as HTMLSpanElement;
- // Combat
- const meleeDpsOutput = document.getElementById(
- "melee-dps",
- ) as HTMLSpanElement;
- const spellDpsOutput = document.getElementById(
- "spell-dps",
- ) as HTMLSpanElement;
- const meleeHitRateOutput = document.getElementById(
- "melee-hit-rate",
- ) as HTMLSpanElement;
- const spellHitRateOutput = document.getElementById(
- "spell-hit-rate",
- ) as HTMLSpanElement;
- function recalculate(): void {
- const initialBaseStats = new Stats(
- handleIntInput(strBaseInput, 4, 4),
- handleIntInput(dexBaseInput, 4, 4),
- handleIntInput(intBaseInput, 4, 4),
- handleIntInput(lukBaseInput, 4, 4),
- );
- const level = handleIntInput(levelInput, 8, 30, 200);
- const wepTypeInt = handleIntInput(weaponTypeInput, 30, 30, 44);
- const wepType = (() => {
- if (!(wepTypeInt in WeaponType)) {
- weaponTypeInput.value = "30";
- return WeaponType.OneHandedSword;
- }
- return wepTypeInt as WeaponType;
- })();
- const wepSpeed = handleIntInput(speedInput, 6, 2, 9) as Speed;
- const weapon = new Weapon(
- psm(wepType),
- meleePeriod(wepType, wepSpeed),
- );
- const spellTypeInt = handleIntInput(spellInput, 0, 0, 2321008);
- const spellType = (() => {
- if (!(spellTypeInt in SpellType)) {
- spellInput.value = "0";
- return SpellType.Other;
- }
- return spellTypeInt as SpellType;
- })();
- const spellT = (() => {
- let t = spellPeriod(
- handleIntInput(spellBoosterInput, 0, -2, 0),
- spellType,
- wepSpeed,
- );
- if (t === undefined) {
- console.error(
- `spellPeriod(${handleIntInput(
- spellBoosterInput,
- 0,
- -2,
- 0,
- )}, ${spellType}, ${wepSpeed}) is undefined`,
- );
- t = 0.81;
- }
- return t;
- })();
- const spell = new Spell(
- handleIntInput(spellBasicAtkInput, 10, 1),
- handleIntInput(masteryInput, 15, 10, 90) / 100,
- spellT,
- handleIntInput(spellLinesInput, 1, 1),
- );
- const totalWatk = handleIntInput(totalWatkInput, 1, 0);
- const rawMatk = handleIntInput(matkAdditionalInput, 0, 0);
- const rawWacc = handleIntInput(waccAdditionalInput, 0, 0);
- const monster = new Monster(
- handleIntInput(enemyLevelInput, 1, 1),
- handleIntInput(avoidInput, 1, 1),
- handleIntInput(wdefInput, 0),
- handleIntInput(mdefInput, 0),
- );
- 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),
- );
- const additionalStats = totalStats.clone().sub(baseStats);
- strOutput.textContent = "" + totalStats.str;
- dexOutput.textContent = "" + totalStats.dex;
- intOutput.textContent = "" + totalStats.int;
- lukOutput.textContent = "" + totalStats.luk;
- strBaseOutput.textContent = "" + baseStats.str;
- dexBaseOutput.textContent = "" + baseStats.dex;
- intBaseOutput.textContent = "" + baseStats.int;
- lukBaseOutput.textContent = "" + baseStats.luk;
- strAdditionalOutput.textContent = "" + additionalStats.str;
- dexAdditionalOutput.textContent = "" + additionalStats.dex;
- intAdditionalOutput.textContent = "" + additionalStats.int;
- lukAdditionalOutput.textContent = "" + additionalStats.luk;
- const d = Math.max(monster.level - level, 0);
- meleeDpsOutput.textContent = wDps(
- totalStats,
- rawWacc,
- totalWatk,
- weapon,
- monster,
- d,
- ).toFixed(3);
- spellDpsOutput.textContent = mDps(
- totalStats,
- rawMatk,
- spell,
- monster,
- d,
- ).toFixed(3);
- meleeHitRateOutput.textContent = (
- 100 *
- wHitRate(
- wacc(totalStats.dex, totalStats.luk, rawWacc),
- monster.avoid,
- d,
- )
- ).toFixed(2);
- spellHitRateOutput.textContent = (
- 100 * mHitRate(totalStats.int, totalStats.luk, monster.avoid, d)
- ).toFixed(2);
- }
- for (const input of [
- strBaseInput,
- dexBaseInput,
- intBaseInput,
- lukBaseInput,
- strAdditionalInput,
- dexAdditionalInput,
- intAdditionalInput,
- lukAdditionalInput,
- waccAdditionalInput,
- matkAdditionalInput,
- totalWatkInput,
- apAvailableInput,
- levelInput,
- weaponTypeInput,
- speedInput,
- spellInput,
- spellBasicAtkInput,
- spellLinesInput,
- masteryInput,
- spellBoosterInput,
- eleAmpInput,
- eleWepInput,
- wdefInput,
- mdefInput,
- avoidInput,
- eleSusInput,
- enemyLevelInput,
- enemyCountInput,
- wDominanceFactorInput,
- ]) {
- input.addEventListener("change", recalculate);
- }
- recalculate();
- }
- function handleIntInput(
- input: HTMLInputElement | HTMLSelectElement,
- def: number,
- min: number = Number.NEGATIVE_INFINITY,
- max: number = Number.POSITIVE_INFINITY,
- ): number {
- let x = Math.min(Math.max(parseInt(input.value, 10), min), max);
- if (!Number.isFinite(x)) {
- x = def;
- }
- input.value = "" + x;
- return x;
- }
- function handleFloatInput(
- input: HTMLInputElement | HTMLSelectElement,
- def: number,
- min: number = Number.NEGATIVE_INFINITY,
- max: number = Number.POSITIVE_INFINITY,
- ): number {
- let x = Math.min(Math.max(parseFloat(input.value), min), max);
- if (!Number.isFinite(x)) {
- x = def;
- }
- input.value = "" + x;
- return x;
- }
|